Initial revision
stanleyg [Tue, 16 Sep 1997 15:36:24 +0000 (15:36 +0000)]
137 files changed:
FastCGI.mak.in [new file with mode: 0644]
FastCGI.mb [new file with mode: 0755]
LICENSE.TERMS [new file with mode: 0755]
Makefile.in [new file with mode: 0755]
README [new file with mode: 0755]
README_NT.txt [new file with mode: 0644]
acconfig.h [new file with mode: 0755]
build.bat [new file with mode: 0755]
build_no_shell.bat [new file with mode: 0755]
cgi-fcgi/Makefile.in [new file with mode: 0644]
cgi-fcgi/cgi-fcgi.c [new file with mode: 0644]
cgi-fcgi/cgi-fcgi.mak [new file with mode: 0644]
cgi-fcgi/cgi-fcgi.mak.in [new file with mode: 0644]
cgi-fcgi/cgi-fcgi.mdp [new file with mode: 0644]
cgi-fcgi/descrip.dfc [new file with mode: 0644]
configure [new file with mode: 0755]
configure.in [new file with mode: 0755]
dependencies.in [new file with mode: 0755]
descrip.dfc [new file with mode: 0644]
doc/FCGI_Accept.3 [new file with mode: 0644]
doc/FCGI_Finish.3 [new file with mode: 0644]
doc/FCGI_SetExitStatus.3 [new file with mode: 0644]
doc/FCGI_StartFilterData.3 [new file with mode: 0644]
doc/Makefile [new file with mode: 0644]
doc/cgi-fcgi.1 [new file with mode: 0644]
doc/fastcgi-prog-guide/ap_guida.htm [new file with mode: 0755]
doc/fastcgi-prog-guide/ap_guide.htm [new file with mode: 0755]
doc/fastcgi-prog-guide/apaman.htm [new file with mode: 0755]
doc/fastcgi-prog-guide/ch1inta1.gif [new file with mode: 0755]
doc/fastcgi-prog-guide/ch1intra.gif [new file with mode: 0755]
doc/fastcgi-prog-guide/ch1intro.htm [new file with mode: 0755]
doc/fastcgi-prog-guide/ch2c.htm [new file with mode: 0755]
doc/fastcgi-prog-guide/ch3perl.htm [new file with mode: 0755]
doc/fastcgi-prog-guide/ch4tcl.htm [new file with mode: 0755]
doc/fastcgi-prog-guide/cover.htm [new file with mode: 0755]
doc/fastcgi-prog-guide/covera.gif [new file with mode: 0755]
doc/fastcgi-whitepaper/Makefile [new file with mode: 0644]
doc/fastcgi-whitepaper/fastcgi.htm [new file with mode: 0644]
doc/fastcgi-whitepaper/img00001.gif [new file with mode: 0644]
doc/fastcgi-whitepaper/img00002.gif [new file with mode: 0644]
doc/fastcgi-whitepaper/img00003.gif [new file with mode: 0644]
doc/fcgi-devel-kit.gut [new file with mode: 0644]
doc/fcgi-devel-kit.htm [new file with mode: 0644]
doc/fcgi-java.gut [new file with mode: 0644]
doc/fcgi-java.htm [new file with mode: 0644]
doc/fcgi-perf.gut [new file with mode: 0644]
doc/fcgi-perf.htm [new file with mode: 0644]
doc/fcgi-perl.gut [new file with mode: 0644]
doc/fcgi-perl.htm [new file with mode: 0644]
doc/fcgi-spec.html [new file with mode: 0644]
doc/fcgi-tcl.gut [new file with mode: 0644]
doc/fcgi-tcl.htm [new file with mode: 0644]
doc/omi-logo.gif [new file with mode: 0644]
doc/www5-api-workshop.html [new file with mode: 0644]
examples/Makefile.in [new file with mode: 0644]
examples/SampleStore/Images/cart-hd.gif [new file with mode: 0644]
examples/SampleStore/Images/main-hd.gif [new file with mode: 0644]
examples/SampleStore/Images/offer-hd.gif [new file with mode: 0644]
examples/SampleStore/Images/purch-hd.gif [new file with mode: 0644]
examples/SampleStore/Images/thank-hd.gif [new file with mode: 0644]
examples/SampleStore/Protected/RMSTitanic.html [new file with mode: 0644]
examples/SampleStore/Unprotected/Purchase.html [new file with mode: 0644]
examples/SampleStore/Unprotected/ThankYou.html [new file with mode: 0644]
examples/conf/om-httpd.config [new file with mode: 0644]
examples/echo-perl [new file with mode: 0755]
examples/echo-tcl [new file with mode: 0755]
examples/echo.c [new file with mode: 0644]
examples/echo.cgi [new file with mode: 0755]
examples/echo.html [new file with mode: 0644]
examples/echo.mak [new file with mode: 0644]
examples/echo2.c [new file with mode: 0644]
examples/echo2.html [new file with mode: 0644]
examples/echo2.mak [new file with mode: 0644]
examples/echo2_nt.fcgi [new file with mode: 0644]
examples/echo_nt.fcgi [new file with mode: 0644]
examples/log-dump.c [new file with mode: 0644]
examples/sample-store.c [new file with mode: 0644]
examples/tclHash.c [new file with mode: 0644]
examples/tiny-authorizer.c [new file with mode: 0644]
examples/tiny-cgi.c [new file with mode: 0644]
examples/tiny-fcgi.c [new file with mode: 0644]
examples/tiny-fcgi.cgi [new file with mode: 0755]
examples/tiny-fcgi.mak [new file with mode: 0644]
examples/tiny-fcgi2.c [new file with mode: 0644]
examples/tiny-fcgi2.cgi [new file with mode: 0755]
examples/tiny-fcgi2.mak [new file with mode: 0644]
examples/tiny-fcgi2_nt.fcgi [new file with mode: 0644]
examples/tiny-fcgi_nt.fcgi [new file with mode: 0644]
examples/tiny-perl-fcgi [new file with mode: 0755]
examples/tiny-tcl-fcgi [new file with mode: 0755]
images/aplib-hd.gif [new file with mode: 0644]
images/divider.gif [new file with mode: 0644]
images/fcgi-hd.gif [new file with mode: 0644]
images/mail-hd.gif [new file with mode: 0644]
images/navbar.gif [new file with mode: 0644]
images/serv-hd.gif [new file with mode: 0644]
images/words-hd.gif [new file with mode: 0644]
include/fastcgi.h [new file with mode: 0644]
include/fcgi_config.h.in [new file with mode: 0644]
include/fcgi_config_x86.h [new file with mode: 0644]
include/fcgi_stdio.h [new file with mode: 0644]
include/fcgiapp.h [new file with mode: 0644]
include/fcgiappmisc.h [new file with mode: 0644]
include/fcgimisc.h [new file with mode: 0644]
include/fcgios.h [new file with mode: 0755]
include/tcl.h [new file with mode: 0644]
include/tclInt.h [new file with mode: 0644]
include/tclRegexp.h [new file with mode: 0644]
index.html [new file with mode: 0755]
install.sh [new file with mode: 0755]
java/classes/FCGIGlobalDefs.class [new file with mode: 0644]
java/classes/FCGIInputStream.class [new file with mode: 0644]
java/classes/FCGIInterface.class [new file with mode: 0644]
java/classes/FCGIMessage.class [new file with mode: 0644]
java/classes/FCGIOutputStream.class [new file with mode: 0644]
java/classes/FCGIRequest.class [new file with mode: 0644]
java/src/FCGIGlobalDefs.java [new file with mode: 0644]
java/src/FCGIInputStream.java [new file with mode: 0644]
java/src/FCGIInterface.java [new file with mode: 0644]
java/src/FCGIMessage.java [new file with mode: 0644]
java/src/FCGIOutputStream.java [new file with mode: 0644]
java/src/FCGIRequest.java [new file with mode: 0644]
libfcgi/Makefile.in [new file with mode: 0644]
libfcgi/descrip.dfc [new file with mode: 0644]
libfcgi/fcgi_stdio.c [new file with mode: 0644]
libfcgi/fcgiapp.c [new file with mode: 0644]
libfcgi/libfcgi.mak [new file with mode: 0644]
libfcgi/libfcgi.mak.in [new file with mode: 0644]
libfcgi/os_unix.c [new file with mode: 0755]
libfcgi/os_win32.c [new file with mode: 0755]
libfcgi/strerror.c [new file with mode: 0644]
tcl/common/tclAppInit.c [new file with mode: 0644]
tcl/common/tclFCGI.c [new file with mode: 0644]
tcl/tcl7.4/Makefile.in [new file with mode: 0644]
tcl/tcl7.4/configure.in [new file with mode: 0755]
version.conf [new file with mode: 0644]
version.in [new file with mode: 0644]

diff --git a/FastCGI.mak.in b/FastCGI.mak.in
new file mode 100644 (file)
index 0000000..7801918
--- /dev/null
@@ -0,0 +1,148 @@
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20
+# ** DO NOT EDIT **
+
+# TARGTYPE "transient" 0x0000
+
+# Another service of Ace Wrecking and Software
+#
+# xmb Generated NMAKE File for NT and VC++ 
+
+!IF "$(CFG)" == ""
+CFG=FastCGI - Win32 Debug
+!MESSAGE No configuration specified.  Defaulting to FastCGI - Win32 Debug.
+!ENDIF 
+
+!IF "$(CFG)" != "FastCGI - Win32 Release" && "$(CFG)" != "FastCGI - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line.  For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "FastCGI.mak" CFG="FastCGI - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "FastCGI - Win32 Release" (based on "transient")
+!MESSAGE "FastCGI - Win32 Debug" (based on "transient")
+!MESSAGE 
+!ERROR An invalid configuration is specified.
+!ENDIF 
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE 
+NULL=nul
+!ENDIF 
+#
+#
+#  Makefile for @SUBSYSTEM@ version @VERSION@(@PLATFORM@)
+#  Auto-generated, do not edit this Makefile
+#
+
+#
+# Default top-level directories in which to install architecture-
+# specific files (exec_prefix) and machine-independent files such
+# as scripts (prefix).  The values specified here may be overridden
+# at configure-time with the --exec-prefix and --prefix options
+# to the "configure" script.
+
+#-----------------------------------------------------------------------------
+# Normally do not edit this section. It is setup by configure
+#
+# the shell MUST BE /bin/sh
+#
+SHELL  = @SHELL@
+PLATFORM_CLASS = @PLATFORM_CLASS@
+O = @O@
+L = @L@
+X = @X@
+#
+exec_prefix =   @exec_prefix@
+prefix =        @prefix@
+common_prefix = @common_prefix@
+CVS_TAG =       @CVS_TAG@
+SRC_DIR =      @srcdir@
+BIN_DIR =       $(exec_prefix)\bin
+LIB_DIR =       $(exec_prefix)\lib
+ETC_DIR =       $(exec_prefix)\etc
+BINCLUDE_DIR = $(exec_prefix)\include
+INCLUDE_DIR =  $(common_prefix)\include
+CBIN_DIR =      $(common_prefix)\bin
+CLIB_DIR =      $(common_prefix)\lib
+CETC_DIR =      $(common_prefix)\etc
+CONTRIB_DIR =   $(common_prefix)\contrib
+MAN_DIR =       $(common_prefix)\man
+MAN1_DIR =      $(MAN_DIR)\man1
+MAN2_DIR =      $(MAN_DIR)\man2
+MAN3_DIR =      $(MAN_DIR)\man3
+MAN5_DIR =      $(MAN_DIR)\man5
+MAN8_DIR =      $(MAN_DIR)\man8
+INFO_DIR =      $(common_prefix)\info
+INSTALL                = @INSTALL@
+INSTALL_PROGRAM        = @INSTALL@
+INSTALL_DATA   = @INSTALL_DATA@
+CC              = @CC@ @CCDEFS@
+CFLAGS          = @CFLAGS@ @INCLUDE_PATH@ -I. @DEFS@
+RANLIB         = @RANLIB@
+AR             = @AR@
+GENMSGC         = @GENMSGC@
+GENMSGH         = @GENMSGH@
+#
+#---------------------------------------------------------------------------
+#
+#
+# All OMI makefiles will have the following make targets:
+#
+#      all - first rule, builds everything
+#      export - installs everything
+#      test - runs unit tests. This can be a null rule.
+#      clean - cleans up after a make
+#      realclean - cleans up after a configure and make
+#
+
+
+
+!IF  "$(CFG)" == "FastCGI - Win32 Release"
+
+ALL : 
+       @ sh -c "(cd libfcgi; nmake -f libfcgi.mak  CFG='libfcgi - Win32 Release' ALL)"
+       @ sh -c "(cd cgi-fcgi; nmake -f cgi-fcgi.mak  CFG='cgi-fcgi - Win32 Release' ALL)"
+
+
+CLEAN : 
+       @ sh -c "(cd libfcgi; nmake -f libfcgi.mak CFG='$(CFG)'  CLEAN)"
+       @ sh -c "(cd cgi-fcgi; nmake -f cgi-fcgi.mak CFG='$(CFG)'  CLEAN)"
+
+
+EXPORT : 
+       @ sh -c "(cd libfcgi; nmake -f libfcgi.mak CFG='libfcgi - Win32 Release' EXPORT)"
+       @ sh -c "(cd cgi-fcgi; nmake -f cgi-fcgi.mak CFG='cgi-fcgi - Win32 Release' EXPORT)"
+
+
+TEST : 
+       @ sh -c "(cd libfcgi; nmake -f libfcgi.mak CFG='libfcgi - Win32 Release' TEST)"
+       @ sh -c "(cd cgi-fcgi; nmake -f cgi-fcgi.mak CFG='cgi-fcgi - Win32 Release' TEST)"
+
+
+!ELSEIF "$(CFG)" == "FastCGI - Win32 Debug"
+
+ALL : 
+       @ sh -c "(cd libfcgi; nmake -f libfcgi.mak  CFG='libfcgi - Win32 Debug' ALL)"
+       @ sh -c "(cd cgi-fcgi; nmake -f cgi-fcgi.mak  CFG='cgi-fcgi - Win32 Debug' ALL)"
+
+
+CLEAN : 
+       @ sh -c "(cd libfcgi; nmake -f libfcgi.mak CFG='$(CFG)'  CLEAN)"
+       @ sh -c "(cd cgi-fcgi; nmake -f cgi-fcgi.mak CFG='$(CFG)'  CLEAN)"
+
+
+EXPORT : 
+       @ sh -c "(cd libfcgi; nmake -f libfcgi.mak CFG='libfcgi - Win32 Debug' EXPORT)"
+       @ sh -c "(cd cgi-fcgi; nmake -f cgi-fcgi.mak CFG='cgi-fcgi - Win32 Debug' EXPORT)"
+
+
+TEST : 
+       @ sh -c "(cd libfcgi; nmake -f libfcgi.mak CFG='libfcgi - Win32 Debug' TEST)"
+       @ sh -c "(cd cgi-fcgi; nmake -f cgi-fcgi.mak CFG='cgi-fcgi - Win32 Debug' TEST)"
+
+
+!ENDIF
+
diff --git a/FastCGI.mb b/FastCGI.mb
new file mode 100755 (executable)
index 0000000..ecd36b7
--- /dev/null
@@ -0,0 +1,19 @@
+
+# buildon platform {c-compiler c-flags} config-flags
+#
+BuildOn bsdi_20 gcc --with-omireleases
+BuildOn bsdi_11 gcc --with-omireleases
+BuildOn aix_414 c89 --with-omireleases
+BuildOn hpux_905 {c89 -D_HPUX_SOURCE} --with-omireleases
+BuildOn hpux_10 {c89 -D_HPUX_SOURCE} --with-omireleases
+BuildOn hpux_1010 {c89 -D_HPUX_SOURCE} --with-omireleases
+BuildOn sun_54 cc --with-omireleases
+BuildOn sun_55 cc --with-omireleases
+BuildOn sun_414 gcc --with-omireleases
+BuildOn osf_30 {cc -std1}  --with-omireleases
+BuildOn osf_32 {cc -std1} --with-omireleases
+BuildOn osf_40 {cc -std1} --with-omireleases
+BuildOn sgi_53 gcc --with-omireleases
+BuildOn sgi_62 cc --with-omireleases
+BuildOn wnt_40 cc --with-omireleases
+
diff --git a/LICENSE.TERMS b/LICENSE.TERMS
new file mode 100755 (executable)
index 0000000..7e6bdfd
--- /dev/null
@@ -0,0 +1,28 @@
+This FastCGI application library source and object code (the
+"Software") and its documentation (the "Documentation") are
+copyrighted by Open Market, Inc ("Open Market").  The following terms
+apply to all files associated with the Software and Documentation
+unless explicitly disclaimed in individual files.
+
+Open Market permits you to use, copy, modify, distribute, and license
+this Software and the Documentation for any purpose, provided that
+existing copyright notices are retained in all copies and that this
+notice is included verbatim in any distributions.  No written
+agreement, license, or royalty fee is required for any of the
+authorized uses.  Modifications to this Software and Documentation may
+be copyrighted by their authors and need not follow the licensing
+terms described here.  If modifications to this Software and
+Documentation have new licensing terms, the new terms must be clearly
+indicated on the first page of each file where they apply.
+
+OPEN MARKET MAKES NO EXPRESS OR IMPLIED WARRANTY WITH RESPECT TO THE
+SOFTWARE OR THE DOCUMENTATION, INCLUDING WITHOUT LIMITATION ANY
+WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  IN
+NO EVENT SHALL OPEN MARKET BE LIABLE TO YOU OR ANY THIRD PARTY FOR ANY
+DAMAGES ARISING FROM OR RELATING TO THIS SOFTWARE OR THE
+DOCUMENTATION, INCLUDING, WITHOUT LIMITATION, ANY INDIRECT, SPECIAL OR
+CONSEQUENTIAL DAMAGES OR SIMILAR DAMAGES, INCLUDING LOST PROFITS OR
+LOST DATA, EVEN IF OPEN MARKET HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.  THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS".
+OPEN MARKET HAS NO LIABILITY IN CONTRACT, TORT, NEGLIGENCE OR
+OTHERWISE ARISING OUT OF THIS SOFTWARE OR THE DOCUMENTATION.
diff --git a/Makefile.in b/Makefile.in
new file mode 100755 (executable)
index 0000000..d62a423
--- /dev/null
@@ -0,0 +1,88 @@
+#
+#  Makefile for FastCGI development kit
+#
+#  Open Market, Inc.
+#
+#  $Id: Makefile.in,v 1.1 1997/09/16 15:36:24 stanleyg Exp $
+#
+SHELL = @SHELL@
+O =     @O@
+L =     @L@
+
+#
+exec_prefix   = @exec_prefix@
+prefix        = @prefix@
+common_prefix = @common_prefix@
+CVS_TAG =       @CVS_TAG@
+EXPORT_DIR = $(prefix)/build
+PROTO_DIR = ../proto
+BIN_DIR       = $(exec_prefix)/bin
+LIB_DIR       = $(prefix)/lib
+LINC_DIR      = $(prefix)/include
+COMMON_DIR    = $(common_prefix)
+INC_DIR       = $(common_prefix)/include
+DOC_DIR       = $(common_prefix)/doc
+MAN_DIR       = $(common_prefix)/man
+MAN1_DIR      = $(MAN_DIR)/man1
+MAN3_DIR      = $(MAN_DIR)/man3
+RANLIB        = @RANLIB@
+
+INSTALL                = @INSTALL@
+INSTALL_PROGRAM        = @INSTALL_PROGRAM@
+INSTALL_DATA   = @INSTALL_DATA@
+
+TARGET_DIRS = $(BIN_DIR) $(LIB_DIR) $(COMMON_DIR) $(INC_DIR) $(MAN_DIR) \
+              $(MAN1_DIR) $(MAN3_DIR) $(LINC_DIR) $(EXPORT_DIR)
+
+LINKS = examples/perl examples/tclsh
+
+all:
+       (cd libfcgi; make all)
+       (cd cgi-fcgi; make all)
+       (cd examples; make all)
+
+export:
+       @ for i in $(TARGET_DIRS); \
+         do \
+         if test ! -d $$i; then \
+           echo "Creating $$i";\
+           mkdir -p $$i;\
+         fi;\
+         done;
+
+       $(INSTALL_DATA)    include/fastcgi.h $(INC_DIR)
+       $(INSTALL_DATA)    include/fcgi_stdio.h $(INC_DIR)
+       $(INSTALL_DATA)    include/fcgiapp.h $(INC_DIR)
+       $(INSTALL_DATA)    include/fcgiappmisc.h $(INC_DIR)
+       $(INSTALL_DATA)    include/fcgimisc.h $(INC_DIR)
+       $(INSTALL_DATA)    include/fcgi_config.h $(LINC_DIR)
+       $(INSTALL_DATA)    libfcgi/libfcgi.${L} $(LIB_DIR)
+       $(RANLIB)          $(LIB_DIR)/libfcgi.${L}
+       $(INSTALL_PROGRAM) cgi-fcgi/cgi-fcgi $(BIN_DIR)
+       $(INSTALL_DATA)    doc/cgi-fcgi.1 $(MAN1_DIR)
+       $(INSTALL_DATA)    doc/FCGI_Accept.3 $(MAN3_DIR)
+       $(INSTALL_DATA)    doc/FCGI_Finish.3 $(MAN3_DIR)
+       $(INSTALL_DATA)    doc/FCGI_SetExitStatus.3 $(MAN3_DIR)
+       $(INSTALL_DATA)    doc/FCGI_StartFilterData.3 $(MAN3_DIR)
+       rm -rf $(PROTO_DIR)
+       for i in $(LINKS); do  if  test ! -r $$i; then rm -f $$i; fi; done; 
+       cp -R . $(PROTO_DIR) 
+       (cd $(PROTO_DIR); make clean)
+       find $(PROTO_DIR) -name CVS -exec rm -rf {} \;
+       rm -rf $(EXPORT_DIR)
+       cp -R $(PROTO_DIR) $(EXPORT_DIR)
+       rm -rf $(PROTO_DIR)
+
+reconfig: clean
+       uname -rs >config.uname
+       ./configure
+
+clean:
+       rm -f *.${L} *.${O} core.* errs *~ \#* TAGS *.E a.out
+       rm -f *.${L} *.${O} core.* errs *~ \#* TAGS *.E a.out
+       rm -f config.cache config.log config.status config.uname
+       (cd libfcgi; make clean)
+       (cd cgi-fcgi; make clean)
+       (cd examples; make clean)
+
+# ----------------------------------------------------------------------------
diff --git a/README b/README
new file mode 100755 (executable)
index 0000000..fd19ef5
--- /dev/null
+++ b/README
@@ -0,0 +1,335 @@
+FastCGI Developer's Kit README
+------------------------------
+
+    Version 2.0b2, 04 April 1997
+    $Id: README,v 1.1 1997/09/16 15:36:24 stanleyg Exp $
+    Copyright (c) 1996 Open Market, Inc.
+    See the file "LICENSE.TERMS" for information on usage and redistribution
+    of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+
+Basic Directions
+----------------
+
+Open the kit's index page, index.html in this directory, using the
+"Open File" command in your Web browser.  The index page gives you an
+overview of the kit structure and helps you navigate the kit. The
+index page also contains links that run some example applications, but
+the applications won't work when index.html is opened using the "Open
+File" command because they aren't being accessed through a Web server.
+
+For further instructions see the FastCGI Developer's Kit
+document, accessible via the index page.
+
+
+TODO:
+----
+The following is a small list of what should be available in the final
+release of the FDK.
+  1. Fix all compilation problems on all Unixes and NT. 
+  2. Provide perl.c for perl5.003 (replace run() with runops())
+  3. Provide latest Tcl patches (NT requires Tcl7.5+)
+  4. Provide FCGI_VERSION directive for automatic version recognition.
+-- if available
+  5. Provide new Java interface to conform to JDK1.1.1 (due to final
+     System.in)
+  6. Provide samples of Perl on NT.
+
+What's New: Version 2.0b2, 04 April 1997
+--------------------------------------
+
+Some additional bug fixes, mostly on NT port.  The following list
+of the bugs that have been and fixed:
+  1. Updated build_no_shell.bat to create a FcgiBin directory under the
+     top level of the FastCGI kit and copy all executables and the
+     FastCGI dll there.  This makes it easier to use.
+  2. Corrected the Unix version of OS_SpawnChild so that it didn't close 
+     the listenFd when forking off child processes.  This code would
+     affect the cgi-fcgi application on Unix.  The problem is that it
+     could only start one fastcgi process.  Any other processes would not
+     get the listen file descriptor and they would die.
+  3. Corrected cgi-fcgi.c so that it properly handled large posts.  The
+     bug was introduced with the asynchronous I/O model implemented for
+     the Windows NT port.  The problem was not clearing a bit indicating
+     that a read had completed.  This caused the application to stall.
+  4. Corrected OS_DoIo, the function used for scheduling I/O for cgi-fcgi.
+     It had a bug where it wasn't creating a copy of the file descriptors 
+     used for I/O.  This would cause the master list of FDs to watch to be 
+     reset and thus would hang the application because we would no longer
+     watch for I/O on those file descriptors. (This problem was specific to
+     Unix and only happened with the cgi-fcgi application.)
+  5. Cleaned up several compilation warnings present on OSF.
+     
+
+What's New: Version 2.0b1, 24 March 1997
+--------------------------------------
+
+This "beta" release adds the functionality of "cgi-fcgi" to the
+Windows NT platform and allows for creation of FastCGI applications
+running in Win32 environment.  There is almost no new documentation 
+provided, but will become part of this kit in the official release.
+  1. Added FastCGI libraries running on Windows NT 3.51+
+  2. Rename errno to FCGI_errno in the FCGX_Stream, which was causing
+     problems on some Linux platforms and NT.
+  3. Fixed a parenthesis problem in FCGI_gets
+
+
+What's New: Version 1.5.1, 12 December 1996
+--------------------------------------
+
+This release introduces mostly bug fixes, without any additional
+functionality to the kit.  
+  1. Conditional compilation for the hp-ux compiler.
+  2. Loop around the accept() call to eliminate "OS Error: Interrupted
+     System Call" message from appearing in the error logs.
+  3. Casting of the FCGI_Header to (char *), which eliminates the 
+     assertion failure "bufPtr->size>0".
+  
+
+What's New: Version 1.5, 12 June 1996
+--------------------------------------
+
+General:
+
+  Added a white paper on FastCGI application performance to the
+  doc directory.  Generally brought the other docs up to date.
+
+  Rearranged the kit to put more emphasis on running FastCGI-capable
+  servers and less on running cgi-fcgi.  Added
+  examples/conf/om-httpd.config, a config file that demonstrates all
+  of the example apps.  (Would like to have similar configs for NCSA
+  and Apache.)
+
+  Added the tiny-authorizer and sample-store applications to
+  the examples.  These are explained in the index.html.
+
+    In addition to everything else it does, sample-store demonstrates
+    a bug in the Open Market WebServer 2.0: When an Authorizer
+    application denies access, the server tacks some extra junk onto
+    the end of the page the application returns.  A little ugly but
+    not fatal.
+
+C libraries:
+
+  Added the functions FCGX_Finish and FCGI_Finish.  These functions
+  finish the current request from the HTTP server but do not begin a
+  new request.  These functions make it possible for applications to
+  perform other processing between requests.  An application must not
+  use its stdin, stdout, stderr, or environ between calling
+  FCGI_Finish and calling FCGI_Accept.  See doc/FCGI_Finish.3 for
+  more information.  The application examples/sample-store.c demonstrates
+  the use of FCGI_Finish.
+
+  Added conditional 'extern "C"' stuff to the .h files fcgi_stdio.h,
+  fcgiapp.h, and fcgiappmisc.h for the benefit of C++ applications
+  (suggested by Jim McCarthy).
+
+  Fixed two bugs in FCGX_VFPrintF (reported by Ben Laurie).  These
+  bugs affected processing of %f format specifiers and of all format
+  specifiers containing a precision spec (e.g "%12.4g").
+
+  Fixed a bug in FCGX_Accept in which the environment variable
+  FCGI_WEBSERVER_ADDRS was being read rather than the specified
+  FCGI_WEB_SERVER_ADDRS.  Fixed a bug in FCGX_Accept in which the
+  wrong storage was freed when FCGI_WEB_SERVER_ADDRS contained more
+  than one address or if the address check failed.
+
+  Changed FCGX_Accept to avoid depending upon accept(2) returning the
+  correct value of sin_family in the socketaddr structure for an
+  AF_UNIX connection (SCO returns the wrong value, as reported by Paul
+  Mahoney).
+
+  Changed the error retry logic in FCGX_Accept.  FCGX_Accept now
+  returns -1 only in case of operating system errors that occur while
+  accepting a connection (e.g. out of file descriptors).  Other errors
+  cause the current connection to be dropped and a new connection to
+  be attempted.
+
+Perl:
+
+  Changed FCGI.xs to make it insensitive to Perl's treatment of
+  environ (we hope).  Changed FCGI::accept so the initial environment
+  variables are not unset on the first call to FCGI::accept (or on
+  subsequent calls either).  Added the echo-perl example
+  program.  Added a workaround for the "empty initial environment bug"
+  to tiny-perl-fcgi.  Changed the example Perl scripts to use a new
+  symbolic link ./perl, avoiding the HP-UX 32 character limit on the
+  first line of a command interpreter file.
+
+  Because the FastCGI-enabled Perl interpreter uses the C fcgi_stdio
+  library, it picks up all the changes listed above for C.  There's
+  a new Perl subroutine FCGI::finish.
+
+Tcl:
+
+  Fixed a bug in tclFCGI.c that caused the request environment
+  variables to be lost.  Changed FCGI_Accept so the initial
+  environment variables are not unset on the first call to FCGI_Accept
+  (or on subsequent calls either).  Added the echo-tcl example
+  program.  Fixed another bug that caused Tcl to become confused by
+  file opens; as a side effect of this change, writes to stdout/stderr
+  that occur in an app running as FastCGI before FCGI_Accept is called
+  are no-ops rather than crashing Tcl.  Changed the example Tcl
+  scripts to use a new symbolic link ./tclsh, avoiding the HP-UX 32
+  character limit on the first line of a command interpreter file.
+
+  Because the FastCGI-enabled Tcl interpreter uses the C fcgi_stdio
+  library, it picks up all the changes listed above for C; there's
+  a new Tcl command FCGI_Finish.
+
+Java:
+
+  Fixed a sign-extension bug in FCGIMessage.java that caused bad encodings
+  of names and values in name-value pairs for lengths in [128..255].
+  Made small cleanups in the Java example programs to make them more
+  consistent with the other examples.
+
+
+
+What's New: Version 1.4, 10 May 1996
+--------------------------------------
+
+Includes Java classes and Java examples.
+
+
+
+What's New: Version 1.3.1, 6 May 1996
+--------------------------------------
+
+New, simplified, license terms.  Includes an expanded whitepaper that
+describes FastCGI support in Open Market's Secure WebServer 2.0.
+Includes Open Market FastCGI 1.0 Programmer's Guide.  Includes
+"FastCGI: A High-Performance Gateway Interface", a position paper
+presented at the workshop "Programming the Web - a search for APIs",
+Fifth International World Wide Web Conference, 6 May 1996, Paris,
+France.
+
+
+
+What's New: Version 1.3, 29 April 1996
+--------------------------------------
+
+First public release; new license terms on all files.
+
+Changed cgi-fcgi.c to use SO_REUSEADDR when creating the listening socket;
+this avoids the need to wait through the TIME_WAIT state on all the TCP
+connections made by the previous instance of an external application
+you are restarting. 
+
+
+
+What's New: Version 1.2.2, 15 April 1996
+----------------------------------------
+
+Partially fixed a bug in Perl's FCGI::accept (source file FCGI.xs).
+The per-request environment variables were being lost.  Now the
+per-request environment variables show up correctly, except that if
+the Perl application has an empty initial environment, the environment
+variables associated with the *first* request are lost.  Therefore,
+when starting Perl, always set some environment variable using the
+AppClass -initial-env option, or by running cgi-fcgi in a non-empty
+environment.
+
+
+
+What's New: Version 1.2.1, 22 March 1996
+----------------------------------------
+
+Fixed a bug in FCGI_Accept.  If your application running as FastCGI
+opened a file before calling FCGI_Accept, it would decide that it
+was really running as CGI.  Things went downhill quickly after that!
+
+Also added advisory locking to serialize calls to accept on shared
+listening sockets on Solaris and IRIX, to work around problems
+with concurrent accept calls on these platforms.
+
+
+
+What's New: Version 1.2, 20 March 1996
+--------------------------------------
+
+1. This version of the kit implements the most recent draft
+of the protocol spec.  Enhancements to the protocol include
+a BEGIN_REQUEST record that simplifies request ID management
+and transmits role and keep-alive information, and a simplified
+end-of-stream indication.
+
+The protocol spec has been revised to describe exactly what's
+been implemented, leaving out the features that we hope to
+introduce in later releases.
+
+At the application level, the visible change is the FCGI_ROLE
+variable that's available to applications.  This allows an application
+to check that it has been invoked in the expected role.  A single
+application can be written to respond in several roles.  The
+FCGI_Accept.3 manpage contains more information.
+
+2.  We introduced the new "module" prefix FCGX in order to simplify
+the relationship between fcgi_stdio and fcgiapp.
+
+A growing number of functions are provided in both fcgi_stdio and
+fcgiapp versions.  Rather than inventing an ad hoc solution for each
+naming conflict (as we did with FCGI_accept and FCGI_Accept), we've
+bitten the bullet and systematically renamed *all* the fcgapp
+primitives with the prefix FCGX_.  In fcgi_stdio, we've renamed
+FCGI_accept to FCGI_Accept.  So all functions that are common in the
+two libraries have the same name modulo the different prefixes.
+
+The Accept function visible in Tcl is now called FCGI_Accept, not
+FCGI_accept.
+
+The Accept function visible in Perl is now FCGI::accept.  All
+lower case names for functions and all upper case names for
+modules appears to be a Perl convention, so we conform.
+
+3. The kit now fully supports the Responder, Authorizer,
+and Filter roles.
+
+The Filter role required a new function, FCGI_StartFilterData.
+FCGI_StartFilterData changes the input stream from reading
+FCGI_STDIN data to reading FCGI_DATA data.  The manpage
+gives full details.
+
+Another new function, FCGI_SetExitStatus, is primarily for
+the Responder role but is available to all.  FCGI_SetExitStatus
+allows an application to set a nonzero "exit" status
+before completing a request and calling FCGI_Accept again.
+The manpage gives full details.
+
+These two new functions are provided at both the fcgi_stdio interface
+and the basic fcgiapp interface.  Naturally, the fcgiapp versions are
+called FCGX_StartFilterData and FCGX_SetExitStatus.
+
+4. The fcgiapp interface changed slightly in order to treat
+the streams and environment data more symmetrically.
+
+FCGX_Accept now returns an environment pointer, rather than requiring
+a call to FCGX_GetAllParams to retrieve an environment pointer.
+FCGX_GetParam takes an explicit environment pointer argument.
+FCGX_GetAllParams is eliminated.  See the documentation in the header
+file for complete information.
+
+fcgiapp also added the procedure FCGX_IsCGI, providing a standardized
+test of whether the app was started as CGI or FastCGI.
+
+5. We've ported the kits to vendor-supported ANSI C compilers
+on Sun (Solaris 2.X), HP, and Digital platforms.  GCC can be
+selected on these platforms by performing SETENV CC gcc before
+running configure.
+
+
+
+What's New: Version 1.1, 30 Jan 1996
+------------------------------------
+
+1. More platforms: Digital UNIX, IBM AIX, Silicon Graphics IRIX,
+Sun SunOS 4.1.4.
+
+2. Perl and Tcl: Simple recipes for producing Perl and Tcl
+interpreters that run as FastCGI applications.  No source
+code changes are needed to Perl and Tcl.  Documented
+in separate documents, accessible via the index page.
+
+
+
+Version 1.0, 10 Jan 1996
+------------------------
diff --git a/README_NT.txt b/README_NT.txt
new file mode 100644 (file)
index 0000000..9160d7c
--- /dev/null
@@ -0,0 +1,149 @@
+FastCGI for Windows NT README (V2.0, beta 1)
+============================================
+
+    Version 2.0.1, 20 March 1997
+    $Id: README_NT.txt,v 1.1 1997/09/16 15:36:24 stanleyg Exp $
+    Copyright (c) 1996 Open Market, Inc.
+    See the file "LICENSE.TERMS" for information on usage and redistribution
+    of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+
+       This is a beta copy of the FastCGI libraries for Windows NT 3.51 
+and NT 4.0.  It will also build on (or should build on) all previously 
+supported versions on Unix.
+
+        The Unix build instructions are identical to the past.  The 
+Windows NT version however is slightly different due to the fact that NT 
+doesn't contain a native equivalent to the bourne shell to start with.
+NT also doesn't support the same Makefile structure and therefore we have
+provided Visual C++ project makefiles for the cgi-fcgi application as well
+as the libfcgi DLL.
+
+        The first version of the libraries for Windows NT will require a
+little care in installing and building.  However, what do you want for 
+free?  Subsequent versions will ideally support a more NT look and feel 
+but that's if time permits.
+
+        For those of you that have the MKS toolkit or other bourne shell
+equivalent for NT, great.  You're off to a good start and will be able
+to use the configure script provided to generate your config header file
+at the very least.  However, in order to make life easier, we are 
+providing an NT version of the header file that should allow you to build
+the sample applications without requiring you to run configure at all.
+(NOTE: The NT version has only been tested on Windows NT 4.0 running on
+       X86 hardware.  Other CPUs may have slightly different defines.)
+
+        There are two batch files provided which will build Debug versions
+of fastcgi.dll and the cgi-fcgi application.  They are:
+
+        build_no_shell.bat - This will copy a canned version of the
+                             fcgi_config_x86.h file to fcgi_config.h and
+                             remove the need to use the "configure" script
+                             to generate this.  (This is the recommended
+                             way to build the sample DLL and applications.)
+
+                 build.bat - This version will run the "configure" script
+                             and will then build libfcgi.dll and 
+                             cgi-fcgi.exe.
+
+       Installation
+       ============
+
+        Unpack the kit and install it into a directory of your choice.
+Try something simple like "C:\FastCGI.beta".
+
+        In order to run under IIS using the cgi-fcgi.exe "shim" program,
+we need to create a file extension type that IIS will recognize and will
+automatically launch the application and/or connect to the target FastCGI
+application.
+
+        1) Make a directory "C:\FastCGI.beta".  The name is not critical
+           but is is what is assumed for the remainder of this README.
+
+        2) cd into "C:\FastCGI.beta".
+
+        3) Unpack the kit into this directory.
+
+        4) Run build_no_shell.bat
+
+       5) Add the .fcgi file type to the registry for IIS.  This is done
+           using regedit.
+
+\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W3SVC\Parameters\Script Map
+
+        Edit -> New -> String Value <CR>
+
+        6) Type in ".fcgi" for the "String Value" extension name.
+
+        7) Double click the ".fcgi" entry.
+
+        8) Enter C:\FastCGI.beta\FcgiBin\cgi-fcgi.exe -f %s
+           NOTE: This assumes you installed into c:\FastCGI.beta
+
+           Save this value and exit regedit.
+
+        9) Add the pathname of libfcgi.dll to your system path 
+           environment variable.
+                "C:\FastCGI.beta\libfcgi\Debug"
+
+           NOTE: The build_no_shell.bat command will copy all the sample
+                 applications as well as cgi-fci.exe and libfcgi.dll to
+                 the directory "FcgiBin" which will be created as a result
+                 of the build_no_shell.bat command being run.  This makes
+                 it easier to use and removes the need for adding the
+                 paths to the system environment as the libfcgi.dll will
+                 live in the same directory as the applications which will
+                 be using it.  This has been tested and qualified on 
+                 IIS 3.0.
+
+                 If your applications live in a directory other than the
+                 FcgiBin directory AND there's no path environment 
+                 variable registered which contains a pointer to a valid
+                 libfcgi.dll the FastCGI application will not work.
+
+        10) Use Internet Service Manager (or the registry editor if you're
+            brave) and map in the directory "C:\FastCGI.beta\FcgiBin" 
+            as a virtual directory "/fcgi" with execute access and 
+            read access.
+
+        You should now be ready to try out the shipped samples.  Try this by
+        accessing the following urls:
+
+        The url "http://yourServer/fcgi/tiny-fcgi.exe" reloaded repeatedly
+        should produce the following output:
+
+       FastCGI Hello! (C, fcgi_stdio library)
+
+       Request number 1 running on host "yourServer" Process ID: N
+
+        where:
+
+        yourServer is the name of your server.
+
+        N is the process id number of the tiny-fcgi.exe process.  This
+        should be changing each time you reload the URL.
+
+       Now try the url "http://yourServer/fcgi/tiny-fcgi_nt.fcgi".  The
+        output from this url should produce the same as the preceeding url 
+        but you should notice the "Request number" incrementing each time
+        you reload and the Process ID should remain constant.  If this is 
+        working, you have a persistent FastCGI application running.
+
+
+       Known Problems/Limitations
+       ==========================
+
+        1) This port was build for Windows NT 3.51 and above.  It was
+           not built with Windows 95 as one of the target platforms.
+           The reason is that I/O completion ports are used for 
+           asynchronous I/O which are not present on Windows 95.  
+           Changing this is not that big a job and involves changing to 
+           use overlapped I/O.  Again, the port was towards Windows NT
+           which was why the I/O completion ports were chosen.  This
+           mechanism was also chosen in anticipation of the multi-threaded
+           FastCGI for NT as it will map to the model we currently
+           have designed.
+
+
+NOTE: Use the application "kill.exe" contained in the NT resource kit
+      to kill persistent FastCGI applciations!
+
diff --git a/acconfig.h b/acconfig.h
new file mode 100755 (executable)
index 0000000..83145bb
--- /dev/null
@@ -0,0 +1,38 @@
+/* Define to type of ssize_t, if not on this platform.  */
+#undef ssize_t
+
+/* Define if you have the <sys/select.h> include file */
+#undef HAVE_SYS_SELECT_H
+
+/* Define if you don't have fd_set */
+#undef NO_FD_SET
+
+/* Define if you don't have sys_errlist */
+#undef NO_SYS_ERRLIST
+
+/* Define if want to compile with ASSERT() statements */
+#undef WITH_ASSERT
+
+/* Define if want to compile with additional debugging code */
+#undef WITH_DEBUG
+
+/* Define if want to compile with hooks for testing */
+#undef WITH_TEST
+
+/* Define if sockaddr_un in <sys/un.h> contains the sun_len component */
+#undef HAVE_SOCKADDR_UN_SUN_LEN
+
+/* Define if we have f{set,get}pos functions */
+#undef HAVE_FPOS
+
+/* Define if we need cross-process locking */
+#undef USE_LOCKING
+
+/* Define if va_arg(arg, long double) crashes the compiler. */
+#undef HAVE_VA_ARG_LONG_DOUBLE_BUG
+
+/* Don't know what this stuff is for */
+#undef HAVE_MATHLIB
+#undef WITH_DOMESTIC
+#undef WITH_EXPORT
+#undef WITH_GLOBAL
diff --git a/build.bat b/build.bat
new file mode 100755 (executable)
index 0000000..47bacdc
--- /dev/null
+++ b/build.bat
@@ -0,0 +1,44 @@
+rem
+rem This build script is intended to be run from the root dir that
+rem the FastCGI kit was unpacked in.  It is intended to be used for
+rem those that want to run the configure script.
+rem
+rem $Id: build.bat,v 1.1 1997/09/16 15:36:24 stanleyg Exp $
+rem
+rem
+
+echo off
+
+if not exist "config.cache" sh -c "./configure"
+
+
+rem
+rem Build the FastCGI DLL and import library.
+rem
+cd libfcgi
+nmake -f libfcgi.mak
+%1%
+
+rem
+rem Build the cgi-fcgi.exe "shim" application.
+rem
+cd ..\cgi-fcgi
+nmake -f cgi-fcgi.mak
+%1%
+
+rem
+rem Now build the sample applications that have been qualified.
+rem
+cd ..\examples
+nmake -f echo.mak
+nmake -f echo2.mak
+nmake -f tiny-fcgi.mak
+nmake -f tiny-fcgi2.mak
+
+cd ..
+goto :DONE
+
+:NO_CONFIG
+echo Could not find the file "fcgi_config_x86.h".  Aborting.
+
+:DONE
diff --git a/build_no_shell.bat b/build_no_shell.bat
new file mode 100755 (executable)
index 0000000..c741285
--- /dev/null
@@ -0,0 +1,56 @@
+rem
+rem This build script is intended to be run from the root dir that
+rem the FastCGI kit was unpacked in.  It should work on an X86
+rem machine but it's only been tested in limited form.
+rem
+rem $Id: build_no_shell.bat,v 1.1 1997/09/16 15:36:24 stanleyg Exp $
+rem
+rem
+
+echo off
+if not exist "include\fcgi_config_x86.h" goto NO_CONFIG
+
+copy include\fcgi_config_x86.h include\fcgi_config.h
+
+rem
+rem Build the FastCGI DLL and import library.
+rem
+cd libfcgi
+nmake -f libfcgi.mak
+%1%
+
+rem
+rem Build the cgi-fcgi.exe "shim" application.
+rem
+cd ..\cgi-fcgi
+nmake -f cgi-fcgi.mak
+%1%
+
+rem
+rem Now build the sample applications that have been qualified.
+rem
+cd ..\examples
+nmake -f echo.mak
+nmake -f echo2.mak
+nmake -f tiny-fcgi.mak
+nmake -f tiny-fcgi2.mak
+
+cd ..
+
+rem
+rem Now copy all binaries (including the libfcgi.dll) to a common 
+rem directory to make testing and accessing the apps easier.
+rem
+if not exist "FcgiBin" mkdir FcgiBin
+copy libfcgi\Debug\libfcgi.dll FcgiBin
+copy examples\*.fcgi FcgiBin
+copy examples\Debug\*.exe FcgiBin
+copy cgi-fcgi\Debug\cgi-fcgi.exe FcgiBin
+
+goto :DONE
+
+:NO_CONFIG
+echo Could not find the file "fcgi_config_x86.h".  Aborting.
+
+:DONE
+
diff --git a/cgi-fcgi/Makefile.in b/cgi-fcgi/Makefile.in
new file mode 100644 (file)
index 0000000..64fc401
--- /dev/null
@@ -0,0 +1,36 @@
+#
+#  Makefile for CGI -> FCGI application
+#
+#  Open Market, Inc.
+#
+#  $Id: Makefile.in,v 1.1 1997/09/16 15:36:25 stanleyg Exp $
+#
+
+SHELL = @SHELL@
+O =     @O@
+L =     @L@
+CC     = @CC@
+
+INCLUDEDIR  = ../include
+CFLAGS = @CCDEFS@ @PROFILE@ -I$(INCLUDEDIR)
+LIBS   = @LIBS@
+RANLIB = @RANLIB@
+
+OBJS       = fcgiapp.${O}
+INCLUDES    = $(INCLUDEDIR)/fastcgi.h $(INCLUDEDIR)/fcgiapp.h \
+             $(INCLUDEDIR)/fcgimisc.h $(INCLUDEDIR)/fcgiappmisc.h
+LIBDIR      = ../libfcgi
+LIBFCGI            = $(LIBDIR)/libfcgi.${L}
+TARGETS            = cgi-fcgi
+
+all: $(TARGETS)
+
+cgi-fcgi: cgi-fcgi.${O} $(LIBFCGI)
+       $(CC) $(CFLAGS) cgi-fcgi.${O} -o cgi-fcgi $(LIBFCGI) $(LIBS)
+
+clean:
+       rm -f *.${L} *.${O} core.* errs *~ \#* TAGS *.E a.out $(TARGETS)
+
+# ----------------------------------------------------------------------------
+
+cgi-fcgi.${O}: cgi-fcgi.c $(INCLUDES)
diff --git a/cgi-fcgi/cgi-fcgi.c b/cgi-fcgi/cgi-fcgi.c
new file mode 100644 (file)
index 0000000..32dfd83
--- /dev/null
@@ -0,0 +1,809 @@
+/* 
+ * cgifcgi.c --
+ *
+ *     CGI to FastCGI bridge
+ *
+ *
+ * Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] = "$Id: cgi-fcgi.c,v 1.1 1997/09/16 15:36:25 stanleyg Exp $";
+#endif /* not lint */
+
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#include "fcgimisc.h"
+#include "fcgiapp.h"
+#include "fcgiappmisc.h"
+#include "fastcgi.h"
+#include "fcgi_config.h"
+#include "fcgios.h"
+
+static int wsReadPending = 0;
+static int wsWritePending = 0;
+static int fcgiReadPending = 0;
+static int fcgiWritePending = 0;
+
+static void ScheduleIo(void);
+
+\f
+/*
+ * Simple buffer (not ring buffer) type, used by all event handlers.
+ */
+#define BUFFLEN 8192
+typedef struct {
+    char *next;
+    char *stop;
+    char buff[BUFFLEN];
+} Buffer;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetPtr --
+ *
+ *      Returns a count of the number of characters available
+ *      in the buffer (at most n) and advances past these
+ *      characters.  Stores a pointer to the first of these
+ *      characters in *ptr.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int GetPtr(char **ptr, int n, Buffer *pBuf)
+{
+    int result;
+    *ptr = pBuf->next;
+    result = min(n, pBuf->stop - pBuf->next);
+    pBuf->next += result;
+    return result;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * MakeHeader --
+ *
+ *      Constructs an FCGI_Header struct.
+ *
+ *----------------------------------------------------------------------
+ */
+static FCGI_Header MakeHeader(
+        int type,
+        int requestId,
+        int contentLength,
+        int paddingLength)
+{
+    FCGI_Header header;
+    ASSERT(contentLength >= 0 && contentLength <= FCGI_MAX_LENGTH);
+    ASSERT(paddingLength >= 0 && paddingLength <= 0xff);
+    header.version = FCGI_VERSION_1;
+    header.type             =  type;
+    header.requestIdB1      = (requestId      >> 8) & 0xff;
+    header.requestIdB0      = (requestId          ) & 0xff;
+    header.contentLengthB1  = (contentLength  >> 8) & 0xff;
+    header.contentLengthB0  = (contentLength      ) & 0xff;
+    header.paddingLength    =  paddingLength;
+    header.reserved         =  0;
+    return header;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * MakeBeginRequestBody --
+ *
+ *      Constructs an FCGI_BeginRequestBody record.
+ *
+ *----------------------------------------------------------------------
+ */
+static FCGI_BeginRequestBody MakeBeginRequestBody(
+        int role,
+        int keepConnection)
+{
+    FCGI_BeginRequestBody body;
+    ASSERT((role >> 16) == 0);
+    body.roleB1 = (role >>  8) & 0xff;
+    body.roleB0 = (role      ) & 0xff;
+    body.flags = (keepConnection) ? FCGI_KEEP_CONN : 0;
+    memset(body.reserved, 0, sizeof(body.reserved));
+    return body;
+}
+
+\f
+static int bytesToRead;    /* number of bytes to read from Web Server */
+static int appServerSock = -1;  /* Socket connected to FastCGI application,
+                                 * used by AppServerReadHandler and
+                                 * AppServerWriteHandler. */
+static Buffer fromAS;      /* Bytes read from the FCGI application server. */
+static FCGI_Header header; /* Header of the current record.  Is global
+                            * since read may return a partial header. */
+static int headerLen = 0;  /* Number of valid bytes contained in header.
+                            * If headerLen < sizeof(header),
+                            * AppServerReadHandler is reading a record header;
+                            * otherwise it is reading bytes of record content
+                            * or padding. */
+static int contentLen;     /* If headerLen == sizeof(header), contentLen
+                            * is the number of content bytes still to be
+                            * read. */
+static int paddingLen;     /* If headerLen == sizeof(header), paddingLen
+                            * is the number of padding bytes still
+                            * to be read. */
+static int requestId;      /* RequestId of the current request.
+                            * Set by main. */
+static FCGI_EndRequestBody erBody;
+static int readingEndRequestBody = FALSE;
+                           /* If readingEndRequestBody, erBody contains
+                            * partial content: contentLen more bytes need
+                            * to be read. */
+static int exitStatus = 0;
+static int exitStatusSet = FALSE;
+
+static int stdinFds[3];
+
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGIexit --
+ *
+ *      FCGIexit provides a single point of exit.  It's main use is for
+ *      application debug when porting to other operating systems.
+ *
+ *----------------------------------------------------------------------
+ */
+static void FCGIexit(int exitCode)
+{
+    if(appServerSock != -1) {
+        OS_Close(appServerSock);
+       appServerSock = -1;
+    }
+    OS_LibShutdown();
+    exit(exitCode);
+}
+
+#undef exit
+#define exit FCGIexit
+
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * AppServerReadHandler --
+ *
+ *      Reads data from the FCGI application server and (blocking)
+ *      writes all of it to the Web server.  Exits the program upon
+ *      reading EOF from the FCGI application server.  Called only when
+ *      there's data ready to read from the application server.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void AppServerReadHandler(ClientData clientData, int bytesRead)
+{
+    int count, outFD;
+    char *ptr;
+
+    assert(fcgiReadPending == TRUE);
+    fcgiReadPending = FALSE;
+    count = bytesRead;
+
+    if(count <= 0) {
+        if(count < 0) {
+            exit(OS_Errno);
+        }
+        if(headerLen > 0 || paddingLen > 0) {
+            exit(FCGX_PROTOCOL_ERROR);
+        }
+       if(appServerSock != -1) {
+           OS_Close(appServerSock);
+           appServerSock = -1;
+       }
+        /*
+         * XXX: Shouldn't be here if exitStatusSet.
+         */
+        exit((exitStatusSet) ? exitStatus : FCGX_PROTOCOL_ERROR);
+    }
+    fromAS.stop = fromAS.next + count;
+    while(fromAS.next != fromAS.stop) {
+        /*
+         * fromAS is not empty.  What to do with the contents?
+         */
+        if(headerLen < sizeof(header)) {
+            /*
+             * First priority is to complete the header.
+             */
+            count = GetPtr(&ptr, sizeof(header) - headerLen, &fromAS);
+            assert(count > 0);
+            memcpy(&header + headerLen, ptr, count);
+            headerLen += count;
+            if(headerLen < sizeof(header)) {
+                break;
+            }
+            if(header.version != FCGI_VERSION_1) {
+                exit(FCGX_UNSUPPORTED_VERSION);
+           }
+            if((header.requestIdB1 << 8) + header.requestIdB0 != requestId) {
+                exit(FCGX_PROTOCOL_ERROR);
+           }
+            contentLen = (header.contentLengthB1 << 8)
+                         + header.contentLengthB0;
+            paddingLen =  header.paddingLength;
+       } else {
+            /*
+             * Header is complete (possibly from previous call).  What now?
+             */
+            switch(header.type) {
+               case FCGI_STDOUT:
+                case FCGI_STDERR:
+                    /*
+                     * Write the buffered content to stdout or stderr.
+                     * Blocking writes are OK here; can't prevent a slow
+                     * client from tying up the app server without buffering
+                     * output in temporary files.
+                     */
+                    count = GetPtr(&ptr, contentLen, &fromAS);
+                    contentLen -= count;
+                    if(count > 0) {
+                        outFD = (header.type == FCGI_STDOUT) ?
+                                    STDOUT_FILENO : STDERR_FILENO;
+                        if(OS_Write(outFD, ptr, count) < 0) {
+                            exit(OS_Errno);
+                        }
+                   }
+                    break;
+                case FCGI_END_REQUEST:
+                    if(!readingEndRequestBody) {
+                        if(contentLen != sizeof(erBody)) {
+                            exit(FCGX_PROTOCOL_ERROR);
+                       }
+                        readingEndRequestBody = TRUE;
+                   }
+                    count = GetPtr(&ptr, contentLen, &fromAS);
+                    if(count > 0) {
+                        memcpy(&erBody + sizeof(erBody) - contentLen,
+                                ptr, count);
+                        contentLen -= count;
+                   }
+                    if(contentLen == 0) {
+                        if(erBody.protocolStatus != FCGI_REQUEST_COMPLETE) {
+                            /*
+                             * XXX: What to do with FCGI_OVERLOADED?
+                             */
+                            exit(FCGX_PROTOCOL_ERROR);
+                       }
+                        exitStatus = (erBody.appStatusB3 << 24)
+                                   + (erBody.appStatusB2 << 16)
+                                   + (erBody.appStatusB1 <<  8)
+                                   + (erBody.appStatusB0      );
+                        exitStatusSet = TRUE;
+                        readingEndRequestBody = FALSE;
+                   }
+                    break;
+                case FCGI_GET_VALUES_RESULT:
+                    /* coming soon */
+                case FCGI_UNKNOWN_TYPE:
+                    /* coming soon */
+                default:
+                    exit(FCGX_PROTOCOL_ERROR);
+           }
+            if(contentLen == 0) {
+                if(paddingLen > 0) {
+                    paddingLen -= GetPtr(&ptr, paddingLen, &fromAS);
+               }
+                /*
+                 * If we've processed all the data and skipped all the
+                 * padding, discard the header and look for the next one.
+                 */
+                if(paddingLen == 0) {
+                    headerLen = 0;
+               }
+           }
+        } /* headerLen >= sizeof(header) */
+    } /*while*/
+    ScheduleIo();
+}
+\f
+static Buffer fromWS;   /* Buffer for data read from Web server
+                         * and written to FastCGI application. Used
+                         * by WebServerReadHandler and
+                         * AppServerWriteHandler. */
+static int webServerReadHandlerEOF;
+                        /* TRUE iff WebServerReadHandler has read EOF from
+                         * the Web server. Used in main to prevent
+                         * rescheduling WebServerReadHandler. */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * WebServerReadHandler --
+ *
+ *      Non-blocking reads data from the Web server into the fromWS
+ *      buffer.  Called only when fromWS is empty, no EOF has been
+ *      received from the Web server, and there's data available to read.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void WebServerReadHandler(ClientData clientData, int bytesRead)
+{
+    assert(fromWS.next == fromWS.stop);
+    assert(fromWS.next == &fromWS.buff[0]);
+    assert(wsReadPending == TRUE);
+    wsReadPending = FALSE;
+
+    if(bytesRead < 0) {
+        exit(OS_Errno);
+    }
+    *((FCGI_Header *) &fromWS.buff[0])
+            = MakeHeader(FCGI_STDIN, requestId, bytesRead, 0);
+    bytesToRead -= bytesRead;
+    fromWS.stop = &fromWS.buff[sizeof(FCGI_Header) + bytesRead];
+    webServerReadHandlerEOF = (bytesRead == 0);
+    ScheduleIo();
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * AppServerWriteHandler --
+ *
+ *      Non-blocking writes data from the fromWS buffer to the FCGI
+ *      application server.  Called only when fromWS is non-empty
+ *      and the socket is ready to accept some data.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void AppServerWriteHandler(ClientData clientData, int bytesWritten)
+{
+    int length = fromWS.stop - fromWS.next;
+
+    assert(length > 0);
+    assert(fcgiWritePending == TRUE);
+
+    fcgiWritePending = FALSE;
+    if(bytesWritten < 0) {
+        exit(OS_Errno);
+    }
+    if((int)bytesWritten < length) {
+        fromWS.next += bytesWritten;
+    } else {
+        fromWS.stop = fromWS.next = &fromWS.buff[0];
+    }
+
+    ScheduleIo();
+}      
+
+\f
+/*
+ * ScheduleIo --
+ *
+ *      This functions is responsible for scheduling all I/O to move
+ *      data between a web server and a FastCGI application.
+ *
+ * Results:
+ *      None.
+ *
+ * Side effects:
+ *      This routine will signal the ioEvent upon completion.
+ * 
+ */
+static void ScheduleIo(void)
+{
+    int length;
+
+    /*
+     * Move data between standard in and the FastCGI connection.
+     */
+    if(!fcgiWritePending && appServerSock != -1 &&
+       ((length = fromWS.stop - fromWS.next) != 0)) {
+       if(OS_AsyncWrite(appServerSock, 0, fromWS.next, length,
+                        AppServerWriteHandler,
+                        (ClientData)appServerSock) == -1) {
+           FCGIexit(OS_Errno);
+       } else {
+           fcgiWritePending = TRUE;
+       }
+    }
+
+    /*
+     * Schedule a read from the FastCGI application if there's not
+     * one pending and there's room in the buffer.
+     */
+    if(!fcgiReadPending && appServerSock != -1) {
+       fromAS.next = &fromAS.buff[0];
+
+       if(OS_AsyncRead(appServerSock, 0, fromAS.next, BUFFLEN, 
+                       AppServerReadHandler,
+                       (ClientData)appServerSock) == -1) {
+           FCGIexit(OS_Errno);
+       } else {
+           fcgiReadPending = TRUE;
+       }
+    }
+
+    /*
+     * Schedule a read from standard in if necessary.
+     */
+    if((bytesToRead > 0) && !webServerReadHandlerEOF && !wsReadPending &&
+       !fcgiWritePending &&
+       fromWS.next == &fromWS.buff[0]) {
+       if(OS_AsyncReadStdin(fromWS.next + sizeof(FCGI_Header),
+                            BUFFLEN - sizeof(FCGI_Header), 
+                            WebServerReadHandler, STDIN_FILENO)== -1) {
+           FCGIexit(OS_Errno);
+       } else {
+           wsReadPending = TRUE;
+       }
+    }
+}
+
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGI_Start --
+ *
+ *      Starts nServers copies of FCGI application appPath, all
+ *      listening to a Unix Domain socket at bindPath.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void FCGI_Start(char *bindPath, char *appPath, int nServers)
+{
+    int listenFd, i;
+    int            tcp = FALSE;
+
+    if((listenFd = OS_CreateLocalIpcFd(bindPath)) == -1) {
+        exit(OS_Errno);
+    }
+    
+    if(access(appPath, X_OK) == -1) {
+       fprintf(stderr, "%s is not executable\n", appPath);
+       exit(1);
+    }
+
+    /*
+     * Create the server processes
+     */
+    for(i = 0; i < nServers; i++) {
+        if(OS_SpawnChild(appPath, listenFd) == -1) {
+            exit(OS_Errno);
+       }
+    }
+    OS_Close(listenFd);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGIUtil_BuildNameValueHeader --
+ *
+ *      Builds a name-value pair header from the name length
+ *      and the value length.  Stores the header into *headerBuffPtr,
+ *      and stores the length of the header into *headerLenPtr.
+ *
+ * Side effects:
+ *      Stores header's length (at most 8) into *headerLenPtr,
+ *      and stores the header itself into
+ *      headerBuffPtr[0 .. *headerLenPtr - 1].
+ *
+ *----------------------------------------------------------------------
+ */
+static buildNameValueHeaderCalls = 0; /* XXX: for testing */
+
+static void FCGIUtil_BuildNameValueHeader(
+        int nameLen,
+        int valueLen,
+        unsigned char *headerBuffPtr,
+        int *headerLenPtr) {
+    unsigned char *startHeaderBuffPtr = headerBuffPtr;
+
+    ASSERT(nameLen >= 0);
+    if(nameLen < 0x80 && (buildNameValueHeaderCalls & 1) == 0) {
+        *headerBuffPtr++ = nameLen;
+    } else {
+        *headerBuffPtr++ = (nameLen >> 24) | 0x80;
+        *headerBuffPtr++ = (nameLen >> 16);
+        *headerBuffPtr++ = (nameLen >> 8);
+        *headerBuffPtr++ = nameLen;
+    }
+    ASSERT(valueLen >= 0);
+    if(valueLen < 0x80 && (buildNameValueHeaderCalls & 2) == 0) {
+        *headerBuffPtr++ = valueLen;
+    } else {
+        *headerBuffPtr++ = (valueLen >> 24) | 0x80;
+        *headerBuffPtr++ = (valueLen >> 16);
+        *headerBuffPtr++ = (valueLen >> 8);
+        *headerBuffPtr++ = valueLen;
+    }
+    *headerLenPtr = headerBuffPtr - startHeaderBuffPtr;
+    buildNameValueHeaderCalls++;
+}
+\f
+
+#define MAXARGS        16
+static int ParseArgs(int argc, char *argv[],
+        int *doBindPtr, int *doStartPtr,
+        char *connectPathPtr, char *appPathPtr, int *nServersPtr) {
+    int            i,
+           x,
+           err = 0,
+           ac;
+    char    *tp1,
+           *tp2,
+           *av[MAXARGS];
+    FILE    *fp;
+    char    line[BUFSIZ];
+
+    *doBindPtr = TRUE;
+    *doStartPtr = TRUE;
+    *connectPathPtr = '\0';
+    *appPathPtr = '\0';
+    *nServersPtr = 0;
+
+    for(i = 0; i < MAXARGS; i++)
+        av[i] = NULL;
+    for(i = 1; i < argc; i++) {
+        if(argv[i][0] == '-') {
+            if(!strcmp(argv[i], "-f")) {
+               if(++i == argc) {
+                   fprintf(stderr,
+                            "Missing command file name after -f\n");
+                   return 1;
+               }
+               if((fp = fopen(argv[i], "r")) == NULL) {
+                   fprintf(stderr, "Cannot open command file %s\n", argv[i]);
+                   return 1;
+               }
+               ac = 1;
+               while(fgets(line, BUFSIZ, fp)) {
+                   if(line[0] == '#') {
+                       continue;
+                   }
+                   if((tp1 = (char *) strrchr(line,'\n')) != NULL) {
+                       *tp1-- = 0;
+                       while(*tp1 == ' ' || *tp1 =='\t') {
+                           *tp1-- = 0;
+                       }
+                   } else {
+                       fprintf(stderr, "Line to long\n");
+                       return 1;
+                   }
+                   tp1 = line;
+                   while(tp1) {
+                       if((tp2 = strchr(tp1, ' ')) != NULL) {
+                           *tp2++ =  0;
+                       }
+                       if(ac >= MAXARGS) {
+                           fprintf(stderr,
+                                    "To many arguments, "
+                                    "%d is max from a file\n", MAXARGS);
+                               exit(-1);
+                       }
+                       if((av[ac] = malloc(strlen(tp1)+1)) == NULL) {
+                           fprintf(stderr, "Cannot allocate %d bytes\n",
+                                   strlen(tp1)+1);
+                           exit(-1);
+                       }
+                       strcpy(av[ac++], tp1);
+                       tp1 = tp2;
+                   }
+               }
+               err = ParseArgs(ac, av, doBindPtr, doStartPtr,
+                        connectPathPtr, appPathPtr, nServersPtr);
+               for(x = 1; x < ac; x++) {
+                   ASSERT(av[x] != NULL);
+                   free(av[x]);
+               }
+               return err;
+#ifdef _WIN32
+           } else if (!strcmp(argv[i], "-jitcgi")) {
+               DebugBreak();
+           } else if (!strcmp(argv[i], "-dbgfcgi")) {
+               putenv("DEBUG_FCGI=TRUE");
+#endif
+           } else if(!strcmp(argv[i], "-start")) {
+               *doBindPtr = FALSE;
+           } else if(!strcmp(argv[i], "-bind")) {
+               *doStartPtr = FALSE;
+           } else if(!strcmp(argv[i], "-connect")) {
+                if(++i == argc) {
+                   fprintf(stderr,
+                            "Missing connection name after -connect\n");
+                    err++;
+                } else {
+                    strcpy(connectPathPtr, argv[i]);
+                }
+           } else {
+               fprintf(stderr, "Unknown option %s\n", argv[i]);
+               err++;
+           }
+       } else if(*appPathPtr == '\0') {
+            strcpy(appPathPtr, argv[i]);
+        } else if(isdigit(argv[i][0]) && *nServersPtr == 0) {
+            *nServersPtr = atoi(argv[i]);
+            if(*nServersPtr <= 0) {
+                fprintf(stderr, "Number of servers must be greater than 0\n");
+                err++;
+            }
+        } else {
+            fprintf(stderr, "Unknown argument %s\n", argv[i]);
+            err++;
+        }
+    }
+    if(*doStartPtr && *appPathPtr == 0) {
+        fprintf(stderr, "Missing application pathname\n");
+        err++;
+    }
+    if(*connectPathPtr == 0) {
+       fprintf(stderr, "Missing -connect <connName>\n");
+       err++;
+    } else if(strchr(connectPathPtr, ':')) {
+/*
+ * XXX: Test to see if we can use IP connect locally...
+        This hack lets me test the ability to create a local process listening
+       to a TCP/IP port for connections and subsequently connect to the app
+       like we do for Unix domain and named pipes.
+       
+        if(*doStartPtr && *doBindPtr) {
+           fprintf(stderr,
+                    "<connName> of form hostName:portNumber "
+                    "requires -start or -bind\n");
+           err++;
+        }
+ */
+    }
+    if(*nServersPtr == 0) {
+        *nServersPtr = 1;
+    }
+    return err;
+}
+\f
+void main(int argc, char **argv, char **envp)
+{
+    int count;
+    FCGX_Stream *paramsStream;
+    int numFDs;
+    unsigned char headerBuff[8];
+    int headerLen, valueLen;
+    char *equalPtr;
+    FCGI_BeginRequestRecord beginRecord;
+    int        doBind, doStart, nServers;
+    char appPath[MAXPATHLEN], bindPath[MAXPATHLEN];
+
+    if(ParseArgs(argc, argv, &doBind, &doStart,
+                  (char *) &bindPath, (char *) &appPath, &nServers)) {
+       fprintf(stderr,
+"Usage:\n"
+"    cgi-fcgi -f <cmdPath> , or\n"
+"    cgi-fcgi -connect <connName> <appPath> [<nServers>] , or\n"
+"    cgi-fcgi -start -connect <connName> <appPath> [<nServers>] , or\n"
+"    cgi-fcgi -bind -connect <connName> ,\n"
+"where <connName> is either the pathname of a UNIX domain socket\n"
+"or (if -bind is given) a hostName:portNumber specification\n"
+"or (if -start is given) a :portNumber specification (uses local host).\n");
+       exit(1);
+    }
+
+    if(OS_LibInit(stdinFds)) {
+        fprintf(stderr, "Error initializing OS library: %d\n", OS_Errno);
+       exit(0);
+    }
+
+    equalPtr = getenv("CONTENT_LENGTH");
+    if(equalPtr != NULL) {
+        bytesToRead = atoi(equalPtr);
+    } else {
+        bytesToRead = 0;
+    }
+    
+    if(doBind) {
+        appServerSock = OS_FcgiConnect(bindPath);
+    }
+    if(doStart && (!doBind || appServerSock < 0)) {
+        FCGI_Start(bindPath, appPath, nServers);
+        if(!doBind) {
+            exit(0);
+        } else {
+            appServerSock = OS_FcgiConnect(bindPath);
+       }
+    }
+    if(appServerSock < 0) {
+        fprintf(stderr, "Could not connect to %s\n", bindPath);
+        exit(OS_Errno);
+    }
+    /*
+     * Set an arbitrary non-null FCGI RequestId
+     */
+    requestId = 1;
+    /*
+     * XXX: Send FCGI_GET_VALUES
+     */
+
+    /*
+     * XXX: Receive FCGI_GET_VALUES_RESULT
+     */
+
+    /*
+     * Send FCGI_BEGIN_REQUEST (XXX: hack, separate write)
+     */
+    beginRecord.header = MakeHeader(FCGI_BEGIN_REQUEST, requestId,
+            sizeof(beginRecord.body), 0);
+    beginRecord.body = MakeBeginRequestBody(FCGI_RESPONDER, FALSE);
+    count = OS_Write(appServerSock, (char *)&beginRecord, sizeof(beginRecord));
+    if(count != sizeof(beginRecord)) {
+        exit(OS_Errno);
+    }
+    /*
+     * Send environment to the FCGI application server
+     */
+    paramsStream = CreateWriter(appServerSock, requestId, 8192, FCGI_PARAMS);
+    for( ; *envp != NULL; envp++) {
+        equalPtr = strchr(*envp, '=');
+        if(equalPtr  == NULL) {
+            exit(1000);
+        }
+        valueLen = strlen(equalPtr + 1);
+        FCGIUtil_BuildNameValueHeader(
+                equalPtr - *envp,
+                valueLen,
+                &headerBuff[0],
+                &headerLen);
+        if(FCGX_PutStr((char *) &headerBuff[0], headerLen, paramsStream) < 0
+                || FCGX_PutStr(*envp, equalPtr - *envp, paramsStream) < 0
+                || FCGX_PutStr(equalPtr + 1, valueLen, paramsStream) < 0) {
+            exit(FCGX_GetError(paramsStream));
+        }
+    }
+    FCGX_FClose(paramsStream);
+    FreeStream(&paramsStream);
+    /*
+     * Perform the event loop until AppServerReadHander sees FCGI_END_REQUEST
+     */
+    fromWS.stop = fromWS.next = &fromWS.buff[0];
+    webServerReadHandlerEOF = FALSE;
+    /*
+     * XXX: might want to use numFDs in the os library.
+     */
+    numFDs = max(appServerSock, STDIN_FILENO) + 1;
+    OS_SetFlags(appServerSock, O_NONBLOCK);
+
+    ScheduleIo();
+    while(!exitStatusSet) {
+        /*
+        * NULL = wait forever (or at least until there's something
+        *        to do.
+        */
+        OS_DoIo(NULL);
+    }
+    if(exitStatusSet) {
+        FCGIexit(exitStatus);
+    } else {
+        FCGIexit(999);
+    }
+}
diff --git a/cgi-fcgi/cgi-fcgi.mak b/cgi-fcgi/cgi-fcgi.mak
new file mode 100644 (file)
index 0000000..526d556
--- /dev/null
@@ -0,0 +1,222 @@
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+!IF "$(CFG)" == ""
+CFG=cgifcgi - Win32 Debug
+!MESSAGE No configuration specified.  Defaulting to cgifcgi - Win32 Debug.
+!ENDIF 
+
+!IF "$(CFG)" != "cgifcgi - Win32 Release" && "$(CFG)" !=\
+ "cgifcgi - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line.  For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "cgi-fcgi.mak" CFG="cgifcgi - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "cgifcgi - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "cgifcgi - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+!ERROR An invalid configuration is specified.
+!ENDIF 
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE 
+NULL=nul
+!ENDIF 
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "cgifcgi - Win32 Debug"
+RSC=rc.exe
+CPP=cl.exe
+
+!IF  "$(CFG)" == "cgifcgi - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : "$(OUTDIR)\cgi-fcgi.exe"
+
+CLEAN : 
+       -@erase "$(INTDIR)\cgi-fcgi.obj"
+       -@erase "$(OUTDIR)\cgi-fcgi.exe"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MD /W3 /GX /O2 /I "..\include" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/cgi-fcgi.pch" /YX /Fo"$(INTDIR)/" /c 
+CPP_OBJS=.\Release/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/cgi-fcgi.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ..\libfcgi\ /nologo /subsystem:console /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib ..\libfcgi\ /nologo /subsystem:console /incremental:no\
+ /pdb:"$(OUTDIR)/cgi-fcgi.pdb" /machine:I386 /out:"$(OUTDIR)/cgi-fcgi.exe" 
+LINK32_OBJS= \
+       "$(INTDIR)\cgi-fcgi.obj"
+
+"$(OUTDIR)\cgi-fcgi.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF  "$(CFG)" == "cgifcgi - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : "$(OUTDIR)\cgi-fcgi.exe"
+
+CLEAN : 
+       -@erase "$(INTDIR)\cgi-fcgi.obj"
+       -@erase "$(INTDIR)\vc40.idb"
+       -@erase "$(INTDIR)\vc40.pdb"
+       -@erase "$(OUTDIR)\cgi-fcgi.exe"
+       -@erase "$(OUTDIR)\cgi-fcgi.ilk"
+       -@erase "$(OUTDIR)\cgi-fcgi.pdb"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MD /W3 /Gm /GX /Zi /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MD /W3 /Gm /GX /Zi /Od /I "..\include" /D "WIN32" /D "_DEBUG"\
+ /D "_CONSOLE" /Fp"$(INTDIR)/cgi-fcgi.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/"\
+ /c 
+CPP_OBJS=.\Debug/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/cgi-fcgi.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ..\libfcgi\Debug\libfcgi.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib ..\libfcgi\Debug\libfcgi.lib /nologo /subsystem:console\
+ /incremental:yes /pdb:"$(OUTDIR)/cgi-fcgi.pdb" /debug /machine:I386\
+ /out:"$(OUTDIR)/cgi-fcgi.exe" 
+LINK32_OBJS= \
+       "$(INTDIR)\cgi-fcgi.obj"
+
+"$(OUTDIR)\cgi-fcgi.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF 
+
+.c{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.c{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+################################################################################
+# Begin Target
+
+# Name "cgifcgi - Win32 Release"
+# Name "cgifcgi - Win32 Debug"
+
+!IF  "$(CFG)" == "cgifcgi - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "cgifcgi - Win32 Debug"
+
+!ENDIF 
+
+################################################################################
+# Begin Source File
+
+SOURCE=".\cgi-fcgi.c"
+
+!IF  "$(CFG)" == "cgifcgi - Win32 Release"
+
+DEP_CPP_CGI_F=\
+       "..\include\fastcgi.h"\
+       "..\include\fcgi_config.h"\
+       "..\include\fcgiapp.h"\
+       "..\include\fcgiappmisc.h"\
+       "..\include\fcgimisc.h"\
+       "..\include\fcgios.h"\
+       {$(INCLUDE)}"\sys\TYPES.H"\
+       
+
+"$(INTDIR)\cgi-fcgi.obj" : $(SOURCE) $(DEP_CPP_CGI_F) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "cgifcgi - Win32 Debug"
+
+DEP_CPP_CGI_F=\
+       "..\include\fastcgi.h"\
+       "..\include\fcgi_config.h"\
+       "..\include\fcgiapp.h"\
+       "..\include\fcgiappmisc.h"\
+       "..\include\fcgimisc.h"\
+       "..\include\fcgios.h"\
+       {$(INCLUDE)}"\sys\TYPES.H"\
+       
+
+"$(INTDIR)\cgi-fcgi.obj" : $(SOURCE) $(DEP_CPP_CGI_F) "$(INTDIR)"
+
+
+!ENDIF 
+
+# End Source File
+# End Target
+# End Project
+################################################################################
diff --git a/cgi-fcgi/cgi-fcgi.mak.in b/cgi-fcgi/cgi-fcgi.mak.in
new file mode 100644 (file)
index 0000000..fe2a93a
--- /dev/null
@@ -0,0 +1,293 @@
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+# Another service of Ace Wrecking and Software
+#
+# xmb Generated NMAKE File for NT and VC++ 
+
+!IF "$(CFG)" == ""
+CFG=cgi-fcgi - Win32 Debug
+!MESSAGE No configuration specified.  Defaulting to cgi-fcgi - Win32 Debug.
+!ENDIF 
+
+!IF "$(CFG)" != "cgi-fcgi - Win32 Release" && "$(CFG)" != "cgi-fcgi - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line.  For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "cgi-fcgi.mak" CFG="cgi-fcgi - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "cgi-fcgi - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "cgi-fcgi - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+!ERROR An invalid configuration is specified.
+!ENDIF 
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE 
+NULL=nul
+!ENDIF 
+#
+#
+#  Makefile for @SUBSYSTEM@ version @VERSION@(@PLATFORM@)
+#  Auto-generated, do not edit this Makefile
+#
+
+#
+# Default top-level directories in which to install architecture-
+# specific files (exec_prefix) and machine-independent files such
+# as scripts (prefix).  The values specified here may be overridden
+# at configure-time with the --exec-prefix and --prefix options
+# to the "configure" script.
+
+#-----------------------------------------------------------------------------
+# Normally do not edit this section. It is setup by configure
+#
+# the shell MUST BE /bin/sh
+#
+SHELL  = @SHELL@
+PLATFORM_CLASS = @PLATFORM_CLASS@
+O = @O@
+L = @L@
+X = @X@
+#
+exec_prefix =   @exec_prefix@
+prefix =        @prefix@
+common_prefix = @common_prefix@
+CVS_TAG =       @CVS_TAG@
+SRC_DIR =      @srcdir@
+BIN_DIR =       $(exec_prefix)\bin
+LIB_DIR =       $(exec_prefix)\lib
+ETC_DIR =       $(exec_prefix)\etc
+BINCLUDE_DIR = $(exec_prefix)\include
+INCLUDE_DIR =  $(common_prefix)\include
+CBIN_DIR =      $(common_prefix)\bin
+CLIB_DIR =      $(common_prefix)\lib
+CETC_DIR =      $(common_prefix)\etc
+CONTRIB_DIR =   $(common_prefix)\contrib
+MAN_DIR =       $(common_prefix)\man
+MAN1_DIR =      $(MAN_DIR)\man1
+MAN2_DIR =      $(MAN_DIR)\man2
+MAN3_DIR =      $(MAN_DIR)\man3
+MAN5_DIR =      $(MAN_DIR)\man5
+MAN8_DIR =      $(MAN_DIR)\man8
+INFO_DIR =      $(common_prefix)\info
+INSTALL                = @INSTALL@
+INSTALL_PROGRAM        = @INSTALL@
+INSTALL_DATA   = @INSTALL_DATA@
+CC              = @CC@ @CCDEFS@
+CFLAGS          = @CFLAGS@ @INCLUDE_PATH@ -I. @DEFS@
+RANLIB         = @RANLIB@
+AR             = @AR@
+GENMSGC         = @GENMSGC@
+GENMSGH         = @GENMSGH@
+#
+#---------------------------------------------------------------------------
+#
+#
+# All OMI makefiles will have the following make targets:
+#
+#      all - first rule, builds everything
+#      export - installs everything
+#      test - runs unit tests. This can be a null rule.
+#      clean - cleans up after a make
+#      realclean - cleans up after a configure and make
+#
+
+
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "cgi-fcgi - Win32 Debug"
+CPP=cl.exe
+MTL=mktyplib.exe
+RSC=rc.exe
+MC=mc.exe
+
+!IF  "$(CFG)" == "cgi-fcgi - Win32 Release"
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+
+ALL : "$(OUTDIR)\cgi-fcgi.exe"
+
+
+CLEAN : 
+       -@erase "$(INTDIR)\cgi-fcgi.obj"
+       -@erase "$(OUTDIR)\cgi-fcgi.exe"
+
+
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+
+EXPORT : 
+
+
+TEST : "$(OUTDIR)\cgi-fcgi.exe"
+
+
+
+
+# ADD BASE CPP /nologo /W3 /GX /O /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O  /I ../include /I "..\include"  /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MD /W3 /GX /O  /I ../include /I "..\include"  /D "WIN32" /D "NDEBUG" /D "_CONSOLE"\
+ /Fp"$(INTDIR)/cgi-fcgi.pch" /YX /Fo"$(INTDIR)/" /c 
+CPP_OBJS=.\Release/
+CPP_SBRS=.\.
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/cgi-fcgi.bsc" 
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console  /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ..\libfcgi\Release\libfcgi.lib  /nologo /subsystem:console /incremental:no /pdb:"$(OUTDIR)\cgi-fcgi.pdb"  /machine:I386 /out:"$(OUTDIR)\cgi-fcgi.exe"
+
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib ..\libfcgi\Release\libfcgi.lib /nologo /subsystem:console /incremental:no\
+ /pdb:"$(OUTDIR)/cgi-fcgi.pdb"  /machine:I386 /out:"$(OUTDIR)\cgi-fcgi.exe"
+
+LINK32_OBJS= \
+       "$(INTDIR)\cgi-fcgi.obj" 
+
+"$(OUTDIR)\cgi-fcgi.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+         $(LINK32) @<<
+         $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+
+
+!ELSEIF  "$(CFG)" == "cgi-fcgi - Win32 Debug"
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : "$(OUTDIR)\cgi-fcgi.exe"
+
+
+CLEAN : 
+       -@erase "$(INTDIR)\cgi-fcgi.obj"
+       -@erase "$(OUTDIR)\cgi-fcgi.exe"
+
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+
+EXPORT : 
+
+
+TEST : "$(OUTDIR)\cgi-fcgi.exe"
+
+
+
+# ADD BASE CPP /nologo /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /Z7 /Od  /I ../include /I "..\include"  /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+CPP_PROJ=/nologo /MD /W3 /GX /Z7 /Od  /I ../include /I "..\include"  /D "WIN32" /D "_DEBUG" /D "_WINDOWS"\
+ /Fp"$(INTDIR)/cgi-fcgi.pch" /YX /Fo"$(INTDIR)/" /c 
+CPP_OBJS=.\Debug/
+CPP_SBRS=.\.
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/cgi-fcgi.bsc" 
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ..\libfcgi\Debug\libfcgi.lib  /nologo /subsystem:console /incremental:yes /pdb:"$(OUTDIR)\cgi-fcgi.pdb" /debug /machine:I386 /out:"$(OUTDIR)\cgi-fcgi.exe"
+
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib ..\libfcgi\Debug\libfcgi.lib /nologo /subsystem:console /incremental:yes\
+ /pdb:"$(OUTDIR)/cgi-fcgi.pdb" /debug /machine:I386 /out:"$(OUTDIR)\cgi-fcgi.exe"
+
+LINK32_OBJS= \
+       "$(INTDIR)\cgi-fcgi.obj" 
+
+"$(OUTDIR)\cgi-fcgi.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+         $(LINK32) @<<
+         $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+
+
+!ENDIF 
+
+.c{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.c{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+################################################################################
+# Begin Target
+
+# Name "cgi-fcgi - Win32 Release"
+# Name "cgi-fcgi - Win32 Debug"
+
+!IF  "$(CFG)" == "cgi-fcgi - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "cgi-fcgi - Win32 Debug"
+
+!ENDIF 
+
+################################################################################
+# Begin Source File
+SOURCE=cgi-fcgi.c
+DEF_CPP_CGI_F=\
+       "..\include\fcgimisc.h"\
+       "..\include\fcgiapp.h"\
+       "..\include\fcgiappmisc.h"\
+       "..\include\fastcgi.h"\
+       "..\include\fcgi_config.h"\
+       "..\include\fcgios.h"
+
+
+"$(INTDIR)\cgi-fcgi.obj" : $(SOURCE) $(DEF_CPP_CGI_F) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+# End Source File
+
+# End Target
+# End Project
+################################################################################
+
diff --git a/cgi-fcgi/cgi-fcgi.mdp b/cgi-fcgi/cgi-fcgi.mdp
new file mode 100644 (file)
index 0000000..565d4b1
Binary files /dev/null and b/cgi-fcgi/cgi-fcgi.mdp differ
diff --git a/cgi-fcgi/descrip.dfc b/cgi-fcgi/descrip.dfc
new file mode 100644 (file)
index 0000000..f101f3d
--- /dev/null
@@ -0,0 +1,13 @@
+# descrip.dfc
+# This is a generated file, DO NOT EDIT
+# Another service of Ace Wrecking & Software
+# If we don't have it then you don't need it
+#
+
+IncludeDir ../include
+
+UseLocalLibrary libfcgi wnt ..\\libfcgi
+
+BuildConsoleApp cgi-fcgi cgi-fcgi.c
+
+
diff --git a/configure b/configure
new file mode 100755 (executable)
index 0000000..e386f6c
--- /dev/null
+++ b/configure
@@ -0,0 +1,3547 @@
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.9 
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Setup a symbol for the null device so that on NT a file can be used
+# instead
+#
+if test "`uname -s`" = "Windows_NT"; then
+  rm -f nulldevr
+  touch nulldevr
+  NULLDEVR=`pwd`/nulldevr
+  rm -f nulldevw
+  NULLDEVW=`pwd`/nulldevw
+  # See if a root device has been specified
+  #
+  if test -z "$ROOTDRIVE"; then
+    ROOTDRIVE="c"
+  fi
+  #
+  # Find the root of the MKS toolkit
+  #
+  if test -z "$MKSROOT"; then
+    if test -d $ROOTDRIVE:/mks/mksnt; then
+      MKSROOT=$ROOTDRIVE:/mks/mksnt
+    elif test -d $ROOTDRIVE:/mksnt; then
+      MKSROOT=$ROOTDRIVE:/mksnt
+    else
+      MKSROOT=$ROOTDRIVE:/mksnotfound
+    fi
+  fi
+else
+  NULLDEVR=/dev/null
+  NULLDEVW=/dev/null
+fi
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+ac_help="$ac_help
+  --with-global           Build an executable for global export(outside USA)"
+ac_help="$ac_help
+  --with-domestic         Build an executable for global export(outside USA)"
+ac_help="$ac_help
+  --with-purify           Build an executable with Purify
+                          (Pure Software's tool for finding memory
+                          errors)"
+ac_help="$ac_help
+  --with-purecov          Build an executable with PureCoverage
+                          (Pure Software's tool for doing test coverage
+                          analysis)"
+ac_help="$ac_help
+  --with-quantify         Build an executable with Quantify
+                          (Pure Software's tool for doing performance
+                          analysis)"
+ac_help="$ac_help
+  --with-profile          Build with profiling"
+ac_help="$ac_help
+  --with-nodebug          Build a production version with no debugging code"
+ac_help="$ac_help
+  --with-noassert         Build a production version with no ASSERTs"
+ac_help="$ac_help
+  --with-notest           Build a production version with no testing code"
+ac_help="$ac_help
+  --with-omireleases     Use OMI standard release paths"
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+if test "`uname -s`" = Windows_NT; then
+  bindir='${exec_prefix}\\bin'
+  sbindir='${exec_prefix}\\sbin'
+  libexecdir='${exec_prefix}\\libexec'
+  datadir='${prefix}\\share'
+  sysconfdir='${prefix}\\etc'
+  sharedstatedir='${prefix}\\com'
+  localstatedir='${prefix}\\var'
+  libdir='${exec_prefix}\\lib'
+  includedir='${prefix}\\include'
+  oldincludedir='\\msdev\\include'
+  infodir='${prefix}\\info'
+  mandir='${prefix}\\man'
+else
+  bindir='${exec_prefix}/bin'
+  sbindir='${exec_prefix}/sbin'
+  libexecdir='${exec_prefix}/libexec'
+  datadir='${prefix}/share'
+  sysconfdir='${prefix}/etc'
+  sharedstatedir='${prefix}/com'
+  localstatedir='${prefix}/var'
+  libdir='${exec_prefix}/lib'
+  includedir='${prefix}/include'
+  oldincludedir='/usr/include'
+  infodir='${prefix}/info'
+  mandir='${prefix}/man'
+fi
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+
+ac_prev=
+for ac_option
+do
+
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  case "$ac_option" in
+  -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+  *) ac_optarg= ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case "$ac_option" in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir="$ac_optarg" ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build="$ac_optarg" ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file="$ac_optarg" ;;
+
+  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+  | --da=*)
+    datadir="$ac_optarg" ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2;
+   if test -z "$IGNORE_ERRORS"; then
+     exit 1 ;
+   fi; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    eval "enable_${ac_feature}=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2;
+   if test -z "$IGNORE_ERRORS"; then
+     exit 1 ;
+   fi; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix="$ac_optarg" ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he)
+    # Omit some internal or obsolete options to make the list less imposing.
+    # This message is too long to be a string in the A/UX 3.1 sh.
+    cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+  --cache-file=FILE       cache test results in FILE
+  --help                  print this message
+  --no-create             do not create output files
+  --quiet, --silent       do not print \`checking...' messages
+  --version               print the version of autoconf that created configure
+Directory and file names:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [same as prefix]
+  --bindir=DIR            user executables in DIR [EPREFIX/bin]
+  --sbindir=DIR           system admin executables in DIR [EPREFIX/sbin]
+  --libexecdir=DIR        program executables in DIR [EPREFIX/libexec]
+  --datadir=DIR           read-only architecture-independent data in DIR
+                          [PREFIX/share]
+  --sysconfdir=DIR        read-only single-machine data in DIR [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data in DIR
+                          [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data in DIR [PREFIX/var]
+  --libdir=DIR            object code libraries in DIR [EPREFIX/lib]
+  --includedir=DIR        C header files in DIR [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc in DIR [/usr/include]
+  --infodir=DIR           info documentation in DIR [PREFIX/info]
+  --mandir=DIR            man documentation in DIR [PREFIX/man]
+  --srcdir=DIR            find the sources in DIR [configure dir or ..]
+  --program-prefix=PREFIX prepend PREFIX to installed program names
+  --program-suffix=SUFFIX append SUFFIX to installed program names
+  --program-transform-name=PROGRAM
+                          run sed PROGRAM on installed program names
+EOF
+    cat << EOF
+Host type:
+  --build=BUILD           configure for building on BUILD [BUILD=HOST]
+  --host=HOST             configure for HOST [guessed]
+  --target=TARGET         configure for TARGET [TARGET=HOST]
+Features and packages:
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --x-includes=DIR        X include files are in DIR
+  --x-libraries=DIR       X library files are in DIR
+EOF
+    if test -n "$ac_help"; then
+      echo "--enable and --with options recognized:$ac_help"
+    fi
+    exit 0 ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host="$ac_optarg" ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir="$ac_optarg" ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir="$ac_optarg" ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir="$ac_optarg" ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir="$ac_optarg" ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst \
+  | --locals | --local | --loca | --loc | --lo)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+  | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+    localstatedir="$ac_optarg" ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir="$ac_optarg" ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir="$ac_optarg" ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix="$ac_optarg" ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix="$ac_optarg" ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix="$ac_optarg" ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name="$ac_optarg" ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir="$ac_optarg" ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir="$ac_optarg" ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site="$ac_optarg" ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir="$ac_optarg" ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir="$ac_optarg" ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target="$ac_optarg" ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers)
+    echo "configure generated by autoconf version 2.9"
+    exit 0 ;;
+
+  -with-* | --with-*)
+    ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2;
+   if test -z "$IGNORE_ERRORS"; then
+     exit 1 ;
+   fi; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_${ac_package}='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2;
+   if test -z "$IGNORE_ERRORS"; then
+     exit 1 ;
+   fi; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    eval "with_${ac_package}=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes="$ac_optarg" ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries="$ac_optarg" ;;
+
+  -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2;
+   if test -z "$IGNORE_ERRORS"; then
+     exit 1 ;
+   fi; }
+    ;;
+
+  *)
+    if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+      echo "configure: warning: $ac_option: invalid host type" 1>&2
+    fi
+    if test "x$nonopt" != xNONE; then
+      { echo "configure: error: can only configure for one host and one target at a time" 1>&2;
+   if test -z "$IGNORE_ERRORS"; then
+     exit 1 ;
+   fi; }
+    fi
+    nonopt="$ac_option"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2;
+   if test -z "$IGNORE_ERRORS"; then
+     exit 1 ;
+   fi; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+  exec 6>$NULLDEVW
+else
+  exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+  case "$ac_arg" in
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c) ;;
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+  *" "*|*"     "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+  ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+  *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+  esac
+done
+
+# NLS nuisances.
+# Only set LANG and LC_ALL to C if already set.
+# These must not be set unconditionally because not all systems understand
+# e.g. LANG=C (notably SCO).
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LANG+set}"   = set; then LANG=C;   export LANG;   fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=libfcgi/fcgiapp.c
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_prog=$0
+  ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+  test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2;
+   if test -z "$IGNORE_ERRORS"; then
+     exit 1 ;
+   fi; }
+  else
+    { echo "configure: error: can not find sources in $srcdir" 1>&2;
+   if test -z "$IGNORE_ERRORS"; then
+     exit 1 ;
+   fi; }
+  fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+  if test "x$prefix" != xNONE; then
+    CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+  else
+    CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+  fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    echo "loading site script $ac_site_file"
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  echo "loading cache $cache_file"
+  . $cache_file
+else
+  echo "creating cache $cache_file"
+  > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+if test "`uname -s`" = "Windows_NT"; then
+  conftest_name=conftest.exe
+  ac_link='${CC-cc} -o $conftest_name $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+else
+  conftest_name=conftest
+  ac_link='${CC-cc} -o $conftest_name $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+fi
+
+if (echo "testing\c"; echo 1,2,3) | grep c >$NULLDEVW; then
+  # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+  if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >$NULLDEVW; then
+    ac_n= ac_c='
+' ac_t='       '
+  else
+    ac_n=-n ac_c= ac_t=
+  fi
+else
+  ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+#--------------------------------------------------------------------
+#  Perform common OMI configuration. This will include:
+#      1) Choosing compiler and associated build options.
+#      2) Checking for various common build associated programs.
+#      3) Determining the best build options for this platform.
+#      4) Checking for certain common header files.
+#      5) Checking for exports/global switch.
+#      6) Importing OMI dependencies (header files, libraries, and binaries).
+#       7) Checking for existence of various system libraries and routines.
+#--------------------------------------------------------------------
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+  if test -f $ac_dir/install-sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f $ac_dir/install.sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2;
+   if test -z "$IGNORE_ERRORS"; then
+     exit 1 ;
+   fi; }
+fi
+ac_config_guess=$ac_aux_dir/config.guess
+ac_config_sub=$ac_aux_dir/config.sub
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+# If we cannot run a trivial program, we must be cross compiling.
+echo $ac_n "checking whether cross-compiling""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_c_cross'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+  ac_cv_c_cross=yes
+else
+cat > conftest.$ac_ext <<EOF
+#line 662 "configure"
+#include "confdefs.h"
+main(){return(0);}
+EOF
+{ (eval echo configure:666: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }
+if test -s $conftest_name && (./$conftest_name; exit) 2>$NULLDEVW; then
+  ac_cv_c_cross=no
+else
+  ac_cv_c_cross=yes
+fi
+fi
+rm -fr conftest*
+fi
+
+echo "$ac_t""$ac_cv_c_cross" 1>&6
+cross_compiling=$ac_cv_c_cross
+
+
+
+EXTRA_OBJS=""
+INCLUDE_PATH=""
+LIBS=""
+EXPORTS_PREFIX=""
+
+
+
+
+
+
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# ./install, which can be erroneously created by make from ./install.sh.
+echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
+if test -z "$INSTALL"; then
+if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+    IFS="${IFS=        }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    # Account for people who put trailing slashes in PATH elements.
+    case "$ac_dir/" in
+    /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;;
+    *)
+      # OSF1 and SCO ODT 3.0 have their own names for install.
+      for ac_prog in ginstall installbsd scoinst install; do
+        if test -f $ac_dir/$ac_prog; then
+         if test $ac_prog = install &&
+            grep dspmsg $ac_dir/$ac_prog >$NULLDEVW 2>&1; then
+           # AIX install.  It has an incompatible calling convention.
+           # OSF/1 installbsd also uses dspmsg, but is usable.
+           :
+         else
+           ac_cv_path_install="$ac_dir/$ac_prog -c"
+           break 2
+         fi
+       fi
+      done
+      ;;
+    esac
+  done
+  IFS="$ac_save_ifs"
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL="$ac_cv_path_install"
+  else
+    # As a last resort, use the slow shell script.  We don't cache a
+    # path for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the path is relative.
+    INSTALL="$ac_install_sh"
+  fi
+fi
+if test "`uname -s`" = Windows_NT; then
+  INSTALL="copy"
+fi
+echo "$ac_t""$INSTALL" 1>&6
+
+if test "`uname -s`" = Windows_NT; then
+  INSTALL_PROGRAM="copy"
+  INSTALL_DATA="copy"
+else
+  # Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+  # It thinks the first close brace ends the variable substitution.
+  test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+  test -z "$INSTALL_DATA" && INSTALL_DATA='$(INSTALL) -m 644'
+fi
+
+for ac_prog in ar
+do
+# Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_path_AR'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  case "$AR" in
+  /*)
+  ac_cv_path_AR="$AR" # Let the user override the test with a path.
+  ;;
+  *)
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_path_AR="$ac_dir/$ac_word"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  ;;
+esac
+fi
+AR="$ac_cv_path_AR"
+if test -n "$AR"; then
+  echo "$ac_t""$AR" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+test -n "$AR" && break
+done
+
+# Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$RANLIB"; then
+  ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_RANLIB="ranlib"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":"
+fi
+fi
+RANLIB="$ac_cv_prog_RANLIB"
+if test -n "$RANLIB"; then
+  echo "$ac_t""$RANLIB" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+
+
+CCDEFS=""
+
+acaux_native_cc=""
+acaux_ccdefs=""
+O=o
+L=a
+X=""
+SHELL=/bin/sh
+if test -n "$CC"; then
+    echo "checking Compiler selection overridden via CC env variable '$CC'" 1>&6
+else
+    case "`uname -sr`" in
+        HP-UX*)
+                 # look for the native HP-UX "c89" compiler
+    # Extract the first word of "c89", so it can be a program name with args.
+set dummy c89; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_prog_acaux_native_cc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$acaux_native_cc"; then
+  ac_cv_prog_acaux_native_cc="$acaux_native_cc" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_acaux_native_cc="c89"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_prog_acaux_native_cc" && ac_cv_prog_acaux_native_cc="c89"
+fi
+fi
+acaux_native_cc="$ac_cv_prog_acaux_native_cc"
+if test -n "$acaux_native_cc"; then
+  echo "$ac_t""$acaux_native_cc" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+    hp_extra_flags=""
+    if test "`uname -r`" = "B.10.10"; then
+      hp_extra_flags="-D_XOPEN_SOURCE_EXTENDED"
+    fi
+    # if we found the compiler, set the flags
+    if test "$acaux_native_cc" = "c89"; then
+        acaux_ccdefs="-Wl,+n +DA1.0 +DS1.0 -lc -l:libdld.sl -Wl,-E -D_HPUX_SOURCE"
+
+        # set CPP for HP-UX here, too
+        CPP="c89 -E -D_HPUX_SOURCE"
+    fi
+
+            ;;
+        SunOS\ 5.*)    
+                # look for the native Solaris "cc" compiler
+    # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_prog_acaux_native_cc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$acaux_native_cc"; then
+  ac_cv_prog_acaux_native_cc="$acaux_native_cc" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_acaux_native_cc="cc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_prog_acaux_native_cc" && ac_cv_prog_acaux_native_cc="cc"
+fi
+fi
+acaux_native_cc="$ac_cv_prog_acaux_native_cc"
+if test -n "$acaux_native_cc"; then
+  echo "$ac_t""$acaux_native_cc" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+    # if we found the compiler, make sure it's the right one
+    if test "$acaux_native_cc" = "cc"; then
+        echo $ac_n "checking if cc is a native Solaris compiler""... $ac_c" 1>&6
+        cat > conftest.c <<EOF
+#ifdef __SVR4
+  yes
+#endif
+EOF
+        cc -E conftest.c > conftest.out 2>&1
+        if egrep yes conftest.out >/dev/null 2>&1; then
+            acaux_native_cc="cc"
+            echo "$ac_t""yes" 1>&6
+        else
+            echo "$ac_t""no" 1>&6
+            echo "configure: warning: 'cc' is NOT the right native Solaris compiler.
+Make sure you have /opt/SUNWspro/SC*/bin (or a link to it) in your path." 1>&2
+        fi
+        rm -f conftest*
+
+        # check for the berkley env. shell - don't want it
+        for dir in `echo $PATH | sed -e "s/\:/ /g"`
+        {
+            if test -x "$dir/cc"; then
+                if egrep SUNWspro < `echo $dir/cc` > /dev/null 2>&1; then
+                    break # SUNWpro version used - OK!
+                fi
+                if egrep ucb < `echo $dir/cc` > /dev/null 2>&1; then
+                    echo "configure: warning: You appear to be using the Berkley 
+compatibility shell around the compiler.  This shell includes Berkley 
+headers ahead of the the SVR4 headers and should not be used.  Make sure 
+your path includes /opt/SUNWspro/bin ahead of /usr/ucb." 1>&2
+                    break
+                fi
+            fi
+        }
+    fi
+
+            ;;
+       OSF*)
+           # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_prog_acaux_native_cc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$acaux_native_cc"; then
+  ac_cv_prog_acaux_native_cc="$acaux_native_cc" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_acaux_native_cc="cc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_prog_acaux_native_cc" && ac_cv_prog_acaux_native_cc="gcc"
+fi
+fi
+acaux_native_cc="$ac_cv_prog_acaux_native_cc"
+if test -n "$acaux_native_cc"; then
+  echo "$ac_t""$acaux_native_cc" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+           ;;
+       AIX*)
+           # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_prog_acaux_native_cc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$acaux_native_cc"; then
+  ac_cv_prog_acaux_native_cc="$acaux_native_cc" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_acaux_native_cc="cc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_prog_acaux_native_cc" && ac_cv_prog_acaux_native_cc="gcc"
+fi
+fi
+acaux_native_cc="$ac_cv_prog_acaux_native_cc"
+if test -n "$acaux_native_cc"; then
+  echo "$ac_t""$acaux_native_cc" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+           ;;
+       BSD\/OS\ 2*)
+           # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_prog_acaux_native_cc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$acaux_native_cc"; then
+  ac_cv_prog_acaux_native_cc="$acaux_native_cc" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_acaux_native_cc="gcc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_prog_acaux_native_cc" && ac_cv_prog_acaux_native_cc="cc"
+fi
+fi
+acaux_native_cc="$ac_cv_prog_acaux_native_cc"
+if test -n "$acaux_native_cc"; then
+  echo "$ac_t""$acaux_native_cc" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+           ;;
+       Windows_NT*)
+                # Check for msvc V2
+    if eval which cl | grep -i msvc2 > $NULLDEVW; then
+      if test "$with_nodebug" = "yes"; then
+        acaux_ccdefs="-O2 -W3"
+      else
+       acaux_ccdefs="-W3 -Zi -Od"
+      fi
+    else
+      # msvc V4
+      if test "$with_nodebug" = "yes"; then
+        acaux_ccdefs="-O2 -W3"
+      else
+       acaux_ccdefs="-W3 -Zi -Od"
+      fi
+    fi
+    # set CC and CPP
+    #
+    acaux_native_cc=$MKSROOT/cc.exe
+    CPP="cc -E"
+    O=obj
+    L=lib
+    X=.exe
+    SHELL=$MKSROOT/sh.exe
+    CONFIG_SHELL=$MKSROOT/sh.exe
+    RANLIB=true
+
+           O=obj
+           L=lib
+           X=exe
+           ;;
+    esac
+    # if native compiler, set CC, else use GCC
+    if test -n "$acaux_native_cc"; then
+        CC="$acaux_native_cc $acaux_ccdefs"
+    else
+        # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="gcc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$CC"; then
+  if test "`uname -s`" = "Windows_NT"; then
+    CC=$MKSROOT/cc.exe
+  else 
+    # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  ac_prog_rejected=no
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+        ac_prog_rejected=yes
+       continue
+      fi
+      ac_cv_prog_CC="cc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# -gt 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    set dummy "$ac_dir/$ac_word" "$@"
+    shift
+    ac_cv_prog_CC="$@"
+  fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+    test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2;
+   if test -z "$IGNORE_ERRORS"; then
+     exit 1 ;
+   fi; }
+  fi
+fi
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.c <<EOF
+#ifdef __GNUC__
+  yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:1165: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >$NULLDEVW 2>&1; then
+  ac_cv_prog_gcc=yes
+else
+  ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+if test $ac_cv_prog_gcc = yes; then
+  GCC=yes
+  if test "${CFLAGS+set}" != set; then
+    echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_prog_gcc_g'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+  ac_cv_prog_gcc_g=yes
+else
+  ac_cv_prog_gcc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc_g" 1>&6
+    if test $ac_cv_prog_gcc_g = yes; then
+      CFLAGS="-g -O"
+    else
+      CFLAGS="-O"
+    fi
+  fi
+else
+  GCC=
+  test "${CFLAGS+set}" = set || CFLAGS="-g"
+fi
+
+    fi
+fi
+if test -z "$CPP"; then
+  echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+    # This must be in double quotes, not single quotes, because CPP may get
+  # substituted into the Makefile and "${CC-cc}" will confuse make.
+  CPP="${CC-cc} -E"
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp.
+  cat > conftest.$ac_ext <<EOF
+#line 1220 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >$NULLDEVW 2>conftest.out"
+{ (eval echo configure:1226: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+if test "`uname -s`" = "Windows_NT"; then
+  ac_err=`egrep '^.*error' conftest.out`
+else
+  ac_err=`grep -v '^ *+' conftest.out`
+fi
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  rm -rf conftest*
+  CPP="${CC-cc} -E -traditional-cpp"
+  cat > conftest.$ac_ext <<EOF
+#line 1239 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >$NULLDEVW 2>conftest.out"
+{ (eval echo configure:1245: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+if test "`uname -s`" = "Windows_NT"; then
+  ac_err=`egrep '^.*error' conftest.out`
+else
+  ac_err=`grep -v '^ *+' conftest.out`
+fi
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  rm -rf conftest*
+  CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+  ac_cv_prog_CPP="$CPP"
+fi
+  CPP="$ac_cv_prog_CPP"
+else
+  ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+fi
+
+
+
+
+
+
+CCNOP=$CC
+
+echo $ac_n "checking for AIX""... $ac_c" 1>&6
+cat > conftest.$ac_ext <<EOF
+#line 1280 "configure"
+#include "confdefs.h"
+#ifdef _AIX
+  yes
+#endif
+
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "yes" >$NULLDEVW 2>&1; then
+  rm -rf conftest*
+  echo "$ac_t""yes" 1>&6; cat >> confdefs.h <<\EOF
+#define _ALL_SOURCE 1
+EOF
+
+else
+  rm -rf conftest*
+  echo "$ac_t""no" 1>&6
+fi
+rm -f conftest*
+
+
+
+#--------------------------------------------------------------------
+#  Determine the best warning options for this platform
+#--------------------------------------------------------------------
+if test "$CC" = "gcc " -o "$CC" = "gcc"; then
+    WARNDEFS="-ansi -pedantic -Wall -Wmissing-prototypes"
+else
+    if test -f /bin/uname && test `/bin/uname` = OSF1; then
+       WARNDEFS="-std1 -warnprotos"
+    fi
+fi
+CCDEFS="$CCDEFS $WARNDEFS"
+
+echo $ac_n "checking for working const""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1319 "configure"
+#include "confdefs.h"
+
+int main() { return 0; }
+int t() {
+
+/* Ultrix mips cc rejects this.  */
+typedef int charset[2]; const charset x;
+/* SunOS 4.1.1 cc rejects this.  */
+char const *const *ccp;
+char **p;
+/* NEC SVR4.0.2 mips cc rejects this.  */
+struct point {int x, y;};
+static struct point const zero = {0,0};
+/* AIX XL C 1.02.0.0 rejects this.
+   It does not let you subtract one const X* pointer from another in an arm
+   of an if-expression whose if-part is not a constant expression */
+const char *g = "string";
+ccp = &g + (g ? g-g : 0);
+/* HPUX 7.0 cc rejects these. */
+++ccp;
+p = (char**) ccp;
+ccp = (char const *const *) p;
+{ /* SCO 3.2v4 cc rejects this.  */
+  char *t;
+  char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+  *t++ = 0;
+}
+{ /* Someone thinks the Sun supposedly-ANSI compiler will reject this.  */
+  int x[] = {25, 17};
+  const int *foo = &x[0];
+  ++foo;
+}
+{ /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+  typedef const int *iptr;
+  iptr p = 0;
+  ++p;
+}
+{ /* AIX XL C 1.02.0.0 rejects this saying
+     "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+  struct s { int j; const int *ap[3]; };
+  struct s *b; b->j = 5;
+}
+{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
+  const int foo = 10;
+}
+
+; return 0; }
+EOF
+if { (eval echo configure:1369: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_c_const=yes
+else
+  rm -rf conftest*
+  ac_cv_c_const=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_c_const" 1>&6
+if test $ac_cv_c_const = no; then
+  cat >> confdefs.h <<\EOF
+#define const 
+EOF
+
+fi
+
+
+echo $ac_n "checking size of unsigned int""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_sizeof_unsigned_int'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+  :
+else
+cat > conftest.$ac_ext <<EOF
+#line 1397 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+  FILE *f=fopen("conftestval", "w");
+  if (!f) exit(1);
+  fprintf(f, "%d\n", sizeof(unsigned int));
+  exit(0);
+}
+EOF
+{ (eval echo configure:1408: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }
+if test -s $conftest_name && (./$conftest_name; exit) 2>$NULLDEVW; then
+  ac_cv_sizeof_unsigned_int=`cat conftestval`
+else
+  ac_cv_sizeof_unsigned_int=0
+fi
+fi
+rm -fr conftest*
+fi
+echo "$ac_t""$ac_cv_sizeof_unsigned_int" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_UNSIGNED_INT $ac_cv_sizeof_unsigned_int
+EOF
+
+
+echo $ac_n "checking size of unsigned short""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_sizeof_unsigned_short'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+  :
+else
+cat > conftest.$ac_ext <<EOF
+#line 1431 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+  FILE *f=fopen("conftestval", "w");
+  if (!f) exit(1);
+  fprintf(f, "%d\n", sizeof(unsigned short));
+  exit(0);
+}
+EOF
+{ (eval echo configure:1442: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }
+if test -s $conftest_name && (./$conftest_name; exit) 2>$NULLDEVW; then
+  ac_cv_sizeof_unsigned_short=`cat conftestval`
+else
+  ac_cv_sizeof_unsigned_short=0
+fi
+fi
+rm -fr conftest*
+fi
+echo "$ac_t""$ac_cv_sizeof_unsigned_short" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_UNSIGNED_SHORT $ac_cv_sizeof_unsigned_short
+EOF
+
+
+echo $ac_n "checking size of unsigned long""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_sizeof_unsigned_long'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+  :
+else
+cat > conftest.$ac_ext <<EOF
+#line 1465 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+  FILE *f=fopen("conftestval", "w");
+  if (!f) exit(1);
+  fprintf(f, "%d\n", sizeof(unsigned long));
+  exit(0);
+}
+EOF
+{ (eval echo configure:1476: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }
+if test -s $conftest_name && (./$conftest_name; exit) 2>$NULLDEVW; then
+  ac_cv_sizeof_unsigned_long=`cat conftestval`
+else
+  ac_cv_sizeof_unsigned_long=0
+fi
+fi
+rm -fr conftest*
+fi
+echo "$ac_t""$ac_cv_sizeof_unsigned_long" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_UNSIGNED_LONG $ac_cv_sizeof_unsigned_long
+EOF
+
+
+echo $ac_n "checking size of int""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_sizeof_int'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+  :
+else
+cat > conftest.$ac_ext <<EOF
+#line 1499 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+  FILE *f=fopen("conftestval", "w");
+  if (!f) exit(1);
+  fprintf(f, "%d\n", sizeof(int));
+  exit(0);
+}
+EOF
+{ (eval echo configure:1510: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }
+if test -s $conftest_name && (./$conftest_name; exit) 2>$NULLDEVW; then
+  ac_cv_sizeof_int=`cat conftestval`
+else
+  ac_cv_sizeof_int=0
+fi
+fi
+rm -fr conftest*
+fi
+echo "$ac_t""$ac_cv_sizeof_int" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_INT $ac_cv_sizeof_int
+EOF
+
+
+echo $ac_n "checking size of long""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_sizeof_long'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+  :
+else
+cat > conftest.$ac_ext <<EOF
+#line 1533 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+  FILE *f=fopen("conftestval", "w");
+  if (!f) exit(1);
+  fprintf(f, "%d\n", sizeof(long));
+  exit(0);
+}
+EOF
+{ (eval echo configure:1544: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }
+if test -s $conftest_name && (./$conftest_name; exit) 2>$NULLDEVW; then
+  ac_cv_sizeof_long=`cat conftestval`
+else
+  ac_cv_sizeof_long=0
+fi
+fi
+rm -fr conftest*
+fi
+echo "$ac_t""$ac_cv_sizeof_long" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_LONG $ac_cv_sizeof_long
+EOF
+
+
+echo $ac_n "checking size of size_t""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_sizeof_size_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+  :
+else
+cat > conftest.$ac_ext <<EOF
+#line 1567 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+  FILE *f=fopen("conftestval", "w");
+  if (!f) exit(1);
+  fprintf(f, "%d\n", sizeof(size_t));
+  exit(0);
+}
+EOF
+{ (eval echo configure:1578: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }
+if test -s $conftest_name && (./$conftest_name; exit) 2>$NULLDEVW; then
+  ac_cv_sizeof_size_t=`cat conftestval`
+else
+  ac_cv_sizeof_size_t=0
+fi
+fi
+rm -fr conftest*
+fi
+echo "$ac_t""$ac_cv_sizeof_size_t" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_SIZE_T $ac_cv_sizeof_size_t
+EOF
+
+
+cat > conftest.$ac_ext <<EOF
+#line 1594 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "ssize_t" >$NULLDEVW 2>&1; then
+  :
+else
+  rm -rf conftest*
+  cat >> confdefs.h <<\EOF
+#define ssize_t int
+EOF
+
+fi
+rm -f conftest*
+
+echo $ac_n "checking size of off_t""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_sizeof_off_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+  :
+else
+cat > conftest.$ac_ext <<EOF
+#line 1618 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+  FILE *f=fopen("conftestval", "w");
+  if (!f) exit(1);
+  fprintf(f, "%d\n", sizeof(off_t));
+  exit(0);
+}
+EOF
+{ (eval echo configure:1629: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }
+if test -s $conftest_name && (./$conftest_name; exit) 2>$NULLDEVW; then
+  ac_cv_sizeof_off_t=`cat conftestval`
+else
+  ac_cv_sizeof_off_t=0
+fi
+fi
+rm -fr conftest*
+fi
+echo "$ac_t""$ac_cv_sizeof_off_t" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_OFF_T $ac_cv_sizeof_off_t
+EOF
+
+
+
+
+echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1651 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >$NULLDEVW 2>conftest.out"
+{ (eval echo configure:1659: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+if test "`uname -s`" = "Windows_NT"; then
+  ac_err=`egrep '^.*error' conftest.out`
+else
+  ac_err=`grep -v '^ *+' conftest.out`
+fi
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  ac_cv_header_stdc=yes
+else
+  echo "$ac_err" >&5
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1678 "configure"
+#include "confdefs.h"
+#include <string.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "memchr" >$NULLDEVW 2>&1; then
+  :
+else
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1696 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "free" >$NULLDEVW 2>&1; then
+  :
+else
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+if test "$cross_compiling" = yes; then
+  :
+else
+cat > conftest.$ac_ext <<EOF
+#line 1717 "configure"
+#include "confdefs.h"
+#include <ctype.h>
+#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+
+EOF
+{ (eval echo configure:1728: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }
+if test -s $conftest_name && (./$conftest_name; exit) 2>$NULLDEVW; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+fi
+rm -fr conftest*
+fi
+fi
+
+echo "$ac_t""$ac_cv_header_stdc" 1>&6
+if test $ac_cv_header_stdc = yes; then
+  cat >> confdefs.h <<\EOF
+#define STDC_HEADERS 1
+EOF
+
+fi
+
+for ac_hdr in limits.h unistd.h
+do
+ac_safe=`echo "$ac_hdr" | tr './\055' '___'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1755 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >$NULLDEVW 2>conftest.out"
+{ (eval echo configure:1760: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+if test "`uname -s`" = "Windows_NT"; then
+  ac_err=`egrep '^.*error' conftest.out`
+else
+  ac_err=`grep -v '^ *+' conftest.out`
+fi
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | tr 'abcdefghijklmnopqrstuvwxyz./\055' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ___'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+
+# Check whether --with-global or --without-global was given.
+if test "${with_global+set}" = set; then
+  withval="$with_global"
+  with_export=$withval
+    cat >> confdefs.h <<\EOF
+#define WITH_EXPORT 1
+EOF
+
+    cat >> confdefs.h <<\EOF
+#define WITH_GLOBAL 1
+EOF
+
+else
+  if test -z "$with_export"; then
+       with_export=no
+     fi
+
+fi
+
+# Check whether --with-domestic or --without-domestic was given.
+if test "${with_domestic+set}" = set; then
+  withval="$with_domestic"
+  with_domestic=$withval
+    cat >> confdefs.h <<\EOF
+#define WITH_DOMESTIC 1
+EOF
+
+else
+  if test -z "$with_export"; then
+       with_export=no
+     fi
+
+fi
+
+
+
+#--------------------------------------------------------------------
+#  Run with Purify?  PureCoverage?  Quantify?
+#--------------------------------------------------------------------
+# Check whether --with-purify or --without-purify was given.
+if test "${with_purify+set}" = set; then
+  withval="$with_purify"
+  with_purify=$withval
+else
+  with_purify=no
+
+fi
+
+if test "$with_purify" = "yes" ; then
+    CC="purify $CC"
+fi
+
+# Check whether --with-purecov or --without-purecov was given.
+if test "${with_purecov+set}" = set; then
+  withval="$with_purecov"
+  with_purecov=$withval
+else
+  with_purecov=no
+
+fi
+
+if test "$with_purecov" = "yes" ; then
+    CC="purecov $CC"
+fi
+# Check whether --with-quantify or --without-quantify was given.
+if test "${with_quantify+set}" = set; then
+  withval="$with_quantify"
+  with_quantify=$withval
+else
+  with_quantify=no
+
+fi
+
+if test "$with_quantify" = "yes" ; then
+    CC="quantify $CC"
+fi
+
+
+
+
+# Check whether --with-profile or --without-profile was given.
+if test "${with_profile+set}" = set; then
+  withval="$with_profile"
+  with_profile=$withval
+else
+  with_profile=no
+
+fi
+
+if test "$with_profile" = "yes" ; then
+    PROFILE="-pg"
+else
+    PROFILE=""
+fi
+
+
+
+#--------------------------------------------------------------------
+#  Build optimized, production version with no debugging code?  No
+#  asserts?  No testing code?
+#--------------------------------------------------------------------
+# Check whether --with-nodebug or --without-nodebug was given.
+if test "${with_nodebug+set}" = set; then
+  withval="$with_nodebug"
+  with_nodebug=$withval
+else
+  with_nodebug=no
+
+fi
+
+if test "$with_nodebug" = "yes" ; then
+    CCDEFS="$CCDEFS -O"
+else
+    cat >> confdefs.h <<\EOF
+#define WITH_DEBUG 1
+EOF
+
+    CCDEFS="$CCDEFS -g"
+fi
+# Check whether --with-noassert or --without-noassert was given.
+if test "${with_noassert+set}" = set; then
+  withval="$with_noassert"
+  with_noassert=$withval
+else
+  with_noassert=no
+
+fi
+
+if test "$with_noassert" = "no" ; then
+    cat >> confdefs.h <<\EOF
+#define WITH_ASSERT 1
+EOF
+
+fi
+# Check whether --with-notest or --without-notest was given.
+if test "${with_notest+set}" = set; then
+  withval="$with_notest"
+  with_notest=$withval
+else
+  with_notest=no
+
+fi
+
+if test "$with_notest" = "no" ; then
+    cat >> confdefs.h <<\EOF
+#define WITH_TEST 1
+EOF
+
+fi
+
+# Make sure that they didn't specify with-domestic and with-global
+if test "$with_export" = "yes" -a "$with_domestic" = "yes"; then
+  { echo "configure: error: --with-domestic and --with-global are mutually exclusive" 1>&2;
+   if test -z "$IGNORE_ERRORS"; then
+     exit 1 ;
+   fi; }
+  exit 1
+fi
+
+
+#--------------
+# Open Market Dependencies
+#
+# define the platform that is being built
+#
+
+# Old style platform name.
+#
+legacy_platform=`uname -s`-`uname -m`-`uname -r`
+legacy_platform=`echo "$legacy_platform" | tr / -`
+#
+# Generate the canonical form of the platform name
+#
+platform=`uname -s`-`uname -r`
+platform=`echo "$platform" | tr / -`
+platform=`echo "$platform" | tr '[A-Z]' '[a-z]'`
+platform_class=UNIX
+
+case $platform in
+  aix*)                platform=aix_414;;
+  bsd-386-1*)          platform=bsdi_11;;
+  bsd-os-2*)           platform=bsdi_20;;
+  hp-ux-b.10.10)       platform=hpux_1010;;
+  hp-ux-b.10*)                 platform=hpux_10;;
+  hp-ux-a.09*)                 platform=hpux_905;;
+  irix-5*)             platform=sgi_53;;
+  irix-6*)             platform=sgi_62;;
+  osf1-v3.0)           platform=osf_30;;
+  osf1-v3.2)           platform=osf_32;;
+  osf1-v4.0)           platform=osf_40;;
+  sunos-5.4)           platform=sun_54;;
+  sunos-5.5*)          platform=sun_55;;
+  sunos-4.1.4)         platform=sun_414;;
+  linux*)              platform=lin_128;;
+  nonstop*)            platform=tan_40;;
+  unix_system_v*)
+    stratus_name=`uname -a`
+    ftx_version=`echo "$stratus_name" | cut -f5 -d' '`
+    case $ftx_version in
+      3.0*)            platform=ftx_30;;
+      3.1*)            platform=ftx_31;;
+    esac;;
+  sco*)                        platform=sco_32;;
+  windows_95-4)
+    platform=win_40
+    platform_class=WINDOWS
+  ;;
+  windows_nt-3)
+    platform=wnt_351
+    platform_class=WINDOWS
+  ;;
+  windows_nt-4)
+    platform=wnt_40
+    platform_class=WINDOWS
+  ;;
+esac
+
+PLATFORM=$platform
+
+PLATFORM_CLASS=$platform_class
+
+
+#
+# Pick up any OMI specific switches
+#
+# Check whether --with-omireleases or --without-omireleases was given.
+if test "${with_omireleases+set}" = set; then
+  withval="$with_omireleases"
+  with_releases="yes"
+else
+  with_releases="no"
+fi
+
+
+# Start of version declaration
+#
+SUBSYSTEM=FastCGI
+
+VERSION=T2.0.a
+
+if test -n "a"; then
+  CVS_TAG=T2_0_a_NT
+  DOT_CVS_TAG=T2.0.a.NT
+else
+  echo "Warning: Patch tag missing from version.in"
+  CVS_TAG=T2_0_NT
+  DOT_CVS_TAG=T2.0.NT
+fi
+
+
+if test "$with_export" = "yes"; then
+  export_class="-- GLOBAL version"
+else
+  export_class="-- DOMESTIC version"
+fi
+#
+echo "--------------------------------------------------------------------------"
+echo Configuring $SUBSYSTEM version $VERSION, cvs tag is: $CVS_TAG $export_class
+echo "Canonical system name is $platform"
+if test -z "$with_purify"; then
+  with_purify="no"
+fi
+echo "CC = $CC, CFLAGS = $acaux_ccdefs"
+echo "omi_releases = $with_releases, purify = $with_purify"
+if test -z "$with_purecov"; then
+  with_purecov="no"
+fi
+if test -z "$with_quantify"; then
+  with_quantify="no"
+fi
+if test -z "$with_profile"; then
+  with_profile="no"
+fi
+echo "purecov = $with_purecov, quantify = $with_quantify, profile = $with_profile"
+if test -z "$with_nodebug"; then
+  with_nodebug="no"
+fi
+if test -z "$with_noassert"; then
+  with_noassert="no"
+fi
+if test -z "$with_notest"; then
+  with_notest="no"
+fi
+echo "nodebug = $with_nodebug, noassert = $with_noassert, notest = $with_notest"
+echo "Built on `uname -n`, OS-type: `uname -s`, version `uname -r`"
+version=`echo Revision: 1.15  | cut -f 2 -d' '`
+echo "Autoconf version 2.9, OMI extensions version: $version"
+echo "--------------------------------------------------------------------------"
+#
+# Create version.conf
+#
+echo "Creating version.conf"
+cat <<EOF > version.conf
+#
+# version.conf for $SUBSYSTEM. 
+#
+# This file is machine generated.
+# Do not edit this file.
+#
+# generated on `date`
+#
+set Release $DOT_CVS_TAG
+set Platform $PLATFORM
+set ProductName $SUBSYSTEM
+EOF
+if test ! -z "$DOT_MIN_RELEASE"; then
+  cat <<EOF >> version.conf
+set MinRelease $DOT_MIN_RELEASE
+EOF
+fi
+if test ! -z "$REQUIRED_PAKS"; then
+  cat <<EOF >>version.conf
+set RequiredPaks {$REQUIRED_PAKS}
+EOF
+fi
+# End of version declaration
+
+
+
+if test "$with_releases" = "yes"; then
+  if test "`uname -s`" = Windows_NT; then
+    echo "Setting up EXPORTS_ROOT for wnt"
+    EXPORTS_ROOT=\\omi\\exports
+    if test "$with_export" = "yes"; then
+      EXPORTS_PREFIX=$EXPORTS_ROOT\\$SUBSYSTEM\\$VERSION/${platform}.global
+    else
+      if test "$export_restricted" = "yes"; then
+        EXPORTS_PREFIX=$EXPORTS_ROOT\\$SUBSYSTEM\\$VERSION\\${platform}.domestic
+      else
+        EXPORTS_PREFIX=$EXPORTS_ROOT\\$SUBSYSTEM\\$VERSION\\$platform
+      fi
+    fi
+    EXPORTS_COMMON=$EXPORTS_ROOT\\$SUBSYSTEM\\$VERSION\\common
+  else 
+    EXPORTS_ROOT=/omi/exports
+    if test "$with_export" = "yes"; then
+      EXPORTS_PREFIX=$EXPORTS_ROOT/$SUBSYSTEM/$VERSION/${platform}.global
+    else
+      if test "$export_restricted" = "yes"; then
+        EXPORTS_PREFIX=$EXPORTS_ROOT/$SUBSYSTEM/$VERSION/${platform}.domestic
+      else
+        EXPORTS_PREFIX=$EXPORTS_ROOT/$SUBSYSTEM/$VERSION/$platform
+      fi
+    fi
+    EXPORTS_COMMON=$EXPORTS_ROOT/$SUBSYSTEM/$VERSION/common
+  fi
+else
+  EXPORTS_ROOT=/usr/local
+  EXPORTS_PREFIX=$EXPORTS_ROOT
+  EXPORTS_COMMON=$EXPORTS_ROOT
+fi
+
+prefix=$EXPORTS_PREFIX
+exec_prefix=$EXPORTS_PREFIX
+common_prefix=$EXPORTS_COMMON
+
+
+
+
+
+
+
+#---------------
+
+#------------------------
+#
+# Start of dependencies.in
+#
+
+#
+# OMI provided dependencies
+#
+
+#
+# Depends on Tcl, Version: V7.4
+#
+if test "$with_releases" = "yes"; then
+  platform_root=$EXPORTS_ROOT/Tcl/V7.4/$platform
+  legacy_platform_root=$EXPORTS_ROOT/Tcl/V7.4/$legacy_platform
+  TCLLIB_DIR=$platform_root/lib
+  TCLINC_DIR=$EXPORTS_ROOT/Tcl/V7.4/common/include
+  TCLCOMMON_DIR=$EXPORTS_ROOT/Tcl/V7.4/common
+  TCLBIN_DIR=$platform_root/bin
+  #
+  # If the file doesn't exist in the new name space, fall back to
+  # the legacy name.
+  #
+  if test ! -r $TCLLIB_DIR/libtcl.$L -a \
+         ! -r $TCLLIB_DIR/Tcl.$L; then
+    echo "  libtcl.$L or Tcl.$L not found in $TCLLIB_DIR" > PATH_ERR
+    TCLLIB_DIR=$legacy_platform_root/lib
+    TCLBIN_DIR=$legacy_platform_root/bin
+    platform_root=$legacy_platform_root
+  fi
+else
+  platform_root=$EXPORTS_ROOT
+  TCLLIB_DIR=$platform_root/lib
+  TCLINC_DIR=$EXPORTS_ROOT/include
+  TCLCOMMON_DIR=$EXPORTS_ROOT
+  TCLBIN_DIR=$platform_root/bin
+fi
+
+
+
+
+
+
+echo $ac_n "checking for Tcl library""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_lib_Tcl'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  
+if test "$with_export" = "yes"; then
+  
+#
+# Declare a dependency
+#
+if test "$with_releases" = "yes"; then
+  #
+  # Append the information to the lock database
+  #
+  dependency="Tcl,V7.4,global,$SUBSYSTEM,$VERSION,$CVS_TAG"
+  #
+  # Log the dependency
+  #
+  /omi/exports/Toolshed/LATEST/common/bin/xmb-ctrl \
+    -send "$dependency" -type dependency
+fi
+
+  if test -r ${platform_root}.global/lib/libtcl.$L; then
+    ac_cv_lib_Tcl=yes
+    TCLLIB_DIR=${platform_root}.global/lib
+    TCLBIN_DIR=${platform_root}.global/bin
+    echo "Using export version for Tcl..."
+  elif test -r ${platform_root}.global/lib/Tcl.$L; then
+    ac_cv_lib_Tcl=yes
+    TCLLIB_DIR=${platform_root}.global/lib
+    TCLBIN_DIR=${platform_root}.global/bin
+    echo "Using export version for Tcl... "
+  else
+    if test -r $TCLLIB_DIR/libtcl.$L -o \
+            -r $TCLLIB_DIR/Tcl.$L; then
+      ac_cv_lib_Tcl=yes
+    else
+      ac_cv_lib_Tcl=no
+    fi
+  fi
+else  
+  if test "$export_restricted" = "yes"; then
+    
+#
+# Declare a dependency
+#
+if test "$with_releases" = "yes"; then
+  #
+  # Append the information to the lock database
+  #
+  dependency="Tcl,V7.4,domestic,$SUBSYSTEM,$VERSION,$CVS_TAG"
+  #
+  # Log the dependency
+  #
+  /omi/exports/Toolshed/LATEST/common/bin/xmb-ctrl \
+    -send "$dependency" -type dependency
+fi
+
+  else
+    
+#
+# Declare a dependency
+#
+if test "$with_releases" = "yes"; then
+  #
+  # Append the information to the lock database
+  #
+  dependency="Tcl,V7.4,unrestricted,$SUBSYSTEM,$VERSION,$CVS_TAG"
+  #
+  # Log the dependency
+  #
+  /omi/exports/Toolshed/LATEST/common/bin/xmb-ctrl \
+    -send "$dependency" -type dependency
+fi
+
+  fi
+  if test -r $TCLLIB_DIR/libtcl.$L -o \
+          -r $TCLLIB_DIR/Tcl.$L; then
+    ac_cv_lib_Tcl=yes
+  else
+    ac_cv_lib_Tcl=no
+  fi
+fi
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'Tcl`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_lib=HAVE_LIB`echo Tcl | tr '[a-z]' '[A-Z]'`
+    cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+  if test "$with_export" = "yes"; then
+    if test -r "${platform_root}.global/lib/libtcl.$L";then
+      LIBS="$LIBS ${platform_root}.global/lib/libtcl.$L"
+    elif test -r "${platform_root}.global/lib/Tcl.$L";then
+      LIBS="$LIBS ${platform_root}.global/lib/Tcl.$L"
+    else
+      LIBS="$LIBS $TCLLIB_DIR/libtcl.$L"
+    fi
+  else
+    if test "$PLATFORM_CLASS" = "UNIX"; then
+      LIBS="$LIBS $TCLLIB_DIR/libtcl.$L"
+    else
+      LIBS="$LIBS $TCLLIB_DIR/Tcl.$L"
+    fi
+  fi
+  INCLUDE_PATH="$INCLUDE_PATH -I$TCLINC_DIR"
+else
+  echo "  libtcl.$L not found in $TCLLIB_DIR" >> PATH_ERR
+  echo "$ac_t""not found" 1>&6
+  cat PATH_ERR
+fi 
+rm -f PATH_ERR
+
+
+#
+# System library dependencies
+#
+
+
+if test $PLATFORM != "sgi_62"; then
+echo $ac_n "checking for nsl""... $ac_c" 1>&6
+echo $ac_n "checking for -lnsl""... $ac_c" 1>&6
+ac_lib_var=`echo nsl_gethostent | tr '.-/+' '___p'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lnsl  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2327 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+char gethostent();
+
+int main() { return 0; }
+int t() {
+gethostent()
+; return 0; }
+EOF
+if { (eval echo configure:2337: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  LIBS="$LIBS -lnsl"
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+
+echo $ac_n "checking for resolv""... $ac_c" 1>&6
+echo $ac_n "checking for -lresolv""... $ac_c" 1>&6
+ac_lib_var=`echo resolv_res_init | tr '.-/+' '___p'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lresolv  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2365 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+char res_init();
+
+int main() { return 0; }
+int t() {
+res_init()
+; return 0; }
+EOF
+if { (eval echo configure:2375: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  LIBS="$LIBS -lresolv"
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+
+echo $ac_n "checking for socket""... $ac_c" 1>&6
+echo $ac_n "checking for -lsocket""... $ac_c" 1>&6
+ac_lib_var=`echo socket_socket | tr '.-/+' '___p'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lsocket  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2403 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+char socket();
+
+int main() { return 0; }
+int t() {
+socket()
+; return 0; }
+EOF
+if { (eval echo configure:2413: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  LIBS="$LIBS -lsocket"
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+
+echo $ac_n "checking for dnet_stub""... $ac_c" 1>&6
+if test -s /usr/lib/libdnet_stub.a; then
+  LIBS="$LIBS -ldnet_stub"
+  echo "$ac_t""yes" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+if test "`uname -s`" = "Windows_NT"; then
+  echo $ac_n "checking for wsock32""... $ac_c" 1>&6
+  if test -s c:/msdev/lib/wsock32.lib; then
+    LIBS="$LIBS -lwsock32"
+    echo "$ac_t""yes" 1>&6
+  fi
+  echo $ac_n "checking for advapi32""... $ac_c" 1>&6
+  if test -s c:/msdev/lib/advapi32.lib; then
+    LIBS="$LIBS -ladvapi32"
+    echo "$ac_t""yes" 1>&6
+  fi
+  echo $ac_n "checking for user32""... $ac_c" 1>&6
+  if test -s c:/msdev/lib/user32.lib; then
+    LIBS="$LIBS -luser32"
+    echo "$ac_t""yes" 1>&6
+  fi
+fi
+fi
+
+
+if test $PLATFORM != sgi_62; then
+echo $ac_n "checking for sin""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_func_sin'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2466 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char sin(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+char sin();
+
+int main() { return 0; }
+int t() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_sin) || defined (__stub___sin)
+choke me
+#else
+sin();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2488: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then
+  rm -rf conftest*
+  eval "ac_cv_func_sin=yes"
+else
+  rm -rf conftest*
+  eval "ac_cv_func_sin=no"
+fi
+rm -f conftest*
+
+fi
+if eval "test \"`echo '$ac_cv_func_'sin`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  :
+else
+  echo "$ac_t""no" 1>&6
+LIBS="$LIBS -lm"
+fi
+
+echo $ac_n "checking for -lieee""... $ac_c" 1>&6
+ac_lib_var=`echo ieee_main | tr '.-/+' '___p'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lieee  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 2514 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+char main();
+
+int main() { return 0; }
+int t() {
+main()
+; return 0; }
+EOF
+if { (eval echo configure:2524: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  LIBS="$LIBS -lieee"
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+cat >> confdefs.h <<\EOF
+#define HAVE_MATHLIB 1
+EOF
+
+fi
+
+
+#
+# End of dependencies.in
+#------------------------
+
+
+if test "$export_restricted" = "yes"; then
+  if test "$with_export" = "yes"; then
+    EXPORT_RESTRICTION=GLOBAL
+  else
+    EXPORT_RESTRICTION=DOMESTIC
+  fi
+else
+  EXPORT_RESTRICTION=NONE
+fi
+
+
+
+
+echo $ac_n "checking for sun_len in sys/un.h""... $ac_c" 1>&6
+cat > conftest.$ac_ext <<EOF
+#line 2569 "configure"
+#include "confdefs.h"
+#include <sys/un.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "sun_len" >$NULLDEVW 2>&1; then
+  rm -rf conftest*
+  echo "$ac_t""yes" 1>&6
+   cat >> confdefs.h <<\EOF
+#define HAVE_SOCKADDR_UN_SUN_LEN 1
+EOF
+
+else
+  rm -rf conftest*
+  echo "$ac_t""no" 1>&6
+fi
+rm -f conftest*
+
+
+#--------------------------------------------------------------------
+#  What extra libraries do we need for this platform?
+#--------------------------------------------------------------------
+
+for ac_func in strerror strtol
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2599 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+char $ac_func();
+
+int main() { return 0; }
+int t() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2621: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=yes"
+else
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+
+fi
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+
+for ac_func in strerror strtol
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2651 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+char $ac_func();
+
+int main() { return 0; }
+int t() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2673: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=yes"
+else
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+
+fi
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  :
+else
+  echo "$ac_t""no" 1>&6
+LIBOBJS="$LIBOBJS ${ac_func}.o"
+fi
+
+done
+
+
+#--------------------------------------------------------------------
+#       Include sys/select.h if it exists and if it supplies things
+#       that appear to be useful and aren't already in sys/types.h.
+#       This appears to be true only on the RS/6000 under AIX.  Some
+#       systems like OSF/1 have a sys/select.h that's of no use, and
+#       other systems like SCO UNIX have a sys/select.h that's
+#       pernicious.  If "fd_set" isn't defined anywhere then set a
+#       special flag.
+#--------------------------------------------------------------------
+
+echo $ac_n "checking for fd_set in in sys/types.h""... $ac_c" 1>&6
+cat > conftest.$ac_ext <<EOF
+#line 2706 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+int main() { return 0; }
+int t() {
+fd_set readMask, writeMask;
+; return 0; }
+EOF
+if { (eval echo configure:2714: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then
+  rm -rf conftest*
+  echo "$ac_t""yes" 1>&6
+else
+  rm -rf conftest*
+  echo "$ac_t""no" 1>&6
+   echo $ac_n "checking for fd_mask in in sys/select.h""... $ac_c" 1>&6
+   cat > conftest.$ac_ext <<EOF
+#line 2722 "configure"
+#include "confdefs.h"
+#include <sys/select.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "fd_mask" >$NULLDEVW 2>&1; then
+  rm -rf conftest*
+  echo "$ac_t""yes" 1>&6
+      cat >> confdefs.h <<\EOF
+#define HAVE_SYS_SELECT_H 1
+EOF
+
+else
+  rm -rf conftest*
+  echo "$ac_t""no" 1>&6
+      cat >> confdefs.h <<\EOF
+#define NO_FD_SET 1
+EOF
+
+fi
+rm -f conftest*
+
+fi
+rm -f conftest*
+
+
+echo $ac_n "checking for fpos in stdio.h""... $ac_c" 1>&6
+cat > conftest.$ac_ext <<EOF
+#line 2750 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "fpos_t" >$NULLDEVW 2>&1; then
+  rm -rf conftest*
+  echo "$ac_t""yes" 1>&6
+   cat >> confdefs.h <<\EOF
+#define HAVE_FPOS 1
+EOF
+
+else
+  rm -rf conftest*
+  echo "$ac_t""no" 1>&6
+fi
+rm -f conftest*
+
+
+for ac_hdr in windows.h
+do
+ac_safe=`echo "$ac_hdr" | tr './\055' '___'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2777 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >$NULLDEVW 2>conftest.out"
+{ (eval echo configure:2782: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+if test "`uname -s`" = "Windows_NT"; then
+  ac_err=`egrep '^.*error' conftest.out`
+else
+  ac_err=`grep -v '^ *+' conftest.out`
+fi
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | tr 'abcdefghijklmnopqrstuvwxyz./\055' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ___'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_hdr in sys/socket.h
+do
+ac_safe=`echo "$ac_hdr" | tr './\055' '___'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2818 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >$NULLDEVW 2>conftest.out"
+{ (eval echo configure:2823: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+if test "`uname -s`" = "Windows_NT"; then
+  ac_err=`egrep '^.*error' conftest.out`
+else
+  ac_err=`grep -v '^ *+' conftest.out`
+fi
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | tr 'abcdefghijklmnopqrstuvwxyz./\055' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ___'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_hdr in winsock.h
+do
+ac_safe=`echo "$ac_hdr" | tr './\055' '___'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2859 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >$NULLDEVW 2>conftest.out"
+{ (eval echo configure:2864: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+if test "`uname -s`" = "Windows_NT"; then
+  ac_err=`egrep '^.*error' conftest.out`
+else
+  ac_err=`grep -v '^ *+' conftest.out`
+fi
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | tr 'abcdefghijklmnopqrstuvwxyz./\055' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ___'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_hdr in netdb.h
+do
+ac_safe=`echo "$ac_hdr" | tr './\055' '___'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2900 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >$NULLDEVW 2>conftest.out"
+{ (eval echo configure:2905: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+if test "`uname -s`" = "Windows_NT"; then
+  ac_err=`egrep '^.*error' conftest.out`
+else
+  ac_err=`grep -v '^ *+' conftest.out`
+fi
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | tr 'abcdefghijklmnopqrstuvwxyz./\055' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ___'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_hdr in netinet/in.h
+do
+ac_safe=`echo "$ac_hdr" | tr './\055' '___'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2941 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >$NULLDEVW 2>conftest.out"
+{ (eval echo configure:2946: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+if test "`uname -s`" = "Windows_NT"; then
+  ac_err=`egrep '^.*error' conftest.out`
+else
+  ac_err=`grep -v '^ *+' conftest.out`
+fi
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | tr 'abcdefghijklmnopqrstuvwxyz./\055' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ___'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_hdr in arpa/inet.h
+do
+ac_safe=`echo "$ac_hdr" | tr './\055' '___'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2982 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >$NULLDEVW 2>conftest.out"
+{ (eval echo configure:2987: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+if test "`uname -s`" = "Windows_NT"; then
+  ac_err=`egrep '^.*error' conftest.out`
+else
+  ac_err=`grep -v '^ *+' conftest.out`
+fi
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | tr 'abcdefghijklmnopqrstuvwxyz./\055' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ___'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_hdr in strings.h
+do
+ac_safe=`echo "$ac_hdr" | tr './\055' '___'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 3023 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >$NULLDEVW 2>conftest.out"
+{ (eval echo configure:3028: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+if test "`uname -s`" = "Windows_NT"; then
+  ac_err=`egrep '^.*error' conftest.out`
+else
+  ac_err=`grep -v '^ *+' conftest.out`
+fi
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | tr 'abcdefghijklmnopqrstuvwxyz./\055' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ___'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_hdr in sys/time.h
+do
+ac_safe=`echo "$ac_hdr" | tr './\055' '___'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 3064 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >$NULLDEVW 2>conftest.out"
+{ (eval echo configure:3069: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+if test "`uname -s`" = "Windows_NT"; then
+  ac_err=`egrep '^.*error' conftest.out`
+else
+  ac_err=`grep -v '^ *+' conftest.out`
+fi
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | tr 'abcdefghijklmnopqrstuvwxyz./\055' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ___'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+
+#--------------------------------------------------------------------
+#  Do we need cross-process locking on this platform?
+#--------------------------------------------------------------------
+echo $ac_n "checking "if this machine needs cross-process locking"""... $ac_c" 1>&6
+case "`uname -sr`" in
+    IRIX\ 5.*) 
+       cat >> confdefs.h <<\EOF
+#define USE_LOCKING 1
+EOF
+
+       echo "$ac_t""yes" 1>&6
+    ;;
+    SunOS\ 5.*)        
+       cat >> confdefs.h <<\EOF
+#define USE_LOCKING 1
+EOF
+
+       echo "$ac_t""yes" 1>&6
+    ;;
+    UNIX_System_V\ 4.0)
+       cat >> confdefs.h <<\EOF
+#define USE_LOCKING 1
+EOF
+
+       echo "$ac_t""yes" 1>&6
+    ;;
+    *)
+       echo "$ac_t""no" 1>&6
+    ;;
+esac
+
+#--------------------------------------------------------------------
+#  Does va_arg(arg, long double) crash the compiler?
+#  hpux 9.04 compiler does and so does Stratus FTX (uses HP's compiler)
+#--------------------------------------------------------------------
+echo $ac_n "checking if va_arg(arg, long double) crashes the compiler""... $ac_c" 1>&6
+cat > conftest.$ac_ext <<EOF
+#line 3135 "configure"
+#include "confdefs.h"
+#include <stdarg.h>
+int main() { return 0; }
+int t() {
+long double lDblArg; va_list arg; lDblArg = va_arg(arg, long double);
+; return 0; }
+EOF
+if { (eval echo configure:3143: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  echo "$ac_t""no" 1>&6
+else
+  rm -rf conftest*
+  cat >> confdefs.h <<\EOF
+#define HAVE_VA_ARG_LONG_DOUBLE_BUG 1
+EOF
+
+   echo "$ac_t""yes" 1>&6
+fi
+rm -f conftest*
+
+
+#--------------------------------------------------------------------
+#  What's the target system?
+#--------------------------------------------------------------------
+case "$PLATFORM_CLASS" in
+    WINDOWS)   
+       SYSTEM="win32"
+    ;;
+    UNIX)      
+       SYSTEM="unix"
+    ;;
+esac
+
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs.  It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already.  You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=$NULLDEVW disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+  sed -n "s/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=\${\1='\2'}/p" \
+  >> confcache
+if test "`uname -s`" = "Windows_NT"; then
+  cat > confcache.sed <<EOF
+s/='"/='/
+s/"'}/'}/p
+EOF
+  sed -n -f confcache.sed < confcache > confcache.wnt
+  mv confcache.wnt confcache
+  rm -f confcache.sed
+fi
+if cmp -s $cache_file confcache; then
+  :
+else
+  if test -w $cache_file; then
+    echo "updating cache $cache_file"
+    cat confcache > $cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[        ]*VPATH[        ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+DEFS=-DHAVE_CONFIG_H
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+if test "`uname -s`" = "Windows_NT"; then
+  cat > $CONFIG_STATUS <<EOF
+#! $MKSROOT/sh.exe
+EOF
+else
+  cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+EOF
+fi
+cat >> $CONFIG_STATUS <<EOF
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>$NULLDEVW | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+NULLDEVW=$NULLDEVW
+if test ! -z "$CONFIG_SHELL"; then
+  CONFIG_SHELL=$CONFIG_SHELL
+fi
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+  case "\$ac_option" in
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+    exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+  -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+    echo "$CONFIG_STATUS generated by autoconf version 2.9"
+    exit 0 ;;
+  -help | --help | --hel | --he | --h)
+    echo "\$ac_cs_usage"; exit 0 ;;
+  *) echo "\$ac_cs_usage"; exit 1 ;;
+  esac
+done
+
+ac_given_srcdir=$srcdir
+ac_given_INSTALL="$INSTALL"
+
+trap 'rm -fr `echo "Makefile FastCGI.mak
+         libfcgi/Makefile libfcgi/libfcgi.mak
+         cgi-fcgi/Makefile cgi-fcgi/cgi-fcgi.mak
+         examples/Makefile  include/fcgi_config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# setup substitution commands for sed
+#
+cat > conftest.tmp <<\CEOF
+$ac_vpsub
+$extrasub
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@MKSROOT@%$MKSROOT%g
+s%@INCLUDE_PATH@%$INCLUDE_PATH%g
+s%@EXTRA_OBJS@%$EXTRA_OBJS%g
+s%@EXPORTS_PREFIX@%$EXPORTS_PREFIX%g
+s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
+s%@INSTALL_DATA@%$INSTALL_DATA%g
+s%@AR@%$AR%g
+s%@RANLIB@%$RANLIB%g
+s%@CCDEFS@%$CCDEFS%g
+s%@acaux_native_cc@%$acaux_native_cc%g
+s%@CC@%$CC%g
+s%@CPP@%$CPP%g
+s%@O@%$O%g
+s%@L@%$L%g
+s%@X@%$X%g
+s%@SHELL@%$SHELL%g
+s%@CCNOP@%$CCNOP%g
+s%@PROFILE@%$PROFILE%g
+s%@PLATFORM@%$PLATFORM%g
+s%@PLATFORM_CLASS@%$PLATFORM_CLASS%g
+s%@SUBSYSTEM@%$SUBSYSTEM%g
+s%@VERSION@%$VERSION%g
+s%@CVS_TAG@%$CVS_TAG%g
+s%@DOT_CVS_TAG@%$DOT_CVS_TAG%g
+s%@common_prefix@%$common_prefix%g
+s%@EXPORTS_ROOT@%$EXPORTS_ROOT%g
+s%@EXPORTS_COMMON@%$EXPORTS_COMMON%g
+s%@TCLLIB_DIR@%$TCLLIB_DIR%g
+s%@TCLINC_DIR@%$TCLINC_DIR%g
+s%@TCLCOMMON_DIR@%$TCLCOMMON_DIR%g
+s%@TCLBIN_DIR@%$TCLBIN_DIR%g
+s%@EXPORT_RESTRICTION@%$EXPORT_RESTRICTION%g
+s%@LIBOBJS@%$LIBOBJS%g
+s%@SYSTEM@%$SYSTEM%g
+
+CEOF
+#
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' conftest.tmp > conftest.subs
+rm -f conftest.tmp
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"Makefile FastCGI.mak
+         libfcgi/Makefile libfcgi/libfcgi.mak
+         cgi-fcgi/Makefile cgi-fcgi/cgi-fcgi.mak
+         examples/Makefile "}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%.*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  # Adjust relative srcdir, etc. for subdirectories.
+
+  # Remove last slash and all that follows it.  Not all systems have dirname.
+  ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+  if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+    # The file is in a subdirectory.
+    test ! -d "$ac_dir" && mkdir "$ac_dir"
+    ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+    # A "../" for each directory in $ac_dir_suffix.
+    ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+  else
+    ac_dir_suffix= ac_dots=
+  fi
+
+  case "$ac_given_srcdir" in
+  .)  srcdir=.
+      if test -z "$ac_dots"; then top_srcdir=.
+      else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+  /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+  *) # Relative path.
+    srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+    top_srcdir="$ac_dots$ac_given_srcdir" ;;
+  esac
+
+  case "$ac_given_INSTALL" in
+  [/$]*) INSTALL="$ac_given_INSTALL" ;;
+  *) INSTALL="$ac_dots$ac_given_INSTALL" ;;
+  esac
+  echo creating "$ac_file"
+  rm -f "$ac_file"
+  configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+  case "$ac_file" in
+  *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+  *) ac_comsub= ;;
+  esac
+  sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+s%@INSTALL@%$INSTALL%g
+" $ac_given_srcdir/$ac_file_in > ${ac_file_in}.fragin
+  ac_lines_subs=`grep -c . conftest.subs`
+  ac_max_subs_lines=12
+  cp conftest.subs conftest.tmp
+  while :
+  do
+    sed ${ac_max_subs_lines}q conftest.tmp >conftest.frag
+    sed 1,${ac_max_subs_lines}d conftest.tmp >conftest.tail
+    sed -f conftest.frag $ac_given_srcdir/${ac_file_in}.fragin \
+      >${ac_file_in}.fragout
+    rm -f ${ac_file_in}.fragin
+    mv ${ac_file_in}.fragout ${ac_file_in}.fragin
+    ac_lines_subs=`grep -c . conftest.tail`
+    if test -z "$ac_lines_subs" || \
+       test "$ac_lines_subs" -eq 0; then
+      break
+    fi
+    rm -f conftest.tmp
+    mv conftest.tail conftest.tmp
+    rm -f conftest.frag
+  done
+  mv ${ac_file_in}.fragin ${ac_file}
+  rm -f conftest.tmp
+  rm -f conftest.tail
+fi; done
+rm -f conftest.subs
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s%^\([  ]*\)#\([        ]*define[       ][      ]*\)'
+ac_dB='\([     ][      ]*\)[^  ]*%\1#\2'
+ac_dC='\3'
+ac_dD='%g'
+# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+ac_uA='s%^\([  ]*\)#\([        ]*\)undef\([    ][      ]*\)'
+ac_uB='\([     ]\)%\1#\2define\3'
+ac_uC=' '
+ac_uD='\4%g'
+# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_eA='s%^\([  ]*\)#\([        ]*\)undef\([    ][      ]*\)'
+ac_eB='$%\1#\2define\3'
+ac_eC=' '
+ac_eD='%g'
+
+CONFIG_HEADERS=${CONFIG_HEADERS-"include/fcgi_config.h"}
+for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%.*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  echo creating $ac_file
+
+  rm -f conftest.frag conftest.in conftest.out
+  cp $ac_given_srcdir/$ac_file_in conftest.in
+
+EOF
+
+# Transform confdefs.h into a sed script conftest.vals that substitutes
+# the proper values into config.h.in to produce config.h.  And first:
+# Protect against being on the right side of a sed subst in config.status.
+# Protect against being in an unquoted here document in config.status.
+rm -f conftest.vals
+cat > conftest.hdr <<\EOF
+s/[\\&%]/\\&/g
+s%[\\$`]%\\&%g
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp
+s%ac_d%ac_u%gp
+s%ac_u%ac_e%gp
+EOF
+sed -n -f conftest.hdr confdefs.h > conftest.vals
+rm -f conftest.hdr
+
+# This sed command replaces #undef with comments.  This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >> conftest.vals <<\EOF
+s%^[   ]*#[    ]*undef[        ][      ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
+EOF
+
+# Break up conftest.vals because some shells have a limit on
+# the size of here documents, and old seds have small limits too.
+# Maximum number of lines to put in a single here document.
+ac_max_here_lines=12
+
+rm -f conftest.tail
+while :
+do
+  ac_lines=`grep -c . conftest.vals`
+  # grep -c gives empty output for an empty file on some AIX systems.
+  if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
+  # Write a limited-size here document to conftest.frag.
+  echo '  cat > conftest.frag <<CEOF' >> $CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS
+  echo 'CEOF
+  sed -f conftest.frag conftest.in > conftest.out
+  rm -f conftest.in
+  mv conftest.out conftest.in
+' >> $CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail
+  rm -f conftest.vals
+  mv conftest.tail conftest.vals
+done
+rm -f conftest.vals
+
+cat >> $CONFIG_STATUS <<\EOF
+  rm -f conftest.frag conftest.h
+  echo "/* $ac_file.  Generated automatically by configure.  */" > conftest.h
+  cat conftest.in >> conftest.h
+  rm -f conftest.in
+  if cmp -s $ac_file conftest.h 2>$NULLDEVW; then
+    echo "$ac_file is unchanged"
+    rm -f conftest.h
+  else
+    rm -f $ac_file
+    mv conftest.h $ac_file
+  fi
+fi; done
+
+
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+if test "$no_create" != yes; then
+  if test "`uname -s`" = Windows_NT; then
+    ${CONFIG_SHELL-$MKSROOT/sh} < $CONFIG_STATUS || exit 1
+  else 
+    ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+  fi
+fi
+
+
+
diff --git a/configure.in b/configure.in
new file mode 100755 (executable)
index 0000000..5ba1c23
--- /dev/null
@@ -0,0 +1,126 @@
+dnl     $Id: configure.in,v 1.1 1997/09/16 15:36:24 stanleyg Exp $
+dnl
+dnl     This file is an input file used by the GNU "autoconf" program to
+dnl     generate the file "configure", which is run during the build
+dnl     to configure the system for the local environment.
+dnl
+
+AC_INIT(libfcgi/fcgiapp.c)
+#--------------------------------------------------------------------
+#  Perform common OMI configuration. This will include:
+#      1) Choosing compiler and associated build options.
+#      2) Checking for various common build associated programs.
+#      3) Determining the best build options for this platform.
+#      4) Checking for certain common header files.
+#      5) Checking for exports/global switch.
+#      6) Importing OMI dependencies (header files, libraries, and binaries).
+#       7) Checking for existence of various system libraries and routines.
+#--------------------------------------------------------------------
+OMI_CONFIG
+
+AC_CONFIG_HEADER(include/fcgi_config.h)
+AC_MSG_CHECKING(for sun_len in sys/un.h)
+AC_EGREP_HEADER(sun_len,
+   sys/un.h,
+   AC_MSG_RESULT(yes)
+   AC_DEFINE(HAVE_SOCKADDR_UN_SUN_LEN),
+   AC_MSG_RESULT(no))
+
+#--------------------------------------------------------------------
+#  What extra libraries do we need for this platform?
+#--------------------------------------------------------------------
+
+AC_CHECK_FUNCS(strerror strtol)
+
+AC_REPLACE_FUNCS(strerror strtol)
+
+#--------------------------------------------------------------------
+#       Include sys/select.h if it exists and if it supplies things
+#       that appear to be useful and aren't already in sys/types.h.
+#       This appears to be true only on the RS/6000 under AIX.  Some
+#       systems like OSF/1 have a sys/select.h that's of no use, and
+#       other systems like SCO UNIX have a sys/select.h that's
+#       pernicious.  If "fd_set" isn't defined anywhere then set a
+#       special flag.
+#--------------------------------------------------------------------
+
+AC_MSG_CHECKING(for fd_set in in sys/types.h)
+AC_TRY_LINK([#include <sys/types.h>],
+   [fd_set readMask, writeMask;],
+   AC_MSG_RESULT(yes),
+   AC_MSG_RESULT(no)
+   AC_MSG_CHECKING(for fd_mask in in sys/select.h)
+   AC_HEADER_EGREP(fd_mask,
+      sys/select.h,
+      AC_MSG_RESULT(yes)
+      AC_DEFINE(HAVE_SYS_SELECT_H),
+      AC_MSG_RESULT(no)
+      AC_DEFINE(NO_FD_SET)))
+
+AC_MSG_CHECKING(for fpos in stdio.h)
+AC_EGREP_HEADER(fpos_t,
+   stdio.h,
+   AC_MSG_RESULT(yes)
+   AC_DEFINE(HAVE_FPOS),
+   AC_MSG_RESULT(no))
+
+AC_HAVE_HEADERS(windows.h)
+AC_HAVE_HEADERS(sys/socket.h)
+AC_HAVE_HEADERS(winsock.h)
+AC_HAVE_HEADERS(netdb.h)
+AC_HAVE_HEADERS(netinet/in.h)
+AC_HAVE_HEADERS(arpa/inet.h)
+AC_HAVE_HEADERS(strings.h)
+AC_HAVE_HEADERS(sys/time.h)
+
+#--------------------------------------------------------------------
+#  Do we need cross-process locking on this platform?
+#--------------------------------------------------------------------
+AC_MSG_CHECKING("if this machine needs cross-process locking")
+case "`uname -sr`" in
+    IRIX\ 5.*) 
+       AC_DEFINE(USE_LOCKING)
+       AC_MSG_RESULT(yes)
+    ;;
+    SunOS\ 5.*)        
+       AC_DEFINE(USE_LOCKING)
+       AC_MSG_RESULT(yes)
+    ;;
+    UNIX_System_V\ 4.0)
+       AC_DEFINE(USE_LOCKING)
+       AC_MSG_RESULT(yes)
+    ;;
+    *)
+       AC_MSG_RESULT(no)
+    ;;
+esac
+
+#--------------------------------------------------------------------
+#  Does va_arg(arg, long double) crash the compiler?
+#  hpux 9.04 compiler does and so does Stratus FTX (uses HP's compiler)
+#--------------------------------------------------------------------
+AC_MSG_CHECKING(if va_arg(arg, long double) crashes the compiler)
+AC_TRY_COMPILE([#include <stdarg.h>],
+   [long double lDblArg; va_list arg; lDblArg = va_arg(arg, long double);],
+   AC_MSG_RESULT(no),
+   AC_DEFINE(HAVE_VA_ARG_LONG_DOUBLE_BUG)
+   AC_MSG_RESULT(yes))
+
+#--------------------------------------------------------------------
+#  What's the target system?
+#--------------------------------------------------------------------
+case "$PLATFORM_CLASS" in
+    WINDOWS)   
+       SYSTEM="win32"
+    ;;
+    UNIX)      
+       SYSTEM="unix"
+    ;;
+esac
+
+AC_SUBST(SYSTEM)
+AC_OUTPUT(Makefile FastCGI.mak
+         libfcgi/Makefile libfcgi/libfcgi.mak
+         cgi-fcgi/Makefile cgi-fcgi/cgi-fcgi.mak
+         examples/Makefile )
+
diff --git a/dependencies.in b/dependencies.in
new file mode 100755 (executable)
index 0000000..f819485
--- /dev/null
@@ -0,0 +1,19 @@
+#------------------------
+#
+# Start of dependencies.in
+#
+
+#
+# OMI provided dependencies
+#
+OMI_DEPENDS_ON(Tcl,V7.4)
+
+#
+# System library dependencies
+#
+OMI_NETWORK
+OMI_MATHLIB
+
+#
+# End of dependencies.in
+#------------------------
diff --git a/descrip.dfc b/descrip.dfc
new file mode 100644 (file)
index 0000000..624e1a0
--- /dev/null
@@ -0,0 +1 @@
+BuildIn libfcgi cgi-fcgi
diff --git a/doc/FCGI_Accept.3 b/doc/FCGI_Accept.3
new file mode 100644 (file)
index 0000000..42cc209
--- /dev/null
@@ -0,0 +1,128 @@
+NAME
+     FCGI_Accept, FCGI_ToFILE, FCGI_ToFcgiStream
+         - fcgi_stdio compatibility library
+
+SYNOPSIS
+     #include "fcgi_stdio.h"
+
+     int
+     FCGI_Accept(void);
+
+     FILE *
+     FCGI_ToFILE(FCGI_FILE *);
+
+     FCGI_Stream *
+     FCGI_ToFcgiStream(FCGI_FILE *);
+
+
+DESCRIPTION
+     The FCGI_Accept function accepts a new request from the HTTP server
+     and creates a CGI-compatible execution environment for the request.
+
+     If the application was invoked as a CGI program, the first
+     call to FCGI_Accept is essentially a no-op and the second
+     call returns -1.  This causes a correctly coded FastCGI Responder
+     application to run a single request and exit, giving CGI
+     behavior.
+
+     If the application was invoked as a FastCGI server, the first
+     call to FCGI_Accept indicates that the application has completed
+     its initialization and is ready to accept its first request.
+     Subsequent calls to FCGI_Accept indicate that the application has
+     completed processing its current request and is ready to accept a
+     new request.  An application can complete the current request
+     without accepting a new one by calling FCGI_Finish(3); later, when
+     ready to accept a new request, the application calls FCGI_Accept.
+
+     In completing the current request, FCGI_Accept may detect
+     errors, e.g. a broken pipe to a client who has disconnected
+     early.  FCGI_Accept ignores such errors.  An application
+     that wishes to handle such errors should explicitly call
+     fclose(stderr), then fclose(stdout); an EOF return from
+     either one indicates an error.
+
+     If the environment variable FCGI_WEB_SERVER_ADDRS is set when
+     FCGI_Accept is called, it should contain a comma-separated list
+     of IP addresses.  Each IP address is written as four decimal
+     numbers in the range [0..255] separated by decimal points.
+     (nslookup(8) translates the more familiar symbolic IP hostname
+     into this form.)  So one legal binding for this variable is
+
+         FCGI_WEB_SERVER_ADDRS=199.170.183.28,199.170.183.71
+
+     FCGI_Accept checks the peer IP address of each new connection for
+     membership in the list.  If the check fails (including the
+     possibility that the connection didn't use TCP/IP transport),
+     FCGI_Accept closes the connection and accepts another one
+     (without returning in between).
+
+     After accepting a new request, FCGI_Accept assigns new values
+     to the global variables stdin, stdout, stderr, and environ.
+     After FCGI_Accept returns, these variables have the same
+     interpretation as on entry to a CGI program.
+
+     FCGI_Accept frees any storage allocated by the previous call
+     to FCGI_Accept.  This has important consequences:
+
+         DO NOT retain pointers to the environ array or any strings
+         contained in it (e.g. to the result of calling getenv(3)),
+         since these will be freed by the next call to FCGI_Finish or
+         FCGI_Accept.
+
+         DO NOT use setenv(3) or putenv(3) to modify the environ array
+         created by FCGI_Accept, since this will either leak storage
+         or cause the next call to FCGI_Finish or FCGI_Accept to free
+         storage that should not be freed.
+
+         If your application needs to use setenv or putenv to modify
+         the environ array, it should follow this coding pattern:
+
+             char **savedEnviron, **requestEnviron;
+             int acceptStatus;
+
+             savedEnviron = environ;
+             acceptStatus = FCGI_Accept();
+             requestEnviron = environ;
+             environ = savedEnviron;
+             if(acceptStatus >= 0 && !FCGX_IsCGI()) {
+                 /*
+                  * requestEnviron points to name-value pairs in
+                  * storage allocated by FCGI_Accept.  OK to read,
+                  * not OK to retain pointers -- make copies instead.
+                  */
+             }
+             /*
+              * OK to do setenv or putenv, but beware of storage leaks!
+              */
+
+     In addition to the standard CGI environment variables, the
+     environment variable FCGI_ROLE is always set to the role
+     of the current request.  The roles currently defined are
+     RESPONDER, AUTHORIZER, and FILTER.
+
+     In the FILTER role, the additional variables FCGI_DATA_LENGTH
+     and FCGI_DATA_LAST_MOD are also defined.  See the manpage
+     FCGI_StartFilterData(3) for complete information.
+
+     The macros FCGI_ToFILE and FCGI_ToFcgiStream are provided
+     to allow escape to native functions that use the types FILE or
+     FCGI_Stream.  In the case of FILE, functions would have to
+     be separately compiled, since fcgi_stdio.h replaces the standard
+     FILE with FCGI_FILE.
+
+     
+RETURN VALUES
+     0 for successful call, -1 for error (application should exit).
+
+SEE ALSO
+     FCGI_Finish(3)
+     FCGI_StartFilterData(3)
+     FCGI_SetExitStatus(3)
+     cgi-fcgi(1)
+     nslookup(8)
+
+HISTORY
+     Copyright (c) 1996 Open Market, Inc.
+     See the file "LICENSE.TERMS" for information on usage and redistribution
+     of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+     $Id: FCGI_Accept.3,v 1.1 1997/09/16 15:36:25 stanleyg Exp $
diff --git a/doc/FCGI_Finish.3 b/doc/FCGI_Finish.3
new file mode 100644 (file)
index 0000000..44a67d7
--- /dev/null
@@ -0,0 +1,41 @@
+NAME
+     FCGI_Finish
+         - fcgi_stdio compatibility library
+
+SYNOPSIS
+     #include "fcgi_stdio.h"
+
+     void
+     FCGI_Finish(void);
+
+
+DESCRIPTION
+     The FCGI_Finish function finishes the current request from the
+     HTTP server.  The current request was started by the most recent
+     call to FCGI_Accept(3).
+
+     FCGI_Finish allows an application to interleave other activities
+     with the processing of requests.  In an extreme case, an
+     application would call FCGI_Finish to complete the current
+     request before exiting, e.g. to reclaim leaked storage.
+
+     In completing the current request, FCGI_Finish may detect
+     errors, e.g. a broken pipe to a client who has disconnected
+     early.  FCGI_Finish ignores such errors.  An application
+     that wishes to handle such errors should explicitly call
+     fclose(stderr), then fclose(stdout); an EOF return from
+     either one indicates an error.
+
+     FCGI_Finish frees any storage allocated by the most recent call
+     to FCGI_Accept.  See FCGI_Accept(3) for warnings against retaining
+     pointers to this storage.
+
+
+SEE ALSO
+     FCGI_Accept(3)
+
+HISTORY
+     Copyright (c) 1996 Open Market, Inc.
+     See the file "LICENSE.TERMS" for information on usage and redistribution
+     of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+     $Id: FCGI_Finish.3,v 1.1 1997/09/16 15:36:25 stanleyg Exp $
diff --git a/doc/FCGI_SetExitStatus.3 b/doc/FCGI_SetExitStatus.3
new file mode 100644 (file)
index 0000000..a1cc214
--- /dev/null
@@ -0,0 +1,28 @@
+NAME
+     FCGI_SetExitStatus - fcgi_stdio compatibility library
+
+SYNOPSIS
+     #include "fcgi_stdio.h"
+
+     void
+     FCGI_SetExitStatus(int status);
+
+
+DESCRIPTION
+     Sets the exit status for the current FastCGI request.
+     The exit status is the status code the request would have
+     exited with, had the request been run as a CGI program.
+
+     You can call FCGI_SetExitStatus several times during a request;
+     the last call before the request ends determines the value.
+     
+SEE ALSO
+     FCGI_Accept(3)
+     FCGI_StartFilterData(3)
+     cgi-fcgi(1)
+
+HISTORY
+     Copyright (c) 1996 Open Market, Inc.
+     See the file "LICENSE.TERMS" for information on usage and redistribution
+     of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+     $Id: FCGI_SetExitStatus.3,v 1.1 1997/09/16 15:36:25 stanleyg Exp $
diff --git a/doc/FCGI_StartFilterData.3 b/doc/FCGI_StartFilterData.3
new file mode 100644 (file)
index 0000000..9dfe909
--- /dev/null
@@ -0,0 +1,52 @@
+NAME
+     FCGI_StartFilterData - fcgi_stdio compatibility library
+
+SYNOPSIS
+     #include "fcgi_stdio.h"
+
+     int
+     FCGI_StartFilterData(void);
+
+
+DESCRIPTION
+     Enables a FastCGI Filter application to begin reading its filter
+     input data from stdin.
+
+     In order to call FCGI_StartFilterData, the FastCGI
+     application should have been invoked in the Filter role
+     (getenv("FCGI_ROLE") == "FILTER"), and should have read
+     stdin to EOF, consuming the entire FCGI_STDIN data stream.
+     The call to FCGI_StartFilterData positions stdin at the
+     start of FCGI_DATA.
+
+     If the preconditions are not met (e.g. the application has
+     not read stdin to EOF), FCGI_StartFilterData returns
+     a negative result, and the application will get EOF on attempts
+     to read from stdin.
+
+     The application can determine the number of bytes available
+     on FCGI_DATA by performing atoi(getenv("FCGI_DATA_LENGTH")).
+     If fewer than this many bytes are delievered on stdin after
+     calling FCGI_StartFilterData, the application should perform
+     an application-specific error response.  If the application
+     normally makes an update, most likely it should abort the update.
+
+     The application can determine last modification time of the
+     filter input data by performing getenv("FCGI_DATA_LAST_MOD").
+     This allows applications to perform caching based on last
+     modification time.
+
+     
+RETURN VALUES
+     0 for successful call, < 0 for error.
+
+SEE ALSO
+     FCGI_Accept(3)
+     FCGI_SetExitStatus(3)
+     cgi-fcgi(1)
+
+HISTORY
+     Copyright (c) 1996 Open Market, Inc.
+     See the file "LICENSE.TERMS" for information on usage and redistribution
+     of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+     $Id: FCGI_StartFilterData.3,v 1.1 1997/09/16 15:36:26 stanleyg Exp $
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644 (file)
index 0000000..35e2ef7
--- /dev/null
@@ -0,0 +1,15 @@
+.SUFFIXES:
+.SUFFIXES: .htm .gut
+
+all:
+       make *.htm
+
+DIR     = /omi/httpd/www-dev/root
+TOOLS   = ${DIR}/fastcgi
+GUTSCSH = ${TOOLS}/fcgi-pa.csh
+
+.gut.htm:
+       ${GUTSCSH} $*.gut > $*.htm
+
+clean:
+       rm -f *~
diff --git a/doc/cgi-fcgi.1 b/doc/cgi-fcgi.1
new file mode 100644 (file)
index 0000000..ae46d37
--- /dev/null
@@ -0,0 +1,113 @@
+NAME
+     cgi-fcgi   - bridge from CGI to FastCGI
+
+SYNOPSIS
+     cgi-fcgi -f cmdPath
+     cgi-fcgi -bind  -connect connName
+     cgi-fcgi -start -connect connName appPath [nServers]
+     cgi-fcgi -connect connName appPath [nServers]
+
+DESCRIPTION
+     cgi-fcgi is a CGI/1.1 program that communicates with an
+     already-running FastCGI application in order to respond to an
+     HTTP request.  cgi-fcgi is also capable of starting a FastCGI
+     application.
+
+     When you invoke cgi-fcgi as
+
+         cgi-fcgi -f cmdPath
+
+     then cgi-fcgi opens the file at cmdPath and reads its
+     arguments from that file.  cgi-fcgi will skip lines
+     that begin with the comment character #.  The first
+     non-comment line should contain valid arguments in
+     one of the other three forms.
+
+     The -f form of cgi-fcgi is designed for Unix systems
+     whose exec(2) family of system calls supports the execution of
+     command interpreter files.  For instance, if a file with
+     execute permission contains the text
+
+         #! /bin/cgi-fcgi -f
+         -connect /httpd/root/sock/app /httpd/root/bin/app
+
+     the effect is the same as executing
+
+         /bin/cgi-fcgi -connect /httpd/root/sock/app /httpd/root/bin/app
+
+     When you invoke cgi-fcgi as
+
+         cgi-fcgi -bind -connect connName
+
+     the connName argument is either the path name of a Unix domain
+     listening socket or a host:port pair.  If connName contains
+     a colon, it is assumed to be host:port.  cgi-fcgi performs
+     a connect(2) using connName.  If the connect succeeds, cgi-fcgi
+     forwards the CGI environment variables and stdin data to the
+     FastCGI application, and forwards the stdout and stderr data from
+     the application to cgi-fcgi's stdout (most likely connected to
+     a Web server).  When the FastCGI application signals the end of
+     its response, cgi-fcgi flushes its buffers and
+     exits, and the Web server completes the http response.
+
+     When you invoke cgi-fcgi as
+
+         cgi-fcgi -start -connect connName appPath [nServers]
+
+     then cgi-fcgi performs the function of starting one or more
+     FastCGI application processes.  The connName argument specifies
+     either the path name of the Unix domain listening socket that
+     cgi-fcgi will create, or is "localhost:NNN" where NNN is the port
+     number of the TCP/IP listening socket that cgi-fcgi will create
+     on the local machine.  (cgi-fcgi will not create processes
+     on remote machines.)  After cgi-fcgi creates the listening socket,
+     it forks nServers copies of a process running the executable file
+     appPath.  If nServers is omitted, the effect is as if the value "1"
+     had been specified.  The processes share the single listening socket.
+
+     When you invoke cgi-fcgi as
+
+         cgi-fcgi -connect connName appPath [nServers]
+    
+     cgi-fcgi performs -bind and then, if necssary, performs -start
+     and repeats the -bind.  That is, cgi-fcgi first operates as if
+     the command had been
+
+         cgi-fcgi -bind -connect connName
+
+     If the connect fails, cgi-fcgi tries
+
+         cgi-fcgi -start -connect connName appPath [nServers]
+
+     and finally retries
+
+         cgi-fcgi -bind -connect connName
+
+     In this form, cgi-fcgi does not support TCP/IP connections.
+     
+ENVIRONMENT VARIABLES
+     The usual CGI ones, but they are not interpreted by cgi-fcgi.
+
+SEE ALSO
+     FGCI_accept(3)
+
+BUGS
+     cgi-fcgi doesn't generate useful HTTP responses in case of error,
+     and it generates no response at all when run as start-fcgi.
+
+     On Digital UNIX 3.0 systems the implementation of Unix Domain
+     sockets does not work when such sockets are stored on NFS file
+     systems.  Symptom: cgi-fcgi may core dump or may exit with
+     status 38.  Work-around: store sockets in local file systems
+     (/tmp often works) or use TCP/IP.
+
+     On AIX systems the implementation of listening sockets
+     does not support socket sharing, and the standard FastCGI
+     application libraries can't synchronize access to AIX listening
+     sockets.  Work-around: Don't use the nServers argument on AIX.
+
+HISTORY
+     Copyright (c) 1996 Open Market, Inc.
+     See the file "LICENSE.TERMS" for information on usage and redistribution
+     of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+     $Id: cgi-fcgi.1,v 1.1 1997/09/16 15:36:26 stanleyg Exp $
diff --git a/doc/fastcgi-prog-guide/ap_guida.htm b/doc/fastcgi-prog-guide/ap_guida.htm
new file mode 100755 (executable)
index 0000000..fa89933
--- /dev/null
@@ -0,0 +1,167 @@
+<html><head><title></title></head>
+<body bgcolor=#ffffff>
+<a href="cover.htm">[Top]</a> <a href="apaman.htm">[Prev]</a> [Next] [Bottom]
+<hr><br>
+<a name="801">
+<center><h1> Index<br></h1></center>
+</a><a name="1384">
+<center><h2>A</h2></center>
+</a><dl>
+<a name="1388">
+<dt><dd> <a href="ch4tcl.htm#4835">applications in Tcl 19</a>
+</a><a name="1390">
+<dt><dd> <a href="ch1intro.htm#8428">Authorizer applications 4</a>
+</a><dl>
+<a name="1392">
+<dt><dd> <a href="ch1intro.htm#9483">environment variables 6</a>
+</a><a name="1394">
+<dt><dd> <a href="ch1intro.htm#8437">stdin and stderr 4</a>
+</a></dl>
+</dl>
+<a name="1396">
+<center><h2>C</h2></center>
+</a><dl>
+<a name="1399">
+<dt><dd> <a href="ch2c.htm#917">C language, writing FastCGI applications in 11</a>
+</a></dl>
+<a name="1401">
+<center><h2>E</h2></center>
+</a><dl>
+<a name="1404">
+<dt><dd> environment variables
+</a><dl>
+<a name="1405">
+<dt><dd> <a href="ch1intro.htm#9483">differences from CGI 6</a>
+</a><a name="1407">
+<dt><dd> <a href="ch1intro.htm#8431">returned from Authorizer applications 4</a>
+</a></dl>
+<a name="1409">
+<dt><dd> examples
+</a><dl>
+<a name="1410">
+<dt><dd> <a href="ch2c.htm#4182">responder application in C 13</a>
+</a><a name="1412">
+<dt><dd> <a href="ch3perl.htm#5002">responder application in perl 18</a>
+</a><a name="1414">
+<dt><dd> <a href="ch4tcl.htm#4343">responder application in Tcl 20</a>
+</a></dl>
+<a name="1416">
+<dt><dd> <a href="apaman.htm#95796">exit status, of FastCGI application 24</a>
+</a></dl>
+<a name="1418">
+<center><h2>F</h2></center>
+</a><dl>
+<a name="1420">
+<dt><dd> FastCGI
+</a><dl>
+<a name="1421">
+<dt><dd> <a href="ch4tcl.htm#4835"> 19</a>
+</a><a name="1423">
+<dt><dd> <a href="ch2c.htm#917">applications in C 11</a>
+</a><a name="1425">
+<dt><dd> <a href="ch3perl.htm#917">applications in Perl 17</a>
+</a><a name="1427">
+<dt><dd> <a href="ch1intro.htm#7995">differences from CGI 1</a>
+</a></dl>
+<a name="1429">
+<dt><dd> <a href="apaman.htm#95732">FCGI_DATA_LAST_MOD 23</a>
+</a><a name="1431">
+<dt><dd> <a href="apaman.htm#95731">FCGI_DATA_LENGTH 23</a>
+</a><a name="1433">
+<dt><dd> <a href="ch1intro.htm#9488">FCGI_DATA_LENGTH (in Filter applications) 6</a>
+</a><a name="1435">
+<dt><dd> <a href="ch1intro.htm#9490">FCGI_ROLE 6, </a><a href="apaman.htm#95661">22</a>
+</a><a name="1438">
+<dt><dd> <a href="apaman.htm#95846">FCGI_SetExitStatus 24</a>
+</a><a name="1440">
+<dt><dd> <a href="apaman.htm#95309">FCGI_StartFilterData 22</a>
+</a><a name="1442">
+<dt><dd> <a href="ch2c.htm#4199">fcgi_stdio library 11</a>
+</a><dl>
+<a name="1444">
+<dt><dd> <a href="ch2c.htm#4629">location of 15</a>
+</a><a name="1446">
+<dt><dd> <a href="apaman.htm#95882">manpages for 21</a>
+</a></dl>
+<a name="1448">
+<dt><dd> <a href="apaman.htm#95663">FCGI_ToFcgiStream 22</a>
+</a><a name="1450">
+<dt><dd> <a href="apaman.htm#95663">FCGI_ToFILE 22</a>
+</a><a name="1452">
+<dt><dd> <a href="ch2c.htm#4199">fcgiapp library 11</a>
+</a><a name="1454">
+<dt><dd> <a href="ch1intro.htm#9486">FILE_LAST_MOD (in Filter applications) 6</a>
+</a><a name="1456">
+<dt><dd> Filter applications
+</a><dl>
+<a name="1457">
+<dt><dd> <a href="apaman.htm#95732">last modification time 23</a>
+</a><a name="1459">
+<dt><dd> <a href="apaman.htm#95728">reading from stdin 23</a>
+</a></dl>
+</dl>
+<a name="1461">
+<center><h2>G</h2></center>
+</a><dl>
+<a name="1463">
+<dt><dd> <a href="ch2c.htm#4785">Great Circle (C garbage collector) 16</a>
+</a></dl>
+<a name="1465">
+<center><h2>I</h2></center>
+</a><dl>
+<a name="1468">
+<dt><dd> <a href="ch1intro.htm#9480">Initial Environment Variables 5</a>
+</a></dl>
+<a name="1470">
+<center><h2>M</h2></center>
+</a><dl>
+<a name="1475">
+<dt><dd> <a href="apaman.htm#95882">manpages 21</a>
+</a><a name="1477">
+<dt><dd> <a href="ch2c.htm#4190">memory leaks 16</a>
+</a></dl>
+<a name="1479">
+<center><h2>P</h2></center>
+</a><dl>
+<a name="1483">
+<dt><dd> Perl
+</a><dl>
+<a name="1484">
+<dt><dd> <a href="ch3perl.htm#917">writing FastCGI applications in 17</a>
+</a></dl>
+<a name="1486">
+<dt><dd> <a href="ch2c.htm#4785">Purify (for checking storage leaks) 16</a>
+</a></dl>
+<a name="1488">
+<center><h2>R</h2></center>
+</a><dl>
+<a name="1491">
+<dt><dd> <a href="ch1intro.htm#9477">response loop 5</a>
+</a><dl>
+<a name="1493">
+<dt><dd> <a href="ch2c.htm#4202">in C 12</a>
+</a><a name="1495">
+<dt><dd> <a href="ch3perl.htm#4243">in Perl 17</a>
+</a><a name="1497">
+<dt><dd> <a href="ch4tcl.htm#4228">in TCL 19</a>
+</a></dl>
+</dl>
+
+<hr><br>
+<a href="cover.htm">[Top]</a> <a href="apaman.htm">[Prev]</a> [Next] [Bottom]
+<hr><br>
+
+
+
+<!-- This file was created with Quadralay WebWorks Publisher 3.0.3 -->
+<!-- -->
+<!-- For more information on how this document, and how the rest of -->
+<!-- this server was created, email yourEmail@xyzcorp.com -->
+<!-- -->
+<!-- Last updated: 04/15/96 08:00:22 -->
+
+</body>
+</html>
diff --git a/doc/fastcgi-prog-guide/ap_guide.htm b/doc/fastcgi-prog-guide/ap_guide.htm
new file mode 100755 (executable)
index 0000000..f74dd52
--- /dev/null
@@ -0,0 +1,132 @@
+<html><head><title></title></head>
+<body bgcolor=#ffffff>
+<a href="cover.htm">[Top]</a> <a href="cover.htm">[Prev]</a> <a href="ch1intro.htm">[Next]</a> <a href="ap_guida.htm">[Bottom]</a>
+<hr><br>
+<a name="4878">
+<h4>1.  <a href="ch1intro.htm#9432">The Fast Common Gateway Interface   1</a></h4>
+</a><dl>
+<a name="4880">
+<dt><dd> <a href="ch1intro.htm#8485">Advantages of FastCGI      1</a>
+</a><dl>
+<a name="4882">
+<dt><dd> <a href="ch1intro.htm#8396">Long-lived Applications    1</a>
+</a><a name="4884">
+<dt><dd> <a href="ch1intro.htm#8445">Separating Application and Server          2</a>
+</a><a name="4886">
+<dt><dd> <a href="ch1intro.htm#8406">FastCGI "Roles"    2</a>
+</a></dl>
+<a name="4888">
+<dt><dd> <a href="ch1intro.htm#4207">Writing FastCGI Applications       4</a>
+</a><dl>
+<a name="4890">
+<dt><dd> <a href="ch1intro.htm#9469">Code Structure     5</a>
+</a><a name="4892">
+<dt><dd> <a href="ch1intro.htm#9480">Initial Environment Variables      5</a>
+</a><a name="4894">
+<dt><dd> <a href="ch1intro.htm#9785">Per-Request Environment Variables          6</a>
+</a><a name="4896">
+<dt><dd> <a href="ch1intro.htm#9048">Building FastCGI Applications in C         6</a>
+</a><a name="4898">
+<dt><dd> <a href="ch1intro.htm#9570">Building FastCGI Applications in Perl      7</a>
+</a><a name="4900">
+<dt><dd> <a href="ch1intro.htm#9562">Building FastCGI Applications in Tcl       7</a>
+</a></dl>
+<a name="4902">
+<dt><dd> <a href="ch1intro.htm#8360">Implementation Details     7</a>
+</a><dl>
+<a name="4904">
+<dt><dd> <a href="ch1intro.htm#7874">The fcgi_stdio Library: I/O Compatibility          9</a>
+</a><a name="4906">
+<dt><dd> <a href="ch1intro.htm#9678">The fcgi_stdio Library: Binary compatibility       10</a>
+</a></dl>
+</dl>
+<a name="4908">
+<h4>2.  <a href="ch2c.htm#3659">Developing FastCGI  Applications in C   11</a></h4>
+</a><dl>
+<a name="4910">
+<dt><dd> <a href="ch2c.htm#5371">The I/O Libraries      11</a>
+</a><a name="4912">
+<dt><dd> <a href="ch2c.htm#5847">Code Structure         12</a>
+</a><a name="4914">
+<dt><dd> <a href="ch2c.htm#5373">Example 1: TinyFastCGI         12</a>
+</a><a name="4916">
+<dt><dd> <a href="ch2c.htm#4171">Example 2: Prime Number Generator      13</a>
+</a><a name="4918">
+<dt><dd> <a href="ch2c.htm#5151">Building       15</a>
+</a><a name="4920">
+<dt><dd> <a href="ch2c.htm#4190">Memory Leaks   16</a>
+</a></dl>
+<a name="4922">
+<h4>3.  <a href="ch3perl.htm#3659">Developing FastCGI  Applications in Perl     17</a></h4>
+</a><dl>
+<a name="4924">
+<dt><dd> <a href="ch3perl.htm#4183">Getting Started     17</a>
+</a><a name="4926">
+<dt><dd> <a href="ch3perl.htm#5002">Example: TinyFastCGI        18</a>
+</a></dl>
+<a name="4928">
+<h4>4.  <a href="ch4tcl.htm#3659">Developing FastCGI  Applications in Tcl       19</a></h4>
+</a><dl>
+<a name="4930">
+<dt><dd> <a href="ch4tcl.htm#4222">Getting Started      19</a>
+</a><a name="4932">
+<dt><dd> <a href="ch4tcl.htm#4853">Example: TinyFastCGI         20</a>
+</a></dl>
+<a name="4934">
+<h4>A.  <a href="apaman.htm#3601">FastCGI  Reference Pages     21</a></h4>
+</a><dl>
+<a name="4936">
+<dt><dd> <a href="apaman.htm#95860">FCGI_Accept (3)     21</a>
+</a><dl>
+<a name="4938">
+<dt><dd> <a href="apaman.htm#95861">Name        21</a>
+</a><a name="4940">
+<dt><dd> <a href="apaman.htm#95652">Synopsis    21</a>
+</a><a name="4942">
+<dt><dd> <a href="apaman.htm#95656">Description         21</a>
+</a><a name="4944">
+<dt><dd> <a href="apaman.htm#95664">Return Values       22</a>
+</a></dl>
+<a name="4946">
+<dt><dd> <a href="apaman.htm#95309">FCGI_StartFilterData (3)    22</a>
+</a><dl>
+<a name="4948">
+<dt><dd> <a href="apaman.htm#95310">Name        22</a>
+</a><a name="4950">
+<dt><dd> <a href="apaman.htm#95312">Synopsis    22</a>
+</a><a name="4952">
+<dt><dd> <a href="apaman.htm#95315">Description         23</a>
+</a><a name="4954">
+<dt><dd> <a href="apaman.htm#95733">Return Values       23</a>
+</a><a name="4956">
+<dt><dd> <a href="apaman.htm#95323">Example     23</a>
+</a></dl>
+<a name="4958">
+<dt><dd> <a href="apaman.htm#95846">FCGI_SetExitStatus(3)       24</a>
+</a><dl>
+<a name="4960">
+<dt><dd> <a href="apaman.htm#95793">Name        24</a>
+</a><a name="4962">
+<dt><dd> <a href="apaman.htm#95786">Synopsis    24</a>
+</a><a name="4964">
+<dt><dd> <a href="apaman.htm#95788">Description         24</a>
+</a></dl>
+</dl>
+
+<hr><br>
+<a href="cover.htm">[Top]</a> <a href="cover.htm">[Prev]</a> <a href="ch1intro.htm">[Next]</a> <a href="ap_guida.htm">[Bottom]</a>
+<hr><br>
+
+<!-- This file was created with Quadralay WebWorks Publisher 3.0.3 -->
+<!-- -->
+<!-- For more information on how this document, and how the rest of -->
+<!-- this server was created, email yourEmail@xyzcorp.com -->
+<!-- -->
+<!-- Last updated: 04/15/96 08:00:12 -->
+
+</body>
+</html>
diff --git a/doc/fastcgi-prog-guide/apaman.htm b/doc/fastcgi-prog-guide/apaman.htm
new file mode 100755 (executable)
index 0000000..464dd3f
--- /dev/null
@@ -0,0 +1,176 @@
+<html><head><title></title></head>
+<body bgcolor=#ffffff>
+<a href="cover.htm">[Top]</a> <a href="ch4tcl.htm">[Prev]</a> <a href="ap_guida.htm">[Next]</a> <a href="ap_guida.htm">[Bottom]</a>
+<hr><br>
+<a name="3601">
+<center><h1>A FastCGI <br>Reference Pages</h1></center>
+</a><a name="95882">
+This appendix contains reference pages for the following FastCGI routines from the <code>fcgi_stdio</code> library:<p>
+</a><ul><a name="95884">
+<li><code>FCGI_Accept</code>
+</a><a name="95885">
+<li><code>FCGI_Start_Filter_Data</code>
+</a><a name="95859">
+<li><code>FCGI_SetExitStatus</code>
+</a></ul><a name="95860">
+<h1> FCGI_Accept (3)</h1>
+</a><a name="95861">
+<h2> Name</h2>
+</a><a name="95637">
+<code>FCGI_Accept, FCGI_ToFILE, FCGI_ToFcgiStream</code> - fcgi_stdio compatibility library<p>
+</a><a name="95652">
+<h2> Synopsis</h2>
+</a><pre><a name="95669">
+#include &lt;fcgi_stdio.h&gt;
+</a>
+<a name="95653">
+int <br>FCGI_Accept(void);
+</a>
+<a name="95654">
+FILE * <br>FCGI_ToFILE(FCGI_FILE *);
+</a>
+<a name="95655">
+FCGI_Stream * <br>FCGI_ToFcgiStream(FCGI_FILE *);
+</a>
+</pre><a name="95656">
+<h2> Description </h2>
+</a><a name="95683">
+The FCGI_Accept function accepts a new request from the HTTP server and creates a CGI-compatible execution environment for the request.<p>
+</a><a name="95657">
+If the application was invoked as a CGI program, the first call to FCGI_Accept is essentially a no-op and the second call returns -1. This causes a correctly coded FastCGI application to run a single request and exit, giving CGI behavior.<p>
+</a><a name="95658">
+If the application was invoked as a FastCGI server, the first call to FCGI_Accept indicates that the application has completed its initialization and is ready to accept its first request. Subsequent calls to FCGI_Accept indicate that the application has completed processing its current request and is ready to accept a new request.<p>
+</a><a name="95659">
+In completing the current request, FCGI_Accept may detect errors, such as a broken pipe to a client who has disconnected early. FCGI_Accept ignores such errors. An application that wishes to handle such errors should explicitly call fclose(stderr), then fclose(stdout); an EOF return from either one indicates an error.<p>
+</a><a name="95660">
+After accepting a new request, FCGI_Accept assigns new values to the global variables stdin, stdout, stderr, and environ. After FCGI_Accept returns, these variables have the same interpretation as on entry to a CGI program.<p>
+</a><a name="95661">
+In addition to the standard CGI environment variables, the environment variable <code>FCGI_ROLE</code> is always set to the role of the current request. The roles currently defined are <code>RESPONDER, AUTHORIZER</code>, and <code>FILTER</code>.<p>
+</a><a name="95662">
+In the <code>FILTER</code> role, the additional variables <code>FCGI_DATA_LENGTH</code> and <code>FCGI_DATA_LAST_MOD</code> are also defined. See <code>FCGI_StartFilterData</code><code>(3</code>) for complete information.<p>
+</a><a name="95663">
+The macros <code>FCGI_ToFILE</code> and <code>FCGI_ToFcgiStream</code> are provided to allow escape to native functions that use the types <code>FILE</code> or <code>FCGI_Stream</code>. In the case of <code>FILE</code>, functions would have to be separately compiled, since <code>fcgi_stdio.h</code> replaces the standard <code>FILE</code> with <code>FCGI_FILE</code>.<p>
+</a><a name="95664">
+<h2> Return Values</h2>
+</a><a name="95686">
+0 for successful call, -1 for error (application should exit).<p>
+</a><a name="95309">
+<h1> FCGI_StartFilterData (3)</h1>
+</a><a name="95310">
+<h2> Name</h2>
+</a><a name="95311">
+<code>FCGI_StartFilterData</code> -<code>fcgi_stdio</code> compatibility library<p>
+</a><a name="95312">
+<h2> Synopsis</h2>
+</a><pre><a name="95313">
+#include &lt;fcgi_stdio.h&gt;
+</a>
+<a name="95314">
+int FCGI_StartFilterData(void)
+</a>
+</pre><a name="95315">
+<h2> Description</h2>
+</a><a name="95728">
+Enables a FastCGI Filter application to begin reading its filter input data from <code>stdin</code>.<p>
+</a><a name="95729">
+In order to call <code>FCGI_StartFilterData</code>, the FastCGI application should have been invoked in the filter role (<code>getenv("FCGI_ROLE") == "FILTER"</code>), and should have read <code>stdin</code> to EOF, consuming the entire <code>FCGI_STDIN</code> data stream. The call to <code>FCGI_StartFilterData</code> positions stdin at the start of <code>FCGI_DATA</code>.<p>
+</a><a name="95730">
+If the preconditions are not met (e.g., the application has not read <code>stdin</code> to EOF), <code>FCGI_StartFilterData</code> returns a negative result, and the application will get EOF on attempts to read from <code>stdin</code>.<p>
+</a><a name="95731">
+The application can determine the number of bytes available on <code>FCGI_DATA</code> by performing <code>atoi(getenv("FCGI_DATA_LENGTH")</code>. If fewer than this many bytes are delivered on <code>stdin</code> after calling <code>FCGI_StartFilterData</code>, the application should perform an application-specific error response. If the application normally makes an update, most likely it should abort the update.<p>
+</a><a name="95732">
+The application can determine last modification time of the filter input data by performing <code>getenv("FCGI_DATA_LAST_MOD").</code> This allows applications to perform caching based on last modification time.<p>
+</a><a name="95733">
+<h2> Return Values</h2>
+</a><a name="95322">
+Returns 0 on success and a negative integer on failure. <p>
+</a><a name="95323">
+<h2> Example</h2>
+</a><a name="95363">
+The following example reads in all the client data, but ignores it. Then, the code calls <code>FCGI_StartFilterData</code>. Finally, the code reads in the file to be filtered and simply echos it back to the client. <p>
+</a><pre><a name="95324">
+while (FCGI_Accept() &gt;= 0) {
+</a>
+<a name="95325">
+...
+</a>
+<a name="95364">
+ /* Read data passed by client. */
+</a>
+<a name="95358">
+  while (getchar () != OF) 
+</a>
+<a name="95935">
+{
+</a>
+<a name="95930">
+}
+</a>
+<a name="95359">
+
+</a>
+<a name="95367">
+ /* Adjust standard input stream. */
+</a>
+<a name="95366">
+  status = FCGI_StartFilterData();
+</a>
+<a name="95369">
+
+</a>
+<a name="95360">
+ /* Read in filter data and echo it back to client. */
+</a>
+<a name="95368">
+  while ((len = fread(tempBuffer, 1, 1024, stdin)) &gt; 0) 
+</a>
+<a name="95361">
+    fwrite(tempBuffer, 1, len, stdout);
+</a>
+<a name="95844">
+
+</a>
+<a name="95845">
+} /* End FCGI_Accept loop */
+</a>
+</pre><a name="95846">
+<h1> FCGI_SetExitStatus(3)</h1>
+</a><a name="95793">
+<h2> Name </h2>
+</a><a name="95794">
+<code>FCGI_SetExitStatus</code> - <code>fcgi_stdio</code> compatibility library<p>
+</a><a name="95786">
+<h2> Synopsis </h2>
+</a><pre><a name="95795">
+#include &lt;fcgi_stdio.h&gt;
+</a>
+<a name="95787">
+void FCGI_SetExitStatus(int status);
+</a>
+</pre><a name="95788">
+<h2> Description </h2>
+</a><a name="95796">
+Sets the exit status for the current FastCGI request. The exit status is the status code the request would have exited with, had the request been run as a CGI program.<p>
+</a><a name="95789">
+You can call <code>FCGI_SetExitStatus</code> several times during a request; the last call before the request ends determines the value.<p>
+</a><a name="95797">
+<p>
+</a>
+<hr><br>
+<a href="cover.htm">[Top]</a> <a href="ch4tcl.htm">[Prev]</a> <a href="ap_guida.htm">[Next]</a> <a href="ap_guida.htm">[Bottom]</a>
+<hr><br>
+
+
+<!-- This file was created with Quadralay WebWorks Publisher 3.0.3 -->
+<!-- -->
+<!-- For more information on how this document, and how the rest of -->
+<!-- this server was created, email yourEmail@xyzcorp.com -->
+<!-- -->
+<!-- Last updated: 04/15/96 08:00:20 -->
+
+</body>
+</html>
diff --git a/doc/fastcgi-prog-guide/ch1inta1.gif b/doc/fastcgi-prog-guide/ch1inta1.gif
new file mode 100755 (executable)
index 0000000..4111cc6
Binary files /dev/null and b/doc/fastcgi-prog-guide/ch1inta1.gif differ
diff --git a/doc/fastcgi-prog-guide/ch1intra.gif b/doc/fastcgi-prog-guide/ch1intra.gif
new file mode 100755 (executable)
index 0000000..5565f98
Binary files /dev/null and b/doc/fastcgi-prog-guide/ch1intra.gif differ
diff --git a/doc/fastcgi-prog-guide/ch1intro.htm b/doc/fastcgi-prog-guide/ch1intro.htm
new file mode 100755 (executable)
index 0000000..40f9216
--- /dev/null
@@ -0,0 +1,243 @@
+<html><head><title></title></head>
+<body bgcolor=#ffffff>
+<a href="cover.htm">[Top]</a> <a href="ap_guide.htm">[Prev]</a> <a href="ch2c.htm">[Next]</a> <a href="ap_guida.htm">[Bottom]</a>
+<hr><br>
+<a name="9432">
+<center><h1>1 The Fast Common<br>Gateway Interface</h1></center>
+</a><a name="7982">
+The Fast Common Gateway Interface (FastCGI) is an enhancement to the existing CGI (Common Gateway Interface), which is a standard for interfacing external applications with Web servers. <p>
+</a><a name="8373">
+FastCGI is a proposed open standard and we expect both free and commercial Web servers to support it. FastCGI is included in Open Market WebServer and Secure WebServer, versions 2.0 and greater.<p>
+</a><a name="8485">
+<h1> Advantages of FastCGI</h1>
+</a><a name="8369">
+FastCGI extends and enhances the CGI model in several ways:<p>
+</a><ul><a name="7832">
+<li>FastCGI enables applications to persist between client requests, eliminating application start up overhead and allowing the application to maintain state between client calls.
+</a><a name="7995">
+<li>FastCGI enables applications to reside on remote systems (rather than having to reside on the same system as the Web server)
+</a><a name="7997">
+<li>FastCGI enables additional flexibility in application functionality, with explicit support for applications that do client authentication and filtering of input.
+</a></ul><a name="8396">
+<h2> Long-lived Applications</h2>
+</a><a name="8458">
+CGI applications are ephemeral and short-lived: each time a client requests a CGI application, the server asks the operating system to spawn a new CGI process. After the CGI process satisfies the request, the server kills it. The server spawns and subsequently kills a new process for each client request. <p>
+</a><a name="8459">
+FastCGI applications are long-lived, and can persist between client calls. The server spawns the FastCGI process once and it continues to run and satisfy client requests until it is explicitly terminated. You can also ask the Web server to start multiple copies of a FastCGI application, if you expect that concurrent processing will improve the application's performance. <p>
+</a><a name="5761">
+Long-lived applications have two important advantages over short-lived applications:<p>
+</a><ul><a name="7138">
+<li>A short-lived application pays start up overhead on every request; a long-lived application spreads the overhead over many requests. For an application that has a heavy start up cost, such as opening a database, doing initialization on every call can be very inefficient. Reinitializing for every client is also very inefficient for Perl programs, where the interpreter reads through the entire program before executing any of it.
+</a><a name="9204">
+<li>A long-lived application can cache information in memory between requests, allowing it to respond more quickly to later requests.
+</a></ul><a name="8733">
+FastCGI is not the only way to get a long-lived application on the Web, however. For example, there are many existing search engines that are implemented as long-lived applications.<p>
+</a><a name="8734">
+In most cases, these applications rely on customized Web servers. In other words, since most Web servers do not support long-lived applications, a programmer must code this support into a Web server. This approach requires a tremendous amount of work and also ties the application to a particular server.<p>
+</a><a name="8735">
+Another way to get a long-lived application is to write code that calls routines from the Web server's API. This alternative involves a lot of extra coding, ties the application to a particular Web server, and introduces problems of maintainability, scalability, and security.<p>
+</a><a name="8736">
+We believe that FastCGI is the most general and flexible strategy for building long-lived Web applications.<p>
+</a><a name="8445">
+<h2> Separating Application and Server</h2>
+</a><a name="8446">
+CGI applications must run on the same node as the Web server; FastCGI applications can run on any node that can be reached from your Web server using TCP/IP protocols. For example, you might want to run the FastCGI application on a high-speed computer server or database engine, and run the Web server on a different node.<p>
+</a><a name="8406">
+<h2> FastCGI "Roles"</h2>
+</a><a name="8777">
+CGI and FastCGI applications are effective ways to allow an application to act as an extension to the Web server. CGI provides no explicit support for different kinds of applications: under CGI, every application receives an HTTP request, does something with it, and generates an HTTP response. FastCGI provides explicit support for several common "roles" that applications can play. <p>
+</a><a name="8769">
+The three roles supported by the WebServer 2.0 are: <p>
+</a><ul><a name="8409">
+<li>Responder
+</a><a name="8410">
+<li>Filter
+</a><a name="8411">
+<li>Authorizer
+</a></ul><a name="8412">
+<h3> Responder Applications</h3>
+</a><a name="8679">
+A <em>responder</em> application is the most basic kind of FastCGI application: it receives the information associated with an HTTP request and generates an HTTP response. Responder is the role most similar to traditional CGI programming, and most FastCGI applications are responders.<p>
+</a><a name="8680">
+<h3> Filter Applications</h3>
+</a><a name="8681">
+A <em>filter</em> FastCGI application receives the information associated with an HTTP request, plus an extra stream of data from a file stored on the Web server, and generates a "filtered" version of the data stream as an HTTP response. <p>
+</a><a name="8421">
+With filter applications, the system administrator maps a particular MIME-type to a particular filter FastCGI application. When a client requests a URL with that MIME-type, the Web server invokes the filter application, which processes the file at the specified URL and sends a response (usually HTML text) back to the client.<p>
+</a><a name="8422">
+For example, suppose you write a filter FastCGI application that converts SGML text to HTML, and map the extension .sgml (MIME-type SGML) to your filter FastCGI application. Now, suppose that a user requests the following URL:<p>
+</a><pre><a name="8423">
+/www.aerjug.com/docs/chap1.sgml
+</a>
+</pre><a name="8424">
+Given this URL, the Web server passes <code>chap1.sgml</code> as input to your filter FastCGI application, which processes <code>chap1.sgml</code> and returns an HTML version of it to the requesting client.<p>
+</a><a name="8425">
+<h3> Authorizer Applications</h3>
+</a><a name="8426">
+An <em>authorizer</em> FastCGI application receives the information in an HTTP request header and generates a decision whether to authorize the request.<p>
+</a><a name="8428">
+To mark a FastCGI application as having the authorizer role, the system administrator names the application inside the server configuration file, using a directive called <code>AuthorizeRegion</code>. (See the Open Market Web Server manual for information on server configuration directives.)<p>
+</a><a name="8429">
+When a client requests a URL that meets the <code>AuthorizeRegion </code>criteria, the Web server calls your authorizer FastCGI application. If your application grants authorization (by returning a response code of 200), the Web server resumes execution of commands in the <code>AuthorizeRegion</code> section. If your application denies authorization (by returning any other response code), the Web server stops processing subsequent commands in the <code>AuthorizeRegion</code> section, and returns the response from your FastCGI application to the client.<p>
+</a><a name="8431">
+Authorizer applications can return headers containing environment variables. Other CGI or FastCGI programs accessing this request (including other authorizers) can access these environment variables. The headers must have the following format:<p>
+</a><pre><a name="8432">
+Variable-<em>name</em>: <em>value</em>
+</a>
+</pre><a name="8433">
+For example, the following header<p>
+</a><pre><a name="8434">
+Variable-AUTH_METHOD: database lookup
+</a>
+</pre><a name="8435">
+causes the environment variable <code>AUTH_METHOD</code> to be set to <code>"database lookup"</code> for this request. Other CGI or FastCGI applications running on this request can access the value of <code>AUTH_METHOD</code>. <p>
+</a><a name="8437">
+Authorizer applications cannot successfully read from standard input. Any attempts to read from standard input result in an immediate EOF.<p>
+</a><a name="8438">
+All data that authorizer applications write to standard error will get written to the traditional server error logs. <p>
+</a><a name="4207">
+<h1> Writing FastCGI Applications</h1>
+</a><a name="9301">
+The work involved in writing a FastCGI application depends in large part on the I/O libraries that you use. This manual describes how to write FastCGI applications in terms of the Open Market libraries, which are available for C, Perl, and Tcl. FastCGI is an open standard and you are welcome to build your own libraries for other languages as well, but this manual focuses on building FastCGI applications in the context of the Open Market libraries.<p>
+</a><a name="9443">
+<p>
+</a><a name="9450">
+In general, the goal of the libraries is to make the job of writing a FastCGI application as much like writing a CGI application as possible. For example, you use the same techniques for query string decoding, HTML output to stdout, use of environment variables, and so on. When you use our libraries, porting CGI applications to FastCGI is mostly a matter of restructuring the code to take advantage of FastCGI features and libraries. <p>
+</a><a name="9469">
+<h2> Code Structure</h2>
+</a><a name="9470">
+The main task of converting a CGI program into a FastCGI program is separating the initialization code from the code that needs to run for each request. The structure should look something like this:<p>
+</a><pre><a name="9471">
+Initialization code
+</a>
+<a name="9472">
+Start of response loop
+</a>
+<a name="9473">
+   body of response loop
+</a>
+<a name="9474">
+End of response loop
+</a>
+</pre><a name="9475">
+The <em>initialization code</em> is run exactly once, when the application is initialized. Initialization code usually performs time-consuming operations such as opening databases or calculating values for tables or bitmaps. <p>
+</a><a name="9477">
+The <em>response loop</em> runs continuously, waiting for client requests to arrive. The loop starts with a call to <code>FCGI_Accept</code>, a routine in the FastCGI library. The <code>FCGI_Accept</code> routine blocks program execution until a client requests the FastCGI application. When a client request comes in, <code>FCGI_Accept</code> unblocks, runs one iteration of the response loop body, and then blocks again waiting for another client request. The loop terminates only when the system administrator or the Web server kills the FastCGI application.<p>
+</a><a name="9480">
+<h2> Initial Environment Variables</h2>
+</a><a name="9786">
+When a FastCGI process starts up, it has not yet accepted a request, and therefore none of the CGI environment variables are set.<p>
+</a><a name="9787">
+You set the initial environment of a FastCGI process started by the <code>AppClass </code>directive using the <code>-initial-env</code> option. The process would use this environment to configure its options and locate files or databases.<p>
+</a><a name="9829">
+In FastCGI processes started by the <code>AppClass</code> directive with the -affinity option, the <code>FCGI_PROCESS_ID</code> variable is set in the initial environment (not in the environment of a request). <code>FCGI_PROCESS_ID</code> is a decimal number in the range 0 to N - 1 where N is the number of processes (argument to the<code> -processes</code> option to <code>AppClass</code>). The process would use <code>FCGI_PROCESS_ID </code>in conjunction with other variables to locate session-related files or databases during restart.<p>
+</a><a name="9785">
+<h2> Per-Request Environment Variables</h2>
+</a><a name="9481">
+In general, FastCGI uses the same per-request environment variables as CGI, and you access the values of environment variables in FastCGI applications just as you would in CGI applications. The only differences are as follows:<p>
+</a><ul><a name="9483">
+<li>In Authorizer FastCGI applications, the Web server unsets the <code>PATH_INFO</code>, <code>PATH_TRANSLATED</code>, and <code>CONTENT_LENGTH</code> variables.
+</a><a name="9484">
+<li>In Filter FastCGI applications, the Web server sets two additional environment variables:
+</a><ul>
+<a name="9486">
+<li><code>FILE_LAST_MOD</code>: The Web server sets <code>FILE_LAST_MOD</code> to the date and time that filter input file was last modified. The format is the number of seconds since midnight (UTC), January 1, 1970. 
+</a><a name="9488">
+<li><code>FCGI_DATA_LENGTH</code>: The application reads at most <code>FCGI_DATA_LENGTH</code> bytes from the data stream before receiving the end-of-stream indication.
+</a></ul>
+<a name="9490">
+<li>FastCGI sets <code>FCGI_ROLE</code> for each request to <code>RESPONDER</code>, <code>AUTHORIZER</code>, or <code>FILTER</code>.
+</a></ul><a name="9048">
+<h2> Building FastCGI Applications in C</h2>
+</a><a name="9049">
+The Software Development Toolkit that accompanies WebServer 2.0 contains two libraries, fcgi_stdio and fcgiapp, for building FastCGI applications in C. <p>
+</a><a name="9723">
+The fcgi_stdio library implements our philosophy of making FastCGI applications similar to CGI applications, and provides full binary compatibility between FastCGI applications and CGI applications: you can run the same C binary as either CGI or FastCGI. <p>
+</a><a name="9545">
+The fcgiapp library is more specific to FastCGI, and doesn't attempt the veneer of CGI. <p>
+</a><a name="9731">
+We recommend that you use the fcgi_stdio library, and this manual describes the routines in that library. The documentation for the fcgiapp library is in the code in the development kit.<p>
+</a><a name="9570">
+<h2> Building FastCGI Applications in Perl</h2>
+</a><a name="9581">
+To build FastCGI applications in Perl, you need a FastCGI-savvy version of Perl, plus the FastCGI extension to Perl. We build FastCGI-savvy versions of the Perl interpreter for several common platforms and make them available on our Website. For details and examples, see Chapter <a href="ch3perl.htm#3659">3, "Developing FastCGI Applications in Perl," on page 17</a>.<p>
+</a><a name="9562">
+<h2> Building FastCGI Applications in Tcl</h2>
+</a><a name="9586">
+To build FastCGI applications in Tcl, you need a FastCGI-savvy version of Tcl. We build FastCGI-savvy versions of the Tcl interpreter for several common platforms and make them available on our Website. For details and examples, see Chapter <a href="ch4tcl.htm#3659">4, "Developing FastCGI Applications in Tcl," on page 19</a>.<p>
+</a><a name="8360">
+<h1> Implementation Details</h1>
+</a><a name="8066">
+The FastCGI application libraries are designed to shield you from the details of the FastCGI design. This section is designed for the curious reader who would like some low-level understanding. If you are not curious about the implementation, you can happily skip this section.<p>
+</a><a name="8554">
+As shown in the following figure, CGI applications use the three standard POSIX streams (<code>stdin</code>, <code>stdout</code>, and <code>stderr</code>), plus environment variables, to communicate with an HTTP server. <p>
+</a><a name="8359">
+<img src="ch1intra.gif"><p>
+</a><a name="4295">
+<p>
+</a><a name="8575">
+<h5>Figure 1:&#32; Flow of Data in CGI</h5>
+</a><a name="9001">
+The fundamental difference between FastCGI and CGI is that FastCGI applications are long-lived, which means that the Web Server needs to rendezvous with a running application, rather than starting the application in order to explicitly communicate with it.<p>
+</a><a name="9110">
+The FastCGI implementation basically creates a bidirectional connection between two processes that have no relationship. FastCGI uses a single connection for all the data associated with an application -- stdin, stdout, stderr, and environment variables. The data on the connection is encapsulated using a FastCGI protocol that allows stdin and the environment variables to share the same half connection (on the way in) and stdout and stderr to share the half connection (on the way out).<p>
+</a><a name="9020">
+On the input side, the FastCGI application receives data on the connection, unpacks it to separate stdin from the environment variables and then invokes the application. On the output side, FastCGI wraps stdout and stderr with appropriate protocol headers, and sends the encapsulated data out to the server.<p>
+</a><a name="9032">
+Since a FastCGI application does not always run on the same node as the HTTP server, we support two implementations of the connection: a <em>stream pipe</em><a href="#9645"><sup>1</sup></a>, for communications on the same machine, and TCP streams, for communication when the client and the server are on different machines.<p>
+</a><a name="8576">
+<img src="ch1inta1.gif"><p>
+</a><a name="7549">
+<h5>Figure 2:&#32; Flow of Data in FastCGI when server and application are on different machines</h5>
+</a><a name="7874">
+<h2> The fcgi_stdio Library: I/O Compatibility</h2>
+</a><a name="8977">
+The implementation for I/O compatibility is that the library <code>fcgi_stdio.h</code> contains macros to translate the types and procedures defined in stdio.h into the appropriate FastCGI calls. For example, consider a FastCGI program written in C containing the following line of code:<p>
+</a><pre><a name="5877">
+fprintf(stdout, "&lt;H2&gt;Aerobic Juggling&lt;/H2&gt;/n");
+</a>
+</pre><a name="9659">
+<code>fcgi_stdio.h</code> header file contains the macro<p>
+</a><pre><a name="6403">
+#define fprintf FCGI_fprintf
+</a>
+</pre><a name="6402">
+So the preprocessor translates the <code>fprintf</code> call into the following call:<p>
+</a><pre><a name="6411">
+FCGI_fprintf(stdout, "&lt;H2&gt;Aerobic Juggling&lt;/H2&gt;/n");
+</a>
+</pre><a name="5888">
+<code>FCGI_fprintf</code> takes the same arguments as <code>fprintf</code>. <p>
+</a><a name="9664">
+The implementation of FCGI_fprintf tests the file to see if it is a normal C stream or a FastCGI stream, and calls the appropriate implementation.<p>
+</a><a name="6463">
+The <code>fcgi_stdio.h</code> header file contains macros to translate calls to all ISO stdio.h routines (and all conventional Posix additions, such as <code>fileno</code>, <code>fdopen</code>, <code>popen</code>, and <code>pclose</code>) into their FastCGI equivalents. <p>
+</a><a name="9678">
+<h2> The fcgi_stdio Library: Binary compatibility</h2>
+</a><a name="9579">
+The fcgi_stdio library provides full binary compatibility between FastCGI applications and CGI applications: you can run the same C binary as either CGI or FastCGI. <p>
+</a><a name="9580">
+The implementation is in FCGI_Accept: the FCGI_Accept function tests its environment to determine whether the application was invoked as a CGI program or an FastCGI program. If it was invoked as a CGI program, the request loop will satisfy a single client request and then exit, producing CGI behavior.<p>
+</a><a name="8957">
+<p>
+</a>
+<hr><br>
+<a href="cover.htm">[Top]</a> <a href="ap_guide.htm">[Prev]</a> <a href="ch2c.htm">[Next]</a> <a href="ap_guida.htm">[Bottom]</a>
+<hr><br>
+<sup>1</sup><a name="9645">
+UNIX Network Programming, W. Richard Stevens, 1990 Prentice-Hall, Section 7.9<p>
+</a>
+
+<!-- This file was created with Quadralay WebWorks Publisher 3.0.3 -->
+<!-- -->
+<!-- For more information on how this document, and how the rest of -->
+<!-- this server was created, email yourEmail@xyzcorp.com -->
+<!-- -->
+<!-- Last updated: 04/15/96 08:00:13 -->
+
+</body>
+</html>
diff --git a/doc/fastcgi-prog-guide/ch2c.htm b/doc/fastcgi-prog-guide/ch2c.htm
new file mode 100755 (executable)
index 0000000..3274029
--- /dev/null
@@ -0,0 +1,226 @@
+<html><head><title></title></head>
+<body bgcolor=#ffffff>
+<a href="cover.htm">[Top]</a> <a href="ch1intro.htm">[Prev]</a> <a href="ch3perl.htm">[Next]</a> <a href="ap_guida.htm">[Bottom]</a>
+<hr><br>
+<a name="3659">
+<center><h1>2 Developing FastCGI <br>Applications in C</h1></center>
+</a><a name="917">
+This chapter explains how to code FastCGI applications in C and how to build them into executables. <p>
+</a><a name="4230">
+If you are converting a CGI application into a FastCGI application, in many cases you will only need to add a few lines of code. For more complex applications, you may also need to rearrange some code.<p>
+</a><a name="5371">
+<h1> The I/O Libraries</h1>
+</a><a name="5384">
+The FastCGI Software Development Kit that accompanies Open Market WebServer 2.0 includes I/O libraries to simplify the job of converting existing CGI applications to FastCGI or writing new FastCGI applications. There are two libraries in the kit: fcgi_stdio and fcgiapp. You must include one of these header files in your program:<p>
+</a><ul><a name="5386">
+<li><code>fcgi_stdio.h</code>
+</a><a name="4237">
+<li><code>fcgiapp.h</code>
+</a></ul><a name="4199">
+The <code>fcgi_stdio</code> library is a layer on top of the <code>fcgiapp</code> library, and we recommend strongly that you use it, both for converting existing CGI applications and for writing new FastCGI applications. The fcgi_stdio library offers several advantages:<p>
+</a><ul><a name="5811">
+<li>Simplicity: there are only 3 new API calls to learn
+</a><a name="5828">
+<li>Familiarity: If you are converting a CGI application to FastCGI, you will find few changes between CGI and FastCGI. We designed our library to make the job of building a FastCGI application as similar as possible to that of building a FastCGI application: you use the same environment variables, same techniques for parsing query strings, the same I/O routines, and so on. 
+</a><a name="5817">
+<li>Convenience: the library provides full binary compatibility between CGI and FastCGI. That is, you can run the same binary as either CGI or FastCGI. 
+</a></ul><a name="5399">
+The fcgiapp library is more specific to FastCGI, without trying to provide the veneer of familiarity with CGI. This manual describes the fcgi_stdio library; the fcgiapp library is documented in the header files that accompany the development kit. <p>
+</a><a name="5847">
+<h1> Code Structure</h1>
+</a><a name="4240">
+To structure code for FastCGI, you separate your code into two sections:<p>
+</a><ul><a name="4200">
+<li>Initialization section, which is executed only once.
+</a><a name="4201">
+<li>Response loop section, which gets executed every time the FastCGI script gets called.
+</a></ul><a name="4202">
+A response loop typically has the following format:<p>
+</a><pre><a name="4203">
+while (FCGI_Accept() &gt;= 0) {
+</a>
+<a name="4204">
+# body of response loop
+</a>
+<a name="4205">
+}
+</a>
+</pre><a name="4206">
+The <code>FCGI_Accept</code> blocks until a client request comes in, and then returns 0. If there is a system failure, or the system administrator terminates the process, Accept will return -1. <p>
+</a><a name="5852">
+If the application was invoked as a CGI program, the first call to Accept returns 0 and the second always returns -1, producing CGI behavior. (See <a href="apaman.htm#95860">"FCGI_Accept (3)" on page &#32;21</a> for details.)<p>
+</a><a name="5909">
+Also note that the CGI world encourages small scripts, whereas FastCGI encourages combining scripts. You may choose to rethink the overall structure of your applications to take better advantage of FastCGI performance gains.<p>
+</a><a name="5373">
+<h1> Example 1: TinyFastCGI</h1>
+</a><a name="4263">
+Here is a simple example of a responder FastCGI application written in C:<p>
+<pre>
+#include "fcgi_stdio.h" /* fcgi library; put it first*/<br>#include &lt;stdlib.h&gt;
+
+int count;
+
+void initialize(void)
+{
+  count=0;
+}
+
+void main(void)
+{
+/* Initialization. */  
+  initialize();
+
+/* Response loop. */
+  while (FCGI_Accept() &gt;= 0)   {
+    printf("Content-type: text/html\r\n"
+           "\r\n"
+           "&lt;title&gt;FastCGI Hello! (C, fcgi_stdio library)&lt;/title&gt;"
+           "&lt;h1&gt;FastCGI Hello! (C, fcgi_stdio library)&lt;/h1&gt;"
+           "Request number %d running on host &lt;i&gt;%s&lt;/i&gt;\n",
+            ++count, getenv("SERVER_HOSTNAME"));
+  }
+}
+</pre>
+<h1> Example 2: Prime Number Generator</h1>
+</a><a name="4182">
+Consider a responder application that generates the n-th prime number. <p>
+</a><a name="5217">
+A CGI application would have no efficient way of solving this problem. For example, if the user asks for the 50,000th prime number, a CGI application would have to calculate the first prime number, then the second, and so on, up until the 50,000th. The application would then terminate, taking with it all its hard-earned calculations. If a client then asks for the 49,000th prime number, the server will have to spawn a new CGI application which will have to start calculating prime numbers from scratch.<p>
+</a><a name="4315">
+FastCGI applications can be much more efficient at this sort of problem, since they can maintain state. A FastCGI application can calculate an extensive table of prime numbers in its initialization phase and then keep the table around indefinitely. Whenever a client requests a particular prime number, the response loop merely needs to look it up in the table. <p>
+</a><a name="4343">
+Here is the code for the prime number example:<p>
+<pre>
+#include "fcgi_stdio.h"
+#include &lt;stdlib.h&gt;
+#include &lt;string.h&gt;
+
+#define POTENTIALLY_PRIME 0
+#define COMPOSITE 1
+#define VALS_IN_SIEVE_TABLE 1000000
+#define MAX_NUMBER_OF_PRIME_NUMBERS 78600 
+
+/* All initialized to POTENTIALLY_PRIME */
+long int  sieve_table[VALS_IN_SIEVE_TABLE]; 
+long int  prime_table[MAX_NUMBER_OF_PRIME_NUMBERS];  
+/* Use Sieve of Erastothenes method of building 
+   a prime number table. */
+void
+initialize_prime_table(void)
+{
+ long int prime_counter=1;
+ long int current_prime=2, c, d; 
+  
+  prime_table[prime_counter]=current_prime;
+
+  while (current_prime &lt; VALS_IN_SIEVE_TABLE)   {
+   /* Mark off composite numbers. */
+     for (c = current_prime; c &lt;= VALS_IN_SIEVE_TABLE; 
+          c += current_prime)  {
+        sieve_table[c] = COMPOSITE;  
+     }
+
+   /* Find the next prime number. */
+     for (d=current_prime+1; sieve_table[d] == COMPOSITE; d++); 
+   /* Put the new prime number into the table. */ 
+     prime_table[++prime_counter]=d; 
+     current_prime=d;
+  }
+}
+
+
+void main(void)
+{
+    char *query_string;
+    long int n;
+
+    initialize_prime_table();
+
+    while(FCGI_Accept() &gt;= 0) {
+        /*
+         * Produce the necessary HTTP header.
+         */
+        printf("Content-type: text/html\r\n"
+               "\r\n");
+        /*
+         * Produce the constant part of the HTML document.
+         */
+        printf("&lt;title&gt;Prime FastCGI&lt;/title&gt;\n"
+               "&lt;h1&gt;Prime FastCGI&lt;/h1&gt;\n");
+        /*
+         * Read the query string and produce the variable part
+         * of the HTML document.
+         */
+        query_string = getenv("QUERY_STRING");
+        if(query_string == NULL) {
+            printf("Usage: Specify a positive number in the query string.\n");
+        } else {
+            query_string = strchr(query_string, `=') + 1;
+            n = strtol(query_string);
+            if(n &lt; 1) {
+                printf("The query string `%s' is not a positive number.\n",
+                       query_string);
+            } else if(n &gt; MAX_NUMBER_OF_PRIME_NUMBERS) {
+                printf("The number %d is too large for this program.\n", n);
+            } else
+                printf("The %ldth prime number is %ld.\n", prime_table[n]);
+            }
+        }
+    } /* while FCGI_Accept */
+}
+</pre><a name="5349">
+This application has a noticeable start up cost while it initializes the table, but subsequent accesses are fast.<p>
+</a><a name="5151">
+<h1> Building</h1>
+</a><a name="4630">
+This section explains how to build and debug FastCGI applications written in C.<p>
+</a><a name="4629">
+The C preprocessor needs to know the location of the <code>fcgi_stdio.h</code> header file, which is at the following pathname:<p>
+</a><pre><a name="4642">
+<em>$toolkit</em>/include/fcgi_stdio.h
+</a>
+</pre><a name="4645">
+where <em>$toolkit</em> symbolizes the directory in which you have installed the Software Development Kit for FastCGI. <p>
+</a><a name="4760">
+The linker needs to know the location of the <code>libfcgi.a</code> library file, which is at the following pathname:<p>
+</a><pre><a name="4647">
+<em>$toolkit</em>/libfcgi/libfcgi.a 
+</a>
+</pre><a name="4648">
+If your linker does not search the Berkeley socket library, then you must add linker directives to force this search.<p>
+</a><a name="4773">
+We provide a sample application <code>Makefile</code> at the following pathname: <p>
+</a><pre><a name="4649">
+<em>$toolkit</em>/examples/Makefile
+</a>
+</pre><a name="4652">
+This <code>Makefile</code> contains the necessary rules and pathnames to build the C FastCGI applications accompanying the toolkit. To build all the applications, type:<p>
+</a><pre><a name="4653">
+$ ./configure<br>$ make 
+</a>
+</pre><a name="4190">
+<h1> Memory Leaks</h1>
+</a><a name="4178">
+Memory leaks are seldom a problem in CGI programming because CGI applications rarely run long enough to be concerned with leaks. However, memory leaks can become a problem in FastCGI applications, particularly if each call to a popular FastCGI application causes additional memory to leak. <p>
+</a><a name="4785">
+When converting to FastCGI, you can either use a tool such as Purify from Pure Software to discover and fix storage leaks or you can run a C garbage collector such as Great Circle from Geodesic Systems. <p>
+</a><a name="4972">
+<p>
+</a>
+<hr><br>
+<a href="cover.htm">[Top]</a> <a href="ch1intro.htm">[Prev]</a> <a href="ch3perl.htm">[Next]</a> <a href="ap_guida.htm">[Bottom]</a>
+<hr><br>
+
+<!-- This file was created with Quadralay WebWorks Publisher 3.0.3 -->
+<!-- -->
+<!-- For more information on how this document, and how the rest of -->
+<!-- this server was created, email yourEmail@xyzcorp.com -->
+<!-- -->
+<!-- Last updated: 04/15/96 08:00:16 -->
+
+</body>
+</html>
diff --git a/doc/fastcgi-prog-guide/ch3perl.htm b/doc/fastcgi-prog-guide/ch3perl.htm
new file mode 100755 (executable)
index 0000000..02fb8ea
--- /dev/null
@@ -0,0 +1,86 @@
+<html><head><title></title></head>
+<body bgcolor=#ffffff>
+<a href="cover.htm">[Top]</a> <a href="ch2c.htm">[Prev]</a> <a href="ch4tcl.htm">[Next]</a> <a href="ap_guida.htm">[Bottom]</a>
+<hr><br>
+<a name="3659">
+<center><h1>3 Developing FastCGI <br>Applications in Perl</h1></center>
+</a><a name="917">
+This chapter explains how to code FastCGI applications in Perl. Before you can build FastCGI applications in Perl, you must have a FastCGI-savvy version of the Perl interpreter. Open Market develops such Perl binaries for popular platforms and makes them available with our developer's kit. <p>
+</a><a name="5008">
+The FastCGI-savvy binaries are extensions of standard Perl, and are intended to replace your existing Perl installation. There is no need to maintain two versions of Perl: the version that we supply will work fine when invoked from a shell or a CGI program. There are also directions in the developer's kit for how to make your own FastCGI-savvy Perl, if you need a version for some platform that we don't supply.<p>
+</a><a name="4369">
+FastCGI is ideal for applications written in Perl, because it provides a huge performance gain. When you run a Perl script, the Perl interpreter analyzes the entire script before executing any of it. With FastCGI, you can factor out this initialization cost and pay it only once, making execution of the actual script much faster in response to client calls.<p>
+</a><a name="4183">
+<h1> Getting Started</h1>
+</a><a name="4234">
+The first line of any Perl script typically specifies the pathname of the Perl interpreter itself. You must specify the pathname of a FastCGI-savvy Perl. <p>
+</a><a name="4235">
+Next, you must tell Perl to load the FastCGI extension. To do so, place the following line near the beginning of every FastCGI script: <p>
+</a><pre><a name="4210">
+use FCGI;
+</a>
+</pre><a name="4212">
+Then, you have to divide FastCGI scripts into the following two sections:<p>
+</a><ul><a name="4242">
+<li>Initialization section, which is executed only once.
+</a><a name="4243">
+<li>Response loop section, which gets executed every time the FastCGI script gets called.
+</a></ul><a name="4248">
+A response loop typically has the following format:<p>
+</a><pre><a name="4255">
+while (FCGI::accept &gt;= 0) {
+</a>
+<a name="4256">
+# body of response loop
+</a>
+<a name="4257">
+}
+</a>
+</pre><a name="4258">
+The <code>accept</code> call returns 0 whenever a client requests the FastCGI script. Otherwise, the <code>accept</code> call returns -1. <p>
+</a><a name="5002">
+<h1> Example: TinyFastCGI</h1>
+</a><a name="4588">
+Here is a simple example of a FastCGI application written in Perl:<p>
+</a><pre><a name="4589">
+#!fcgi-savvy-perl
+
+use FCGI; # Imports the library; required line
+
+# Initialization code
+
+$cnt = 0;
+
+# Response loop
+
+while (FCGI::accept &gt;= 0) {
+  print "Content-type: text/html\r\n\r\n";
+  print "&lt;head&gt;\n&lt;title&gt;FastCGI Demo Page (perl)&lt;/title&gt;\n&lt;/head&gt;\n";
+  print  "&lt;h1&gt;FastCGI Demo Page (perl)&lt;/h1&gt;\n";
+  print "This is coming from a FastCGI server.\n&lt;BR&gt;\n";
+  print "Running on &lt;EM&gt;$ENV{SERVER_NAME}&lt;/EM&gt; to &lt;EM&gt;$ENV{REMOTE_HOST}&lt;/EM&gt;\n&lt;BR&gt;\n";
+       $cnt++;
+  print "This is connection number $cnt\n";
+}
+</pre>
+<p>
+</a>
+<hr><br>
+<a href="cover.htm">[Top]</a> <a href="ch2c.htm">[Prev]</a> <a href="ch4tcl.htm">[Next]</a> <a href="ap_guida.htm">[Bottom]</a>
+<hr><br>
+
+
+
+<!-- This file was created with Quadralay WebWorks Publisher 3.0.3 -->
+<!-- -->
+<!-- For more information on how this document, and how the rest of -->
+<!-- this server was created, email yourEmail@xyzcorp.com -->
+<!-- -->
+<!-- Last updated: 04/15/96 08:00:18 -->
+
+</body>
+</html>
diff --git a/doc/fastcgi-prog-guide/ch4tcl.htm b/doc/fastcgi-prog-guide/ch4tcl.htm
new file mode 100755 (executable)
index 0000000..1fe0d28
--- /dev/null
@@ -0,0 +1,73 @@
+<html><head><title></title></head>
+<body bgcolor=#ffffff>
+<a href="cover.htm">[Top]</a> <a href="ch3perl.htm">[Prev]</a> <a href="apaman.htm">[Next]</a> <a href="ap_guida.htm">[Bottom]</a>
+<hr><br>
+<a name="3659">
+<center><h1>4 Developing FastCGI <br>Applications in Tcl</h1></center>
+</a><a name="4835">
+This chapter explains how to code FastCGI applications in Tcl. Prior to creating a FastCGI application, you must have a FastCGI-savvy version of the Tcl interpreter. Open Market develops Tcl binaries for popular platforms and makes them available with our developer's kit. <p>
+</a><a name="4943">
+The FastCGI-savvy binaries are extensions of standard Tcl, and are intended to replace your existing Tcl installation. There is no need to maintain two versions of Tcl: the version that we supply will work fine when invoked from a shell or a CGI program. There are also directions in the developer's kit for how to make your own FastCGI-savvy Tcl, if you need a version for some platform that we don't supply.<p>
+</a><a name="4221">
+In many cases, you can convert a Tcl script from CGI to FastCGI by adding a few lines of code to an existing script. For more complex scripts, you may also need to rearrange some existing code. <p>
+</a><a name="4222">
+<h1> Getting Started</h1>
+</a><a name="4223">
+The first line of any Tcl script typically specifies the pathname of the Tcl interpreter itself. You must specify the pathname of a FastCGI-savvy Tcl. <p>
+</a><a name="4226">
+Then, you have to divide FastCGI scripts into the following two sections:<p>
+</a><ul><a name="4227">
+<li>Initialization section, which is executed only once.
+</a><a name="4228">
+<li>Response loop section, which gets executed every time the FastCGI script gets called.
+</a></ul><a name="4229">
+A response loop typically has the following format:<p>
+</a><pre><a name="4923">
+while {[FCGI_Accept] &gt;= 0 } {
+</a>
+<a name="4925">
+# body of response loop
+</a>
+<a name="4367">
+}
+</a>
+</pre><a name="4233">
+The <code>FCGI_Accept</code> call returns 0 whenever a client requests the FastCGI script. Otherwise, the <code>FCGI_Accept</code> call returns -1. <p>
+</a><a name="4853">
+<h1> Example: TinyFastCGI</h1>
+</a><a name="4343">
+Here is a simple example of a FastCGI application written in Tcl:<p>
+</a><pre><a name="4344">
+#!fcgi-savvy-tcl
+
+set count 0
+
+# Response Loop
+while {[FCGI_Accept] &gt;= 0 } {
+        incr count
+        puts -nonewline "Content-type: text/html\r\n\r\n"
+        puts "&lt;title&gt;FastCGI Hello! (Tcl)&lt;/title&gt;"
+        puts "&lt;h1&gt;FastCGI Hello! (Tcl)&lt;/h1&gt;"
+        puts "Request number $count running on host    &lt;i&gt;$env(SERVER_NAME)&lt;/i&gt;"
+}
+</pre>
+<p>
+</a>
+<hr><br>
+<a href="cover.htm">[Top]</a> <a href="ch3perl.htm">[Prev]</a> <a href="apaman.htm">[Next]</a> <a href="ap_guida.htm">[Bottom]</a>
+<hr><br>
+
+
+<!-- This file was created with Quadralay WebWorks Publisher 3.0.3 -->
+<!-- -->
+<!-- For more information on how this document, and how the rest of -->
+<!-- this server was created, email yourEmail@xyzcorp.com -->
+<!-- -->
+<!-- Last updated: 04/15/96 08:00:19 -->
+
+</body>
+</html>
diff --git a/doc/fastcgi-prog-guide/cover.htm b/doc/fastcgi-prog-guide/cover.htm
new file mode 100755 (executable)
index 0000000..021561f
--- /dev/null
@@ -0,0 +1,62 @@
+<html><head><title></title></head>
+<body bgcolor=#ffffff>
+[Top] [Prev] <a href="ap_guide.htm">[Next]</a> <a href="ap_guida.htm">[Bottom]</a>
+<hr><br>
+<a name="336">
+<h1> Open Market FastCGI 1.0</h1>
+</a><a name="94">
+ Open Market, Inc.<br>245 First Street, Cambridge, MA 02142<br>T:&#32;617-621-9500&#32;F:&#32;617-252-3492<p>
+</a><a name="238">
+<h1> <img src="covera.gif"></h1>
+</a><a name="347">
+<h2> Programmer's Guide</h2>
+</a><a name="351">
+<h2> April 15, 1996 s p/n 42-10530-001 Rev. A</h2>
+</a>
+<hr><br>
+
+<a name="249">
+ OPEN MARKET, INC., PROVIDES THIS PUBLICATION "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. In no event shall Open Market be liable for any loss of profits, loss of business, loss of use of data, interruption of business, or for indirect, special, incidental, or consequential damages of any kind, even if Open Market has been advised of the possibility of such damages arising from any defect or error in this publication.<p>
+</a><a name="256">
+ <p>
+</a><a name="260">
+ Open Market may revise this publication from time to time without notice. Some states or jurisdictions do not allow disclaimer of express or implied warranties in certain transactions; therefore, this statement may not apply to you.<p>
+</a><a name="261">
+ <p>
+</a><a name="265">
+ Copyright &#169; 1996 Open Market, Inc.<p>
+</a><a name="266">
+ <p>
+</a><a name="267">
+ All rights reserved.<p>
+</a><a name="268">
+ <p>
+</a><a name="269">
+ <em>Alpha/OSF</em> is a trademark of Digital Equipment Corporation.<p>
+</a><a name="278">
+ <em>Digital UNIX</em> is a trademark of Digital Equipment Corporation.<br><em>BSD/386</em> is a trademark of Berkeley Software Design, Inc.<br><em>BSD/OS</em> is a trademark of Berkeley Software Design, Inc.<p>
+</a><a name="308">
+ <em>Great Circle</em> is a trademark of Geodesic Systems, Inc.<br><em>HP-UX</em> is a trademark of Hewlett-Packard Co., Inc.<br><em>IBM AIX</em> is a trademark of International Business Machines, Inc.<br><em>Word</em> is a trademark of Microsoft Corporation.<br><em>Netscape</em> is a trademark of Netscape Communications Company.<br><em>PostScript</em> is a trademark of Adobe Systems Inc.<p>
+</a><a name="307">
+ <em>Purify</em> is a trademark of Pure Software, Inc.<br><em>SecureWeb</em> is a trademark of Terisa Systems, Inc.<br><em>HylaFAX</em> is a trademark of Silicon Graphics, Inc.<br><em>SGI IRIX</em> is a trademark of Silicon Graphics, Inc.<br><em>Solaris</em> is a trademark of Sun Microsystems, Inc.<br><em>SunOS</em> is a trademark of Sun Microsystems, Inc.<br><em>UNIX</em> is a trademark of UNIX Systems Laboratories, Inc.<p>
+</a><a name="270">
+ <p>
+</a><a name="271">
+ Any other trademarks and product names used herein may be the trademarks of their respective companies.<p>
+</a>
+[Top] [Prev] <a href="ap_guide.htm">[Next]</a> <a href="ap_guida.htm">[Bottom]</a>
+<hr><br>
+
+
+<!-- This file was created with Quadralay WebWorks Publisher 3.0.3 -->
+<!-- -->
+<!-- For more information on how this document, and how the rest of -->
+<!-- this server was created, email yourEmail@xyzcorp.com -->
+<!-- -->
+<!-- Last updated: 04/15/96 08:03:01 -->
+
+</body>
+</html>
diff --git a/doc/fastcgi-prog-guide/covera.gif b/doc/fastcgi-prog-guide/covera.gif
new file mode 100755 (executable)
index 0000000..c1012fd
Binary files /dev/null and b/doc/fastcgi-prog-guide/covera.gif differ
diff --git a/doc/fastcgi-whitepaper/Makefile b/doc/fastcgi-whitepaper/Makefile
new file mode 100644 (file)
index 0000000..b4caeb4
--- /dev/null
@@ -0,0 +1,15 @@
+.SUFFIXES:
+.SUFFIXES: .htm .gut
+
+all:
+       make *.htm
+
+DIR     = /omi/httpd/www-dev/root
+TOOLS   = ${DIR}/Tools
+GUTSCSH = ${TOOLS}/fcgi-pa.csh
+
+.gut.htm:
+       ${GUTSCSH} $*.gut > $*.htm
+
+clean:
+       rm -f *~
diff --git a/doc/fastcgi-whitepaper/fastcgi.htm b/doc/fastcgi-whitepaper/fastcgi.htm
new file mode 100644 (file)
index 0000000..e5981b6
--- /dev/null
@@ -0,0 +1,743 @@
+<HTML>\r
+\r
+<HEAD>\r
+\r
+<TITLE>FastCGI</TITLE>\r
+\r
+<META NAME="GENERATOR" CONTENT="Internet Assistant for Microsoft Word 2.0z">\r
+</HEAD>
+
+<!--Copyright (c) 1996 Open Market, Inc.                                    -->
+<!--See the file "LICENSE.TERMS" for information on usage and redistribution-->
+<!--of this file, and for a DISCLAIMER OF ALL WARRANTIES.                   -->
+<!-- $Id: fastcgi.htm,v 1.1 1997/09/16 15:36:27 stanleyg Exp $ -->
+
+<BODY>\r
+\r
+<P>\r
+Open Market, Inc.</FONT>\r
+<P>\r
+<CENTER><FONT SIZE=4 COLOR=#FFFFFF FACE="Impact">Technical White\r
+Paper<BR>\r
+</FONT></CENTER>\r
+<H1>FastCGI:<BR>\r
+A High-Performance Web Server Interface</H1>\r
+\r
+<P>\r
+April 1996<!--Please send comments to:--></FONT>\r
+<HR>\r
+<!-- payne@openmarket.com-->\r
+<H2>1. Introduction</H2>\r
+\r
+<P>\r
+The surge in the use of the Web by business has created\r
+a tremendous need for server extension applications that create\r
+dynamic content. These are the applications that will allow businesses\r
+to deliver products, services, and messages whose shape and content\r
+are in part determined by the interaction with, and knowledge\r
+of, the customers to which they are delivered.</FONT>\r
+<P>\r
+This important movement away from static Web content\r
+is pushing the limits and exposing the weaknesses of the environment\r
+in which these applications are currently bound: CGI (Common Gateway\r
+Interface). Most importantly it does not offer the performance\r
+these applications require.  A new communication infrastructure\r
+is needed to connect Web servers with these new applications.\r
+This is what led Open Market to develop FastCGI.</FONT>\r
+<P>\r
+FastCGI is a fast, open, and secure Web server interface\r
+that solves the performance problems inherent in CGI, without\r
+introducing the overhead and complexity of proprietary APIs (Application\r
+Programming Interfaces).  </FONT>\r
+<P>\r
+This paper assumes that the reader has basic familiarity\r
+with Web technology and developing Web applications.</FONT>\r
+<H3>Common Gateway Interface</H3>\r
+\r
+<P>\r
+The de facto standard interface for Web server applications\r
+is CGI, which was first implemented in the NCSA server.  CGI has\r
+many benefits: </FONT>\r
+<UL>\r
+<LI><B>Simplicity.  </FONT></B><FONT>It is\r
+easy to understand.</FONT>\r
+<LI><B>Language independence.</FONT></B>\r
+CGI applications can be written in nearly any language.</FONT>\r
+<LI><B>Process isolation.</FONT></B>\r
+Since applications run in separate processes, buggy applications\r
+cannot crash the Web server or access the server's private internal\r
+state.</FONT>\r
+<LI><B>Open standard.</FONT></B>  Some\r
+form of CGI has been implemented on every Web server.</FONT>\r
+<LI><B>Architecture independence.</FONT></B>\r
+CGI is not tied to any particular server architecture (single\r
+threaded, multi-threaded, etc.).</FONT>\r
+</UL>\r
+\r
+<P>\r
+CGI also has some significant drawbacks.  The leading\r
+problem is performance:  Since a new process is created for each\r
+request and thrown away when the request is done, efficiency is\r
+poor.</FONT>\r
+<P>\r
+CGI also has limited functionality:  It only supports\r
+a simple &quot;responder&quot; role, where the application generates\r
+the response that is returned to the client.  CGI programs can't\r
+link into other stages of Web server request processing, such\r
+as authorization and logging.</FONT>\r
+<H3>Server APIs</H3>\r
+\r
+<P>\r
+In response to the performance problems for CGI,\r
+several vendors have developed APIs for their servers.  The two\r
+most notable are NSAPI from Netscape and ISAPI from Microsoft.\r
+ The freely available Apache server also has an API.</FONT>\r
+<P>\r
+Applications linked into the server API may be significantly\r
+faster than CGI programs.  The CGI startup/initialization problem\r
+is improved, because the application runs in the server process\r
+and is persistent across requests.  Web server APIs also offer\r
+more functionality than CGI:  you can write extensions that perform\r
+access control, get access to the server's log file, and link\r
+in to other stages in the server's request processing.</FONT>\r
+<P>\r
+However, APIs sacrifice all of CGI's benefits.  Vendor\r
+APIs have the following problems:</FONT>\r
+<UL>\r
+<LI><B>Complexity.</FONT></B>  Vendor\r
+APIs introduce a steep learning curve, with increased implementation\r
+and maintenance costs.</FONT>\r
+<LI><B>Language dependence.</FONT></B>\r
+ Applications have to be written in a language supported by the\r
+vendor API (usually C/C++).  Perl, the most popular language for\r
+CGI programs, can't be used with any existing vendor API.</FONT>\r
+<LI><B>No process isolation.  </FONT></B><FONT>Since\r
+the applications run in the server's address space, buggy applications\r
+can corrupt the core server (or each other).  A malicious or buggy\r
+application can compromise server security, and bugs in the core\r
+server can corrupt applications.</FONT>\r
+<LI><B>Proprietary.</FONT></B>  Coding\r
+your application to a particular API locks you into a particular\r
+vendor's server.</FONT>\r
+<LI><B>Tie-in to server architecture.</FONT></B>\r
+ API applications have to share the same architecture as the server:\r
+ If the Web server is multi-threaded, the application has to be\r
+thread-safe.  If the Web server has single-threaded processes,\r
+multi-threaded applications don't gain any performance advantage.\r
+ Also, when the vendor changes the server's architecture, the\r
+API will usually have to change, and applications will have to\r
+be adapted or rewritten.</FONT>\r
+</UL>\r
+\r
+<H3>FastCGI</H3>\r
+\r
+<P>\r
+The FastCGI interface combines the best aspects of\r
+CGI and vendor APIs. Like CGI, FastCGI applications run in separate,\r
+isolated processes.  FastCGI's advantages include:</FONT>\r
+<UL>\r
+<LI><B>Performance.</FONT></B>  FastCGI\r
+processes are persistent-they are reused to handle multiple requests.\r
+ This solves the CGI performance problem of creating new processes\r
+for each request.</FONT>\r
+<LI><B>Simplicity, with easy migration from CGI.</FONT></B>\r
+ The FastCGI application library (described on page 9) simplifies\r
+the migration of existing CGI applications.  Applications built\r
+with the application library can also run as CGI programs, for\r
+backward compatibility with old Web servers.</FONT>\r
+<LI><B>Language independence.</FONT></B>\r
+Like CGI, FastCGI applications can be written in any language,\r
+not just languages supported by the vendor API.</FONT>\r
+<LI><B>Process isolation.</FONT></B>\r
+A buggy FastCGI application cannot crash or corrupt the core server\r
+or other applications.  A malicious FastCGI application cannot\r
+steal any secrets (such as session keys for encryption) from the\r
+Web server.</FONT>\r
+<LI><B>Non-proprietary.</FONT></B> \r
+FastCGI is supported in all of Open Market's server products,\r
+and support is under development for other Web servers, including\r
+the freely available Apache and NCSA servers, as well as commercial\r
+servers from Microsoft and Netscape.</FONT>\r
+<LI><B>Architecture independence.</FONT></B>\r
+ The FastCGI interface is not tied to a particular server architecture.\r
+ Any Web server can implement the FastCGI interface.  Also, FastCGI\r
+does not impose any architecture on the application:  applications\r
+can be single or multi-threaded, regardless of the threading architecture\r
+of the Web server.</FONT>\r
+<LI><B>Support for distributed computing.</FONT></B>\r
+ FastCGI provides the ability to run applications remotely, which\r
+is useful for distributing load and managing external Web sites.</FONT>\r
+</UL>\r
+\r
+<P>\r
+The following sections describe the FastCGI interface,\r
+protocol, application library, and support in Open Market's WebServer\r
+products.</FONT>\r
+<H2>2. FastCGI Interface</H2>\r
+\r
+<P>\r
+The functionality provided by the FastCGI interface\r
+is very similar to that provided by CGI.  To best understand the\r
+FastCGI protocol, we review the CGI interface here. Basic CGI\r
+request processing proceeds as follows:</FONT>\r
+<OL>\r
+<LI>For each request, the server creates a new process\r
+and the process initializes itself.</FONT>\r
+<LI>The Web server passes the request information\r
+(such as remote host, username, HTTP headers, etc.) to the CGI\r
+program in environment variables.</FONT>\r
+<LI>The Web server sends any client input (such as\r
+user-entered field values from an HTML form) to the CGI program's\r
+standard input.</FONT>\r
+<LI>The CGI program writes any output to be returned\r
+to the client on standard output.  Error information written to\r
+standard error is logged by the Web server.</FONT>\r
+<LI>When the CGI process exits, the request is complete.</FONT>\r
+</OL>\r
+\r
+<P>\r
+FastCGI is conceptually very similar to CGI, with\r
+two major differences:</FONT>\r
+<UL>\r
+<LI>FastCGI processes are persistent:  after finishing\r
+a request, they wait for a new request instead of exiting.</FONT>\r
+<LI>Instead of using operating system environment\r
+variables and pipes, the FastCGI protocol  multiplexes the environment\r
+information, standard input, output and error over a single full-duplex\r
+connection.  This allows FastCGI programs to run on remote machines,\r
+using TCP connections between the Web server and the FastCGI application.</FONT>\r
+</UL>\r
+\r
+<P>\r
+Request processing in a single-threaded FastCGI application\r
+proceeds as follows:</FONT>\r
+<OL>\r
+<LI>The Web server creates FastCGI application processes\r
+to handle requests.  The processes may be created at startup,\r
+or created on demand.</FONT>\r
+<LI>The FastCGI program initializes itself, and waits\r
+for a new connection from the Web server.</FONT>\r
+<LI>When a client request comes in, the Web server\r
+opens a connection to the FastCGI process.  The server sends the\r
+CGI environment variable information and standard input over the\r
+connection.</FONT>\r
+<LI>The FastCGI process sends the standard output\r
+and error information back to the server over the same connection.</FONT>\r
+<LI>When the FastCGI process closes the connection,\r
+the request is complete.  The FastCGI process then waits for another\r
+connection from the Web server.</FONT>\r
+</OL>\r
+\r
+<P>\r
+FastCGI applications can run locally (on the same\r
+machine as the Web server) or remotely.  For local applications,\r
+the server uses a full-duplex pipe to connect to the FastCGI application\r
+process.  For remote applications, the server uses a TCP connection.</FONT>\r
+<P>\r
+FastCGI applications can be single-threaded or multi-threaded.\r
+ For single threaded applications, the Web server maintains a\r
+pool of processes (if the application is running locally)  to\r
+handle client requests.  The size of the pool is user configurable.\r
+ Multi-threaded FastCGI applications may accept multiple connections\r
+from the Web server and handle them simultaneously in a single\r
+process.  (For example, Java's built-in multi-threading, garbage\r
+collection, synchronization primitives, and platform independence\r
+make it a natural implementation language for multi-threaded FastCGI\r
+applications.)</FONT>\r
+<H3>Remote FastCGI</H3>\r
+\r
+<P>\r
+FastCGI's ability to run applications remotely (over\r
+a TCP connection) provides some major benefits.  These benefits\r
+are described in this section, along with some of the security\r
+issues that affect remote FastCGI applications.</FONT>\r
+<H4>FastCGI with Firewalls</H4>\r
+\r
+<P>\r
+Applications that run on organizational (external) Web servers\r
+and depend on internal databases can be a challenge to administer.\r
+Figure 1 shows a typical organization, with an external Web server,\r
+a firewall restricting access to the internal network, and internal\r
+databases and applications.\r
+<P>\r
+<CENTER><IMG SRC="img00001.gif"><A NAME="_Ref352505891">Figure\r
+1</A></CENTER>\r
+<P>\r
+With CGI and vendor APIs, the application has to run on the Web\r
+server machine.  This means the server administrator has to replicate\r
+the necessary database information onto the system hosting the\r
+Web server (which may be difficult to do in an automated way without\r
+compromising firewall security).  Or, the administrator may build\r
+a &quot;bridge&quot; that allows access through the Web server\r
+to internal databases and applications (which is effectively re-inventing\r
+remote FastCGI).\r
+<P>\r
+With remote FastCGI, the applications can run on the internal\r
+network, simplifying the administrator's job.  When used with\r
+appropriate firewall configuration and auditing, this approach\r
+provides a secure, high-performance, scalable way to bring internal\r
+applications and data to the external network.\r
+<H4>Load Distribution</H4>\r
+\r
+<P>\r
+For resource-intensive CGI and API applications, the Web server\r
+machine quickly becomes the bottleneck for overall throughput.\r
+The usual way to solve this performance problem is to buy a bigger,\r
+faster Web server machine, or to partition the Web site across\r
+several Web servers.\r
+<P>\r
+With remote FastCGI, the resource-intensive applications can be\r
+moved off the Web server machine, giving the server administrator\r
+additional flexibility in configuring the Web server.  The administrator\r
+can configure FastCGI applications &quot;behind the scenes&quot;\r
+without having to change any content links or the external view\r
+of the Web site.  The administrator can use several smaller, inexpensive\r
+server machines for applications, and can tailor each machine\r
+to the application it is hosting.\r
+<H4>Security Issues with Remote FastCGI</H4>\r
+\r
+<P>\r
+The two security issues with remote FastCGI connections are authentication\r
+and privacy.  FastCGI applications should only accept connections\r
+from Web servers that they trust (the application library includes\r
+support for IP address validation).  Future versions of the protocol\r
+will include support for applications authenticating Web servers,\r
+as well as support for running remote connections over secure\r
+transport protocols such as SSL or PCT.<!--This pargraph needs to be made stronger, going into the issues in a little more detail.-->\r
+\r
+<H3>The FastCGI Protocol</H3>\r
+\r
+<P>\r
+This section offers a brief introduction to the protocol\r
+used on the connection between the Web server and FastCGI application.\r
+ Most application developers will use the FastCGI application\r
+library and won't have to worry about the protocol details.  However,\r
+specialized applications are free to implement the FastCGI protocol\r
+directly.</FONT>\r
+<P>\r
+FastCGI uses a simple packet record format on the\r
+connection between the application and the Web server.  The same\r
+record format is used in both directions and is outlined in Figure 2.</FONT>\r
+<P>\r
+<CENTER><IMG SRC="img00002.gif"><A NAME="_Ref352404075">Figure\r
+2</FONT></A></CENTER>\r
+<P>\r
+The protocol version field specifies the version\r
+of the FastCGI protocol that is in use.  The type field specifies\r
+the type of the record (described in the following section). \r
+The request ID identifies this record to a particular request,\r
+allowing multiple requests to be multiplexed over a single connection.\r
+ The data length field specifies the number of data bytes that\r
+follow.</FONT>\r
+<P>\r
+The different FastCGI packet types are:</FONT>\r
+<TABLE>\r
+\r
+<TR><TD WIDTH=186><TT><FONT FACE="Courier">FCGI_PARAMS</FONT></TT>\r
+</TD><TD WIDTH=228>Used for sending name/value pairs (CGI environment variables) from the Web server to the application.\r
+</TD></TR>\r
+\r
+<TR><TD WIDTH=186><TT><FONT FACE="Courier">FCGI_STDIN</FONT></TT>\r
+</TD><TD WIDTH=228>Used for sending the standard input from the Web server to the application.\r
+</TD></TR>\r
+\r
+<TR><TD WIDTH=186><TT><FONT FACE="Courier">FCGI_DATA</FONT></TT>\r
+</TD><TD WIDTH=228>Used for sending filter data to the application (for more information, see the filter role described on page 7.)\r
+</TD></TR>\r
+\r
+<TR><TD WIDTH=186><TT><FONT FACE="Courier">FCGI_STDOUT</FONT></TT>\r
+</TD><TD WIDTH=228>Used to send standard output from the application to the Web server.\r
+</TD></TR>\r
+\r
+<TR><TD WIDTH=186><TT><FONT FACE="Courier">FCGI_STDERR</FONT></TT>\r
+</TD><TD WIDTH=228>Used to send standard error information from the application to the Web server.\r
+</TD></TR>\r
+\r
+<TR><TD WIDTH=186><TT><FONT FACE="Courier">FCGI_END_REQUEST</FONT></TT>\r
+</TD><TD WIDTH=228>Ends the request (can be sent by either the server or the application).\r
+</TD></TR>\r
+\r
+</TABLE>\r
+\r
+<P>\r
+\r
+<P>\r
+For complete protocol details, see the <I>FastCGI Protocol Specification</I>,\r
+available from the Web site listed at the end of this paper.\r
+<H2>3. Application Roles</H2>\r
+\r
+<P>\r
+A major problem with CGI is its limited functionality:\r
+ CGI programs can only provide simple responses to requests. \r
+FastCGI provides expanded functionality with support for three\r
+different application &quot;roles&quot;:</FONT>\r
+<UL>\r
+<LI><B>Responder.</FONT></B>  This is\r
+the basic FastCGI role, and corresponds to the simple functionality\r
+offered by CGI today.</FONT>\r
+<LI><B>Filter.</FONT></B>  The FastCGI\r
+application filters the requested Web server file before sending\r
+it to the client.</FONT>\r
+<LI><B>Authorizer.</FONT></B>  The FastCGI\r
+program performs an access control decision for the request (such\r
+as performing a username/password database lookup). </FONT>\r
+</UL>\r
+\r
+<P>\r
+Other roles will be defined in the future.  For instance,\r
+a &quot;logger&quot; role would be useful, where the FastCGI program\r
+would receive the server's log entries for real-time processing\r
+and analysis.</FONT>\r
+<P>\r
+The roles are described in more detail in the following\r
+sections.</FONT>\r
+<H3>Responder Role</H3>\r
+\r
+<P>\r
+FastCGI's Responder role is identical to the functionality\r
+provided by CGI today.  When a request comes into the server,\r
+the FastCGI program generates the response that's returned to\r
+the client (typically an HTML page).</FONT>\r
+<H3><A NAME="_Ref352404524">Filter Role</A></H3>\r
+\r
+<P>\r
+The Filter role allows a FastCGI application to process\r
+a requested file before it is returned to the client. </FONT>\r
+<P>\r
+Let's assume that the Web server is configured so\r
+that all files with the .</FONT><TT><FONT FACE="Courier">sgml\r
+</FONT></TT>extension are processed by a SGML-to-HTML\r
+FastCGI filter application, and the user accesses the following\r
+URL:</FONT>\r
+<P>\r
+ </FONT><TT><FONT FACE="Courier">/document.sgml</FONT></TT>\r
+<P>\r
+After the Web server makes an access control decision\r
+and maps this URL to a content file, it invokes the FastCGI filter\r
+application with this file available as input.  The FastCGI program's\r
+HTML output is sent back to the client, just as in the responder\r
+role.  The process is outlined in Figure 3.</FONT>\r
+<P>\r
+<CENTER><IMG SRC="img00003.gif"><A NAME="_Ref352560526">Figure\r
+3</FONT></A></CENTER>\r
+<P>\r
+Filter applications can significantly improve performance\r
+by caching filter results (the server provides the modification\r
+time in the request information so that applications can flush\r
+the cache when the server file has been modified).</FONT>\r
+<P>\r
+The Filter role is useful for:</FONT>\r
+<UL>\r
+<LI>On-the-fly format conversions</FONT>\r
+<LI>Dynamic documents (such as documents with embedded\r
+SQL queries, or dynamic advertisement insertion)</FONT>\r
+<LI>Applying a standard template:  headers, footers,\r
+and backgrounds</FONT>\r
+</UL>\r
+\r
+<H3>Authorizer Role</H3>\r
+\r
+<P>\r
+The Authorizer role allows a FastCGI application\r
+to make an access control decision for a request.  The FastCGI\r
+application is invoked with all of the request information, just\r
+as in the Responder role.  If the authorizer application generates\r
+a &quot;200 OK&quot; HTTP result, the Web server assumes that\r
+access is allowed and proceeds with the request.  (The Web server\r
+may process other access checks, including other FastCGI authorizers,\r
+before access is ultimately allowed.)  If the application generates\r
+any other response, that response is returned to the client and\r
+the request is ended.  The response can be any valid HTTP response,\r
+including &quot;Access Denied&quot; or &quot;Redirect&quot;.</FONT>\r
+<P>\r
+The Authorizer role is useful for:</FONT>\r
+<UL>\r
+<LI>Access control based on username and password,\r
+where the user information is looked up in an external database.</FONT>\r
+<LI>Complex access policies, such as time-of-day\r
+based access.</FONT>\r
+<LI>Smart-card challenge/response authentication.</FONT>\r
+<LI>Dynamic redirects, where the user is sent to\r
+different pages based on the request profile. </FONT>\r
+</UL>\r
+\r
+<H2><A NAME="_Ref352251764">4. FastCGI Application Library</A></H2>\r
+\r
+<P>\r
+Open Market has developed a FastCGI application library\r
+that implements the FastCGI protocol (hiding the protocol details\r
+from the developer). This library makes implementing FastCGI programs\r
+as easy as writing CGI applications.</FONT>\r
+<P>\r
+The application library provides a replacement for\r
+the C language standard I/O (stdio) routines, such as </FONT><TT><FONT FACE="Courier">printf()</FONT></TT>\r
+and </FONT><TT><FONT FACE="Courier">gets()</FONT></TT>.\r
+ The library converts references to standard input, standard output,\r
+and standard error to the FastCGI protocol.  References to other\r
+files &quot;fall through&quot; to the underlying operating system\r
+standard I/O routines.</FONT>\r
+<P>\r
+This approach has several benefits:</FONT>\r
+<UL>\r
+<LI>Developers don't have to learn a new API to develop\r
+FastCGI applications.</FONT>\r
+<LI>Existing CGI programs can be migrated with minimal\r
+source changes (CGI migration is described in more detail in the\r
+following section).</FONT>\r
+<LI>FastCGI interpreters for Perl, Tcl, and other\r
+interpreted languages can be built without modifying the interpreter\r
+source code.</FONT>\r
+</UL>\r
+\r
+<P>\r
+Here's a simple FastCGI application:<P>
+<PRE>
+    #include &lt;fcgi_stdio.h&gt;
+
+    void main(void)
+    {
+        int count = 0;
+        while(FCGI_Accept() &gt;= 0) {
+            printf("Content-type: text/html\r\n");
+            printf("\r\n");
+            printf("Hello world!&lt;br&gt;\r\n");
+            printf("Request number %d.", count++);
+        }
+        exit(0);
+    }
+</PRE>
+This application returns a &quot;Hello world&quot;\r
+HTML response to the client.  It also keeps a counter of the number\r
+of times it has been accessed, displaying the value of the counter\r
+at each request.</FONT>\r
+<P>\r
+The <TT>fcgi_stdio.h</TT>\r
+header file provides the FastCGI replacement routines for the\r
+C standard I/O library.  The <TT>FCGI_Accept()</TT>\r
+routine accepts a new request from the Web server.</FONT>\r
+<H3>Migrating Existing CGI Programs</H3>\r
+\r
+<P>\r
+The application library was designed to make migration\r
+of existing CGI programs as simple as possible.  Many applications\r
+can be converted by adding a loop around the main request processing\r
+code and recompiling with the FastCGI application library.  FastCGI\r
+applications have the following structure, with an initialization\r
+section and a request processing loop:<p>
+\r
+<I>Initialize application;<BR>\r
+</I><TT>while(FCGI_Accept() &gt;= 0) {</TT><BR>\r
+  <I>Process request</I>;<BR>\r
+<TT>}</TT>\r
+<P>\r
+To ease migration to FastCGI, executables built with\r
+the application library can run as either CGI or FastCGI programs,\r
+depending on how they are invoked.  The library detects the execution\r
+environment and automatically selects FastCGI or regular I/O routines,\r
+as appropriate.</FONT>\r
+<P>\r
+After migration, developers can clean up their FastCGI\r
+applications for best performance:</FONT>\r
+<UL>\r
+<LI>Fix any resource leaks.  Many CGI programs do\r
+not attempt to manage memory or close files, because they assume\r
+the world is going to be cleaned up when they exit. (If you don't\r
+want to clean up your program, you can just have your process\r
+assume that it is leaking memory and exit after processing some\r
+fixed number of requests.)  Purify from Pure Software is one of\r
+a number of excellent tools for finding leaks and other memory\r
+use problems.</FONT>\r
+<LI>Fix any problems with retained application state.\r
+ The application must ensure that any state that it creates in\r
+processing one request has no unintended effects on later requests.\r
+ </FONT>\r
+<LI>Collapse functionality.  A common practice with\r
+CGI applications is to implement many small programs, with one\r
+function per program.  CGI encourages this, because smaller programs\r
+load faster.  With FastCGI, it's better to have related functionality\r
+in a single executable, so there are fewer processes to manage\r
+and applications can take advantage of sharing cached information\r
+across functions.</FONT>\r
+</UL>\r
+\r
+<P>\r
+Applications written in Perl, Tcl, and other scripting\r
+languages can be migrated by using a language interpreter built\r
+with the application library.  FastCGI-integrated Tcl and Perl\r
+interpreters for popular Unix platforms are available from Open\r
+Market.  The interpreters are backward-compatible:  They can run\r
+standard Tcl and Perl applications. </FONT>\r
+<H2>5. FastCGI in the Open Market WebServer</H2>\r
+\r
+<P>\r
+This section describes the FastCGI support in the\r
+following Open Market server products:</FONT>\r
+<UL>\r
+<LI>Open Market WebServer V2.0</FONT>\r
+<LI>Open Market Secure WebServer V2.0</FONT>\r
+<LI>Open Market Secure WebServer (Global) V2.0</FONT>\r
+</UL>\r
+\r
+<P>\r
+For more information about FastCGI support,  see\r
+the <I>Open Market WebServer Installation and Configuration Guide</I>.</FONT>\r
+<H3>Server Configuration</H3>\r
+\r
+<P>\r
+FastCGI applications are configured with the server's\r
+configuration file.  Configuration has two parts.</FONT>\r
+<P>\r
+First, the server administrator defines an <I>application\r
+class</I>.  For local applications, the application class specifies\r
+the details of running the FastCGI application, such as:</FONT>\r
+<UL>\r
+<LI>The pathname of the application executable.</FONT>\r
+<LI>Any arguments and environment variables to pass\r
+to the process at startup.</FONT>\r
+<LI>The number of processes to run.</FONT>\r
+</UL>\r
+\r
+<P>\r
+For remote applications, the class configuration\r
+information includes the host and TCP port to connect to.  The\r
+Web server assumes that the FastCGI application has been started\r
+on the remote host.  If a request comes in and the server can't\r
+connect to the FastCGI TCP port, the server logs an error and\r
+returns an error page to the client.</FONT>\r
+<P>\r
+The second configuration step is mapping the application\r
+class to a role:</FONT>\r
+<UL>\r
+<LI>For responder roles, the administrator configures\r
+some part of the URL space to be handled by the FastCGI application.\r
+ For example, all URLs beginning with </FONT><FONT FACE="Courier">/rollcall/</FONT>\r
+might be handled by the employee database application.</FONT>\r
+<LI>For filter roles, the administrator configures\r
+a file extension to be handled by a filter application.  For example,\r
+all files with the </FONT><FONT FACE="Courier">.sql</FONT>\r
+extension could be handled by a SQL query lookup filter. </FONT>\r
+<LI>For authorizer roles, the administrator configures\r
+an authorizer application in the same manner as other access methods\r
+(hostname, username/password, etc.)  A request must pass <I>all\r
+</I>access control checks (possibly including multiple FastCGI\r
+authorizers) before access is allowed. </FONT>\r
+</UL>\r
+\r
+<H3>Basic FastCGI</H3>\r
+\r
+<P>\r
+To simplify migration for existing CGI programs,\r
+the WebServer provides a simple way to install new FastCGI programs\r
+without having to reconfigure the server. However, this approach\r
+doesn't offer all of the performance benefits of FastCGI application\r
+classes.</FONT>\r
+<P>\r
+The WebServer treats any file with the extension\r
+</FONT><FONT FACE="Courier">.fcg</FONT> as\r
+a FastCGI application.  When a request corresponds to such a file,\r
+the WebServer creates a new FastCGI process to handle the request,\r
+and shuts down the process when the request is complete (just\r
+as in CGI).  In this mode of operation performance is comparable\r
+to CGI.  Future versions of the WebServer will improve performance\r
+by automatically caching processes and re-using them for subsequent\r
+requests.</FONT>\r
+<H3>Session Affinity</H3>\r
+\r
+<P>\r
+FastCGI programs can improve performance by caching\r
+information in the application process.  For applications that\r
+require frequent but expensive operations such as validating a\r
+username/password in an external database for each request, this\r
+technique can significantly improve performance.</FONT>\r
+<P>\r
+To improve the effectiveness of this technique, the\r
+WebServer implements <I>session affinity</I>.  When session affinity\r
+is enabled, the WebServer arranges for all requests in a user\r
+session to be handled by the same FastCGI application process.\r
+  What constitutes a &quot;session&quot; is configurable.  The\r
+default configuration uses the WebServer's built-in session tracking\r
+facility to identify user sessions.  However, the server administrator\r
+can use any part of the request information for the session affinity\r
+mapping:  the URL path, the client's hostname, the username, etc.<!--Talk about applications that need to hold onto resources for the user (such as open connections to the database).--></FONT>\r
+\r
+<H2>6. FastCGI Performance Analysis</H2>\r
+\r
+<P>\r
+How fast is FastCGI?  The answer depends on the application.\r
+ This section contains some real FastCGI performance measurements,\r
+as well as guidelines for estimating the FastCGI speedup.  </FONT>\r
+<H3>FastCGI vs CGI</H3>\r
+\r
+<P>\r
+We measured the relative performance of CGI, FastCGI,\r
+and static files on the Open Market WebServer, using a simple\r
+application that generates a fixed number of output bytes.  The\r
+following table shows the measured request processing time for\r
+different request types on a typical platform.  The times are\r
+measured from the client perspective and include client, server,\r
+and application processing time.</FONT>\r
+<TABLE BORDERCOLOR=#000000 BORDER=2>\r
+\r
+<TR><TD WIDTH=72><CENTER><FONT SIZE=2 FACE="Arial Narrow">Static file</FONT></CENTER>\r
+</TD><TD WIDTH=180><CENTER><FONT SIZE=2 FACE="Arial Narrow">21ms + 0.19ms per Kbyte</FONT></CENTER>\r
+</TD></TR>\r
+\r
+<TR><TD WIDTH=72><CENTER><FONT SIZE=2 FACE="Arial Narrow">FastCGI</FONT></CENTER>\r
+</TD><TD WIDTH=180><CENTER><FONT SIZE=2 FACE="Arial Narrow">22ms + 0.28ms per Kbyte</FONT></CENTER>\r
+</TD></TR>\r
+\r
+<TR><TD WIDTH=72><CENTER><FONT SIZE=2 FACE="Arial Narrow">CGI</FONT></CENTER>\r
+</TD><TD WIDTH=180><CENTER><FONT SIZE=2 FACE="Arial Narrow">59ms + 0.37ms per Kbyte</FONT></CENTER>\r
+</TD></TR>\r
+\r
+</TABLE>\r
+\r
+<P>\r
+FastCGI performance is comparable to serving static files, and\r
+significantly better than CGI (clearly showing the high overhead\r
+for process creation).  Real applications have an additional time\r
+component: process initialization, which should be added to overall\r
+request processing time.\r
+<P>\r
+Let's use this data to estimate the speedup from migrating a typical\r
+database CGI application to FastCGI.  Assume the application takes\r
+50ms to initialize the database connection and generates 5K of\r
+output data.  Request performance can be computed as follows:\r
+<TABLE>\r
+\r
+<TR><TD WIDTH=108>CGI </TD><TD WIDTH=331>59ms + 50ms + (0.37ms)(5) = 111ms\r
+</TD></TR>\r
+\r
+<TR><TD WIDTH=108>FastCGI</TD><TD WIDTH=331>22ms + (0.28ms)(5) = 23ms\r
+</TD></TR>\r
+\r
+</TABLE>\r
+\r
+<P>\r
+In this example, FastCGI has a 5x performance advantage over CGI,\r
+mostly due to savings from not having to create and initialize\r
+new processes for each request.<!--Need to talk about FastCGI vs proprietary APIs.-->\r
+\r
+<H2>7. Conclusions</H2>\r
+\r
+<P>\r
+Today's Web business applications need a platform\r
+that's fast, open, maintainable, straightforward, stable, and\r
+secure.  FastCGI's design meets these requirements, and provides\r
+for a logical extension from proven and widely deployed CGI technology.\r
+ This allows developers to take advantage of FastCGI's benefits\r
+without losing their existing investment in CGI applications.<!--Need to talk about NT.--></FONT>\r
+<!--Need to give &quot;more punch&quot; to this conclusion: include info about uses for FastCGI (accessing legacy data in databases, access control, distributed applications, apps that have to run in multiple OS environments. -->\r
+<H2>8. For More Information</H2>\r
+\r
+<P>\r
+For more information about Open Market and our products,\r
+visit our Web site at:</FONT><FONT FACE="Courier">http://www.openmarket.com/</FONT>\r
+<P>\r
+For more information about the FastCGI protocol and\r
+the developer's kit, and the latest information about FastCGI\r
+standardization and support in other Web servers, visit the FastCGI\r
+project page at:</FONT><FONT FACE="Courier">http://www.openmarket.com/fastcgi/</FONT>\r
+</BODY>\r
+\r
+</HTML>\r
diff --git a/doc/fastcgi-whitepaper/img00001.gif b/doc/fastcgi-whitepaper/img00001.gif
new file mode 100644 (file)
index 0000000..f69a978
Binary files /dev/null and b/doc/fastcgi-whitepaper/img00001.gif differ
diff --git a/doc/fastcgi-whitepaper/img00002.gif b/doc/fastcgi-whitepaper/img00002.gif
new file mode 100644 (file)
index 0000000..3a39820
Binary files /dev/null and b/doc/fastcgi-whitepaper/img00002.gif differ
diff --git a/doc/fastcgi-whitepaper/img00003.gif b/doc/fastcgi-whitepaper/img00003.gif
new file mode 100644 (file)
index 0000000..45e997e
Binary files /dev/null and b/doc/fastcgi-whitepaper/img00003.gif differ
diff --git a/doc/fcgi-devel-kit.gut b/doc/fcgi-devel-kit.gut
new file mode 100644 (file)
index 0000000..4fb9b53
--- /dev/null
@@ -0,0 +1,746 @@
+FastCGI Developer's Kit
+/fastcgi/words
+fcgi-hd.gif
+[FastCGI]
+<center>FastCGI Developer's Kit</center>
+
+<!--Copyright (c) 1996 Open Market, Inc.                                    -->
+<!--See the file "LICENSE.TERMS" for information on usage and redistribution-->
+<!--of this file, and for a DISCLAIMER OF ALL WARRANTIES.                   -->
+
+<center>
+Mark R. Brown<br>
+Open Market, Inc.<br>
+<p>
+
+Document Version: 1.08<br>
+11 June 1996<br>
+</center>
+<p>
+
+<h5 align=center>
+Copyright &copy; 1996 Open Market, Inc.  245 First Street, Cambridge,
+  MA 02142 U.S.A.<br>
+Tel: 617-621-9500 Fax: 617-621-1703 URL:
+  <a href="http://www.openmarket.com/">http://www.openmarket.com/</a><br>
+$Id: fcgi-devel-kit.gut,v 1.1 1997/09/16 15:36:26 stanleyg Exp $ <br>
+</h5>
+<hr>
+
+<ul type=square>
+  <li><a HREF = "#S1">1. Introduction</a>
+  <li><a HREF = "#S2">2. Getting started</a>
+  <li><a HREF = "#S3">3. Writing applications</a>
+  <ul type=square>
+    <li><a HREF = "#S3.1">3.1 Using the <tt>fcgi_stdio</tt> library</a>
+    <li><a HREF = "#S3.2">3.2 Using the <tt>fcgiapp</tt> library</a>
+    <li><a HREF = "#S3.3">3.3 Using Perl and Tcl</a>
+    <li><a HREF = "#S3.4">3.4 Using Java</a>
+  </ul>
+  <li><a HREF = "#S4">4. Running applications</a>
+  <ul type=square>
+    <li><a HREF = "#S4.1">4.1 Using a Web server that supports FastCGI</a>
+    <li><a HREF = "#S4.2">4.2 Using <tt>cgi-fcgi</tt> with any Web server</a>
+  </ul>
+  <li><a HREF = "#S5">5. Known problems</a>
+  <li><a HREF = "#S6">6. Getting support</a>
+</ul>
+
+<hr>
+
+
+
+<h3><a NAME = "S1">1. Introduction</a></h3>
+
+FastCGI is an open extension to CGI that provides high performance
+for all Internet applications without the penalties of Web server
+APIs.<p>
+
+FastCGI is designed to be layered on top of existing Web server
+APIs.  For instance, the <tt>mod_fastcgi</tt> Apache module adds
+FastCGI support to the Apache server.  FastCGI can also be used,
+with reduced functionality and reduced performance, on any Web server
+that supports CGI.<p>
+
+This FastCGI Developer's Kit is designed to make developing FastCGI
+applications easy.  The kit currently supports FastCGI
+applications written in C/C++, Perl, Tcl, and Java.<p>
+
+This document:
+
+<ul type=square>
+  <li>Describes how to configure and build the
+      kit for your development platform.
+
+  <li>Tells how to write applications using the
+      libraries in the kit.
+
+  <li>Tells how to run applications using Web servers that support
+      FastCGI or using any Web server and <tt>cgi-fcgi</tt>.
+</ul>
+
+The kit includes a <a href="fastcgi-whitepaper/fastcgi.htm">technical white
+paper</a>, <tt>doc/fastcgi-whitepaper/fastcgi.htm</tt>.
+You should read at least the first three sections of the
+technical white paper before starting to write FastCGI applications.
+The <a href="fcgi-perf.htm">performance paper</a> will help you understand
+how application design affects performance with FastCGI.<p>
+
+The <a href ="fcgi-spec.html">FastCGI Specification</a>,
+<tt>doc/fcgi-spec.html</tt>, defines the interface between a FastCGI
+application and a Web server that supports FastCGI.  The software in
+the kit implements the specification.  You don't need to
+read the specification in order to write applications.<p>
+
+Additional information is provided in the 
+<a href ="http://www.fastcgi.com/words/FAQ.htm">FAQ</a> document, which
+contains frequently asked questions about application development 
+using FastCGI, as well as some general information.<p>
+
+Experience with CGI programming will be extremely valuable in writing FastCGI
+applications.  If you don't have enough experience with CGI programming,
+you should read one of the popular books on the topic or study the
+<a href = "http://hoohoo.ncsa.uiuc.edu/cgi/">NCSA CGI page</a>.
+For a more formal treatment of CGI/1.1 see the
+<a href = "http://ds.internic.net/internet-drafts/draft-robinson-www-interface-01.txt">Internet Draft CGI 1.1 Specification</a>.<p>
+
+
+
+<h3><a NAME = "S2">2. Getting started</a></h3>
+
+The kit is a compressed tar (tar.Z) file,
+distributed via the
+<a href = "http://www.fastcgi.com/applibs/">www.fastcgi.com/applibs</a>
+Web page.  Unpacking the tar file creates a new directory
+<tt>fcgi-devel-kit</tt>.<p>
+
+Open the kit's index page, <tt>fcgi-devel-kit/index.html</tt>, using
+the "Open File" command in your Web browser.  The index page gives you
+an overview of the kit structure and helps you navigate the kit. The
+index page also contains links that run some example applications, but
+the applications won't work when index.html is opened using the "Open
+File" command because they aren't aren't being accessed through a Web
+server.<p>
+
+In order to use the kit in earnest you'll need a Web server that you
+control, a Web server running with your user ID.  The Web server will
+be starting FastCGI applications that you will need to debug; this
+will be a lot more convenient for you if these processes run with your
+user ID.  It is best to have a Web server that supports FastCGI.
+<a href = "#S4">Section 4</a> discusses Web server issues.<p>
+
+If you can, keep the kit on a file system accessible from your
+personal workstation, do your builds on your workstation, and run your
+Web server on your workstation.  If that's not possible, arrange a
+configuration such that the kit is accessible from the machine that's
+going to run your Web server, and build the kit and your applications
+on a machine that's configured exactly the same way (same processor
+architecture, operating system, etc.) as the machine that's going to
+run your Web server.<p>
+
+To build the kit you execute this sequence of commands
+in the <tt>fcgi-devel-kit</tt> directory:<p>
+
+<pre>
+    % ./configure
+    % make
+</pre>
+
+We've built and exercised the kit on these platforms
+(listed in alphabetical order):<p>
+
+<ul type=square>
+    <li>
+        BSD/OS 1.1 (Intel Pentium), gcc
+    <li>
+        Digital UNIX V3.2 148 (Alpha), gcc/cc
+    <li>
+        Hewlett-Packard HP-UX A.09.05 C and B.10.01 A (PA-RISC), gcc/cc
+    <li>
+        IBM AIX 1 4 (RS/6000), gcc
+    <li>
+        Silicon Graphics IRIX 5.3 11091812 (MIPS), gcc
+    <li>
+        Sun Solaris 2.4 and 2.5 (SPARC), gcc/cc
+    <li>
+        Sun SunOS 4.1.4 (SPARC), gcc
+</ul>
+
+Once you've built the kit, follow the directions in
+<a href = "#S4">Section 4</a> to bring up your Web server
+and run the example applications.<p>
+
+
+
+<h3><a NAME = "S3">3. Writing applications</a></h3>
+
+
+<h4><a NAME = "S3.1">3.1 Using the <tt>fcgi_stdio</tt> library</a>
+</h4>
+
+The <tt>fcgi_stdio</tt> library provides
+the easiest transition for C CGI programs and C CGI
+programmers to FastCGI.  Using this library your application
+can run using either CGI or FastCGI, with the same binary
+for both situations.<p>
+
+To introduce the <tt>fcgi_stdio</tt> library
+we give a pair of examples: a tiny CGI program and the translation of this
+program to FastCGI.  These two example programs are included in the kit.<p>
+
+The CGI program is <tt>examples/tiny-cgi.c</tt>:<p>
+
+<pre>
+    #include &lt;stdio.h&gt;
+    #include &lt;stdlib.h&gt;
+
+    void main(void)
+    {
+        int count = 0;
+        printf("Content-type: text/html\r\n"
+               "\r\n"
+               "&lt;title&gt;CGI Hello!&lt;/title&gt;"
+               "&lt;h1&gt;CGI Hello!&lt;/h1&gt;"
+               "Request number %d running on host &lt;i&gt;%s&lt;/i&gt;\n",
+               ++count, getenv("SERVER_NAME"));
+    }
+</pre>
+
+The key features of this tiny CGI program are:<p>
+
+<ul type = square>
+    <li>
+        The program sends data to the Web server by writing to
+        <tt>stdout</tt>, using <tt>printf</tt> in this example.  The CGI
+        program first sends a <tt>Content-type</tt> header, then a
+        small HTML document.  The program includes <tt>stdio.h</tt> in
+        order to get access to the <tt>printf</tt> function.<p>
+    <li>
+        The program obtains parameters provided by the Web server by
+        reading environment variables.  The CGI program reads the
+        <tt>SERVER_NAME</tt> variable using <tt>getenv</tt> and
+        includes the value in the HTML document.  The program includes
+        <tt>stdlib.h</tt> in order to get access to the
+        <tt>getenv</tt> function.<p>
+</ul>
+
+The <tt>count</tt> variable is degenerate in this example;
+the CGI program runs a single request, so the request number
+is always one.  This variable will be more interesting
+in the FastCGI example.<p>
+
+<a NAME = "S3.1.1">The</a>
+corresponding FastCGI program is <tt>examples/tiny-fcgi.c</tt>:<p>
+
+<pre>
+    #include "fcgi_stdio.h"
+    #include &lt;stdlib.h&gt;
+
+    void main(void)
+    {
+        int count = 0;
+        while(FCGI_Accept() &gt;= 0)
+            printf("Content-type: text/html\r\n"
+                   "\r\n"
+                   "&lt;title&gt;FastCGI Hello!&lt;/title&gt;"
+                   "&lt;h1&gt;FastCGI Hello!&lt;/h1&gt;"
+                   "Request number %d running on host &lt;i&gt;%s&lt;/i&gt;\n",
+                    ++count, getenv("SERVER_NAME"));
+    }
+</pre>
+
+The key features of this tiny FastCGI program are:<p>
+
+<ul type = square>
+    <li>
+        The program is structured as a loop that begins by calling the
+        function <tt>FCGI_Accept</tt>.  The <tt>FCGI_Accept</tt>
+        function blocks until a new request arrives for the program to
+        execute.  The program includes <tt>fcgi_stdio.h</tt> in order
+        to get access to the <tt>FCGI_Accept</tt> function.<p>
+    <li>
+        Within the loop, <tt>FCGI_Accept</tt> creates a
+        CGI-compatible world.  <tt>printf</tt> and <tt>getenv</tt>
+        operate just as in the CGI program.  <tt>stdin</tt> and
+        <tt>stderr</tt>, not used by this tiny program, also
+        operate just as in a CGI program.<p>
+</ul>
+
+The <tt>count</tt> variable increments each time through the loop, so
+the program displays a new request number each time.  You can use
+the reload button in your browser to demonstrate this, once you've
+got the program built and running.<p>
+
+
+<h4>Building the program</h4>
+
+If you can build <tt>examples/tiny-cgi.c</tt>,
+it will be straightforward for you to build
+<tt>examples/tiny-fcgi.c</tt>.  You need to:<p>
+
+<ul type = square>
+    <li>
+        Add the directory containing the
+        <tt>fcgi_stdio.h</tt> header to the compiler's include search
+        path.  The kit calls this directory <tt>include</tt>.<p>
+    <li>
+        Add the library <tt>libfcgi.a</tt> to the linker's command
+        line so that it will be searched when linking.  The
+        <tt>libfcgi.a</tt> library implements the functions defined in
+        <tt>fcgi_stdio.h</tt>.  The kit calls the directory containing
+        this library <tt>libfcgi</tt>.<p>
+    <li>
+        Determine whether or not the linker on your
+        platform searches the Berkeley socket
+        library by default, and if not, add linker directives to
+        force this search.<p>
+</ul>
+
+See <tt>examples/Makefile</tt> (created by <tt>configure</tt>)
+for a Makefile that builds both
+programs.  Autoconf handles the platform-dependent linking issues; to
+see how, examine <tt>configure.in</tt> and
+<tt>examples/Makefile.in</tt>.<p>
+
+
+<h4>Running the program</h4>
+
+<a href = "#S4">Section 4</a> is all about
+how to run FastCGI applications.<p>
+
+You can use CGI to run application binaries built with the
+<tt>fcgi_stdio</tt> library.  The <tt>FCGI_Accept</tt>
+function tests its environment to determine how the application was
+invoked.  If it was invoked as a CGI program, the first
+call to FCGI_Accept is essentially a no-op and the second call
+returns <tt>-1</tt>.  In effect, the request loop disappears.<p>
+
+Of course, when a FastCGI application is run using CGI it does not
+get the benefits of FastCGI.  For instance, the application exits
+after servicing a single request, so it cannot maintain
+cached information.<p>
+
+
+<h4>Implementation details</h4>
+
+<tt>fcgi_stdio.h</tt> works by first including <tt>stdio.h</tt>, then
+defining macros to replace essentially all of the types and procedures
+defined in <tt>stdio.h</tt>.  (<tt>stdio.h</tt> defines a few
+procedures that have nothing to do with <tt>FILE *</tt>, such as
+<tt>sprintf</tt> and <tt>sscanf</tt>; <tt>fcgi_stdio.h</tt> doesn't
+replace these.)  For instance, <tt>FILE</tt> becomes
+<tt>FCGI_FILE</tt> and <tt>printf</tt> becomes <tt>FCGI_printf</tt>.
+You'll only see these new names if you read <tt>fcgi_stdio.h</tt> or
+examine your C source code after preprocessing.<p>
+
+Here are some
+consequences of this implementation technique:<p>
+
+<ul type = square>
+    <li>
+        On some platforms the implementation will break if you include
+        <tt>stdio.h</tt> after including <tt>fcgi_stdio.h</tt>,
+        because <tt>stdio.h</tt> often defines macros for functions such
+        as <tt>getc</tt> and <tt>putc</tt>.  Fortunately, on most
+        platforms <tt>stdio.h</tt> is protected against multiple
+        includes by lines near the top of the file that look like
+    <pre>
+    #ifndef _STDIO_H
+    #define _STDIO_H
+    </pre>
+        The specific symbol used for multiple-include protection,
+        <tt>_STDIO_H</tt> in this example, varies from platform to
+        platform.  As long as your platform protects <tt>stdio.h</tt>
+        against multiple includes, you can forget about this issue.<p>
+    <li>
+        If your application passes <tt>FILE *</tt> to functions
+        implemented in libraries for which you have source code, then
+        you'll want to recompile these
+        libraries with <tt>fcgi_stdio.h</tt> included.
+        Most C compilers provide a command-line option for including
+        headers in a program being compiled; using such a compiler feature
+        allows you to rebuild your libraries without making source changes.
+        For instance the gcc command line
+    <pre>
+    gcc -include /usr/local/include/fcgi_stdio.h wonderlib.c
+    </pre>
+        causes gcc to include <tt>fcgi_stdio.h</tt> before it even
+        begins to read the module <tt>wonderlib.c</tt>.<p>
+    <li>
+        If your application passes <tt>FILE *</tt> to functions
+        implemented in libraries
+        for which you do not have source code, then
+        you'll need to include the headers for these libraries
+        <i>before</i> you include <tt>fcgi_stdio.h</tt>.
+        You can't pass the <tt>stdin</tt>,
+        <tt>stdout</tt>, or <tt>stderr</tt> streams produced by
+        <tt>FCGI_Accept</tt> to any functions implemented by these
+        libraries.  You can pass a stream on a Unix file to a library
+        function by following this pattern:
+    <pre>
+    FILE *myStream = fopen(path, "r");
+    answer = MungeStream(FCGI_ToFile(myStream));
+    </pre>
+        Here <tt>MungeStream</tt> is a library function that you can't
+        recompile and <tt>FCGI_ToFile</tt> is a macro that converts
+        from <tt>FCGI_FILE *</tt> to <tt>FILE *</tt>.  The macro
+        <tt>FCGI_ToFile</tt> is defined in <tt>fcgi_stdio.h</tt>.<p>
+</ul>
+
+
+<h4>Converting CGI programs</h4>
+
+The main task in converting a CGI program into a FastCGI program is
+separating the code that needs to execute once, initializing the
+program, from the code that needs to run for each request.
+In our tiny example, initializing the <tt>count</tt> variable
+is outside the loop, while incrementing the <tt>count</tt> variable
+goes inside.<p>
+
+Retained application state may be an issue.  You must ensure that
+any application state created in processing one request has no
+unintended effects on later requests.  FastCGI offers the possibility
+of significant application performance improvements, through caching;
+it is up to you to make the caches work
+correctly.<p>
+
+Storage leaks may be an issue.  Many CGI programs don't worry about
+storage leaks because the programs don't run for long enough for
+bloating to be a problem.  When converting to FastCGI, you
+can either use a tool such as
+<a href ="http://www.pure.com/"><i>Purify</i></a> from Pure Software
+to discover and fix storage
+leaks, or you can run a C garbage collector such as
+<a href ="http://www.geodesic.com/"><i>Great Circle</i></a>
+from Geodesic Systems.<p>
+
+
+<h4>Limitations</h4>
+
+Currently there are some limits to the compatibility provided
+by the <tt>fcgi_stdio</tt> library:<p>
+
+<ul type = square>
+    <li>
+        The library does not provide FastCGI versions of
+        the functions <tt>fscanf</tt> and <tt>scanf</tt>.  If you wish
+        to apply <tt>fscanf</tt> or <tt>scanf</tt> to <tt>stdin</tt>
+        of a FastCGI program, the workaround is to read lines or other
+        natural units into memory and then call <tt>sscanf</tt>.  If
+        you wish to apply <tt>fscanf</tt> to a stream on a Unix file,
+        the workaround is to follow the pattern:
+    <pre>
+    FILE *myStream = fopen(path, "r");
+    count = fscanf(FCGI_ToFile(myStream), format, ...);
+    </pre>
+</ul>
+
+<h4>Reference documentation</h4>
+
+The <a href = "FCGI_Accept.3"><tt>FCGI_Accept</tt> manpage</a>,
+<tt>doc/FCGI_Accept.3</tt>, describes the function in the traditional
+format.<p>
+
+The <a href = "FCGI_Finish.3"><tt>FCGI_Finish</tt></a>
+(<tt>doc/FCGI_Finish.3</tt>),
+<a href = "FCGI_SetExitStatus.3"><tt>FCGI_SetExitStatus</tt></a>
+(<tt>doc/FCGI_SetExitStatus.3</tt>), and
+<a href = "FCGI_StartFilterData.3"><tt>FCGI_StartFilterData</tt></a>
+(<tt>doc/FCGI_StartFilterData.3</tt>)
+manpages document capabilities of the <tt>fcgi-stdio</tt>
+library that are not illustrated above.<p>
+
+
+<h4><a NAME = "S3.2">3.2 Using the <tt>fcgiapp</tt> library</a></h4>
+
+The <tt>fcgiapp</tt> library is a second C library for FastCGI.  It
+does not provide the high degree of source code compatibility provided
+by <tt>fcgi_stdio</tt>; in return, it does not make such heavy use of
+<tt>#define</tt>.  <tt>fcgi_stdio</tt> is implemented as
+a thin layer on top of <tt>fcgiapp</tt>.<p>
+
+Applications built using the <tt>fcgiapp</tt> library cannot run
+as CGI programs; that feature is provided at the <tt>fcgi_stdio</tt>
+level.<p>
+
+Functions defined in <tt>fcgiapp</tt> are named using the prefix
+<tt>FCGX_</tt> rather than <tt>FCGI_</tt>.  For instance,
+<tt>FCGX_Accept</tt> is the <tt>fcgiapp</tt> version of
+<tt>FCGI_Accept</tt>.<p>
+
+Documentation of the <tt>fcgiapp</tt> library takes the form
+of extensive comments in the header file <tt>include/fcgiapp.h</tt>.
+The sample programs <tt>examples/tiny-fcgi2.c</tt> and
+<tt>examples/echo2.c</tt> illustrate how to use
+<tt>fcgiapp</tt>.<p>
+
+
+<h4><a NAME = "S3.3">3.3 Using Perl and Tcl</a></h4>
+
+A major advantage of the FastCGI approach to high-performance Web
+applications is its language-neutrality.  CGI scripts written in
+popular languages such as Perl and Tcl can be evolved into
+high-performance FastCGI applications.<p>
+
+We have produced FastCGI-integrated Perl and Tcl
+interpreters.  Doing so was easy, since Perl and Tcl
+are conventional C applications and <tt>fcgi_stdio</tt> was
+designed for converting conventional C applications.  Essentially no source
+code changes were required in these programs; a small amount
+of code was added in order to make <tt>FCGI_Accept</tt> and other
+FastCGI primitives available in these languages.  And because
+these interpreters were developed using <tt>fcgi_stdio</tt>, they
+run standard Perl and Tcl applications (e.g. CGI scripts) as well
+as FastCGI applications.<p>
+
+See the
+<a href = "http://www.fastcgi.com/applibs/">www.fastcgi.com/applibs</a>
+Web page for downloadable Perl and Tcl binaries for selected platforms.
+Because many users of Perl and Tcl run extended versions of these languages,
+the kit includes separate companion documents describing
+<a href ="fcgi-perl.htm">how to build FastCGI-integrated Perl</a>
+and <a href ="fcgi-tcl.htm">how to build FastCGI-integrated Tcl</a>.<p>
+
+Here are the Perl and Tcl versions of <tt>tiny-fcgi</tt>:<p>
+<pre>
+#!./perl
+use FCGI;
+$count = 0;
+while(FCGI::accept() >= 0) {
+    print("Content-type: text/html\r\n\r\n",
+          "&lt;title&gt;FastCGI Hello! (Perl)&lt;/title&gt;\n",
+          "&lt;h1&gt;FastCGI Hello! (Perl)&lt;/h1&gt;\n";
+          "Request number ",  ++$count,
+          " running on host &lt;i&gt";$env(SERVER_NAME)&lt;/i&gt;");
+}
+</pre>
+
+<pre>
+#!./tclsh
+set count 0 
+while {[FCGI_Accept] &gt;= 0 } {
+    incr count
+    puts -nonewline "Content-type: text/html\r\n\r\n"
+    puts "&lt;title&gt;FastCGI Hello! (Tcl)&lt;/title&gt;"
+    puts "&lt;h1&gt;FastCGI Hello! (Tcl)&lt;/h1&gt;"
+    puts "Request number $count running on host &lt;i&gt;$env(SERVER_NAME)&lt;/i&gt;"
+}
+</pre>
+
+Converting a Perl or Tcl CGI application to FastCGI is not fundamentally
+different from converting a C CGI application to FastCGI.  You separate
+the portion of the application that performs one-time
+initialization from the portion that performs per-request
+processing.  You put the per-request processing into a loop
+controlled by <tt>FCGI::accept</tt> (Perl) or <tt>FCGI_Accept</tt>
+(Tcl).
+
+
+
+<h4><a NAME = "S3.4">3.4 Using Java</a></h4>
+
+Java is not just for browser-based applets.  It is already suitable for
+writing some Web server applications, and its range of applicability will
+only grow as Java compilers and other Java tools improve.  Java's
+modules, garbage collection, and threads are especially valuable
+for writing long-lived application servers.<p>
+
+The <tt>FCGIInterface</tt> class provides facilities for Java
+applications analogous to what <tt>fcgi_stdio</tt> provides for C
+applications.  Using this library your Java application can run using
+either CGI or FastCGI.<p>
+
+The kit includes separate companion document on
+<a href ="fcgi-java.htm">using FastCGI with Java</a>.  The
+source code for FastCGI classes is contained in
+directory <tt>java/src</tt>
+and the compiled code in <tt>java/classes</tt>.<p>
+
+Here is the Java version of <tt>tiny-fcgi</tt>:<p>
+
+<pre>
+import FCGIInterface;
+
+class TinyFCGI {       
+    public static void main (String args[]) {          
+        int count = 0;
+        while(new FCGIInterface().FCGIaccept()>= 0) {
+            count ++;
+            System.out.println("Content-type: text/html\r\n\r\n");
+            System.out.println(
+                    "&lt;title&gt;FastCGI Hello! (Java)&lt;/title&gt;");
+            System.out.println("&lt;h1&gt;FastCGI Hello! (Java)&lt;/h1&gt;");
+            System.out.println(
+                    "request number " + count + " running on host &lt;i&gt;" +
+                    System.getProperty("SERVER_NAME") + "&lt;/i&gt;");
+        }
+    }
+}
+</pre>
+<p>
+
+
+<h3><a NAME = "S4">4. Running applications</a></h3>
+
+
+<h3><a NAME = "S4.1">4.1 Using a Web server that supports FastCGI</a></h3>
+
+For a current listing of Web servers that support FastCGI,
+see the <a HREF = "http://www.fastcgi.com/servers">www.fastcgi.com/servers</a>
+Web page.<p>
+
+All of the Web servers that support FastCGI perform management of
+FastCGI applications.  You don't need to start and stop FastCGI
+applications; the Web server takes care of this.  If an application
+process should crash, the Web server restarts it.<p>
+
+Web servers support FastCGI via new configuration directives.
+Since these directives are server-specific, get more information
+from the documentation that accompanies each server.<p>
+
+The directory
+<a HREF = "../examples/conf"><tt>examples/conf</tt></a> contains config
+files designed to run the example programs included in the FastCGI
+Developer's Kit.  Each config file contains specific installation
+instructions.<p>
+
+The more advanced example programs take advantage of special features
+of the Open Market Secure WebServer, such as anonymous ticketing
+and support for the Authorizer role.  If you don't have this server,
+download a
+<a HREF= "http://www.openmarket.com/store/eval/swsg.htm">free
+evaluation copy</a> to run the examples.<p>
+
+
+<h3><a NAME = "S4.2">4.2 Using <tt>cgi-fcgi</tt> with any Web server</a></h3>
+
+The program <tt>cgi-fcgi</tt> allows you to run FastCGI applications
+using any Web server that supports CGI.<p>
+
+Here is how <tt>cgi-fcgi</tt> works.  <tt>cgi-fcgi</tt> is a
+standard CGI program that uses Unix domain or TCP/IP sockets
+to communicate with
+a FastCGI application.  <tt>cgi-fcgi</tt> takes the path name
+or host/port name of a
+listening socket as a parameter and <tt>connect</tt>s to the FastCGI
+application listening on that socket.  <tt>cgi-fcgi</tt> then forwards the CGI
+environment variables and <tt>stdin</tt> data to the FastCGI
+application, and forwards the <tt>stdout</tt> and <tt>stderr</tt> data
+from the FastCGI application to the Web server.  When the FastCGI
+application signals the end of its response, <tt>cgi-fcgi</tt> flushes
+its buffers and exits.<p>
+
+Obviously, having <tt>cgi-fcgi</tt> is not as good as having
+a server with integrated FastCGI support:<p>
+
+<ul>
+  <li>Communication is slower than with a Web server that avoids the
+      fork/exec overhead on every FastCGI request.
+
+  <li><tt>cgi-fcgi</tt> does not perform application management,
+      so you need to provide this yourself.
+
+  <li><tt>cgi-fcgi</tt> supports only the Responder role.
+</ul>
+
+But <tt>cgi-fcgi</tt> does allow you to
+develop applications that retain state in memory between connections,
+which often provides a major performance boost over normal CGI.  And
+all the applications you develop using <tt>cgi-fcgi</tt>
+will work with Web servers that have integrated support for
+FastCGI.<p>
+
+The file <tt>examples/tiny-fcgi.cgi</tt> demonstrates
+a way to use <tt>cgi-fcgi</tt> to run a typical application,
+in this case the <tt>examples/tiny-fcgi</tt> application:
+
+<pre>
+    #!../cgi-fcgi/cgi-fcgi -f
+    -connect sockets/tiny-fcgi tiny-fcgi
+</pre>
+
+On most Unix platforms, executing this command-interpreter file runs
+<tt>cgi-fcgi</tt> with arguments <tt>-f</tt> and
+<tt>examples/tiny-fcgi.cgi</tt>.  (Beware: On some
+Unix platforms, including HP-UX, the first line of a command-interpreter file
+cannot contain more than 32 characters, including the newline;
+you may need to install the <tt>cgi-fcgi</tt> application in a standard
+place like <tt>/usr/local/bin</tt> or create a symbolic link
+to the <tt>cgi-fcgi</tt> application in the directory containing
+your application.)
+The <tt>cgi-fcgi</tt> program reads the command-interpreter file and
+connects to the FastCGI application whose listening socket is
+<tt>examples/sockets/tiny-fcgi</tt>.<p>
+
+Continuing the example, if
+<tt>cgi-fcgi</tt>'s connection attempt fails, it
+creates a new process
+running the program <tt>examples/tiny-fcgi</tt> and listening on
+socket <tt>examples/sockets/tiny-fcgi</tt>.  Then <tt>cgi-fcgi</tt>
+retries the connection attempt, which now should succeed.<p>
+
+The <tt>cgi-fcgi</tt> program has two other modes of operation.
+In one mode it connects to applications but does not start them;
+in the other it starts applications but does not connect
+to them.  These modes are required when using TCP/IP.  The
+<a href = "cgi-fcgi.1"><tt>cgi-fcgi</tt> manpage</a>,
+<tt>doc/cgi-fcgi.1</tt>, tells the full story.<p>
+
+To run the example applications using <tt>cgi-fcgi</tt>, start your
+Web server and give it the directory <tt>fcgi-devel-kit</tt> as the
+root of its URL space.  If the machine running your server is called
+<tt>bowser</tt> and your server is running on port <tt>8888</tt>,
+you'd then open the URL <tt>http://bowser:8888/index.html</tt> to
+reach the kit's index page.  Now the links on the index page that run
+example applications via <tt>cgi-fcgi</tt> should be active.<p>
+
+
+
+<h3><a NAME = "S5">5. Known problems</a></h3>
+
+On Digital UNIX 3.0 there's a problem with Unix domain listening
+sockets on NFS file systems.  The symptom when using cgi-fcgi is an
+exit status of 38 (<tt>ENOTSOCK</tt>: socket operation on non-socket),
+but cgi-fcgi may dump core in this case when compiled optimized.
+Work-around: Store your Unix domain listening
+sockets on a non NFS file system, upgrade to Digital UNIX 3.2, or use
+TCP sockets.<p>
+
+On AIX there's a problem with shared listening sockets.
+The symptoms can include application core dumps and kernel panic.
+Work-around: Run a single FastCGI application server per listening
+socket.<p>
+
+
+
+<h3><a NAME = "S6">6. Getting support</a></h3>
+
+The mailing list <tt>fastcgi-developers@openmarket.com</tt>
+is used for discussions of issues in developing FastCGI applications.
+Topics include announcement of FastCGI-capable Web servers or
+changes to such servers, announcement of new application libraries
+or changes to such libraries, announcement of known bugs, discussion
+of design trade-offs in FastCGI application programming, and discussion
+of development plans and experiences.  To join the
+list, send a message to
+<a href = "mailto:fastcgi-developers-request@openmarket.com">fastcgi-developers-request@openmarket.com</a>
+with a message body consisting of the word "subscribe"
+(leaving off the quotes).<p>
+
+Mail sent to this list is archived and available on the
+World-Wide Web at
+<pre>
+    http://www.fastcgi.com/mail/
+</pre>
+
+Open Market Secure WebServer customers get FastCGI
+support where they get server support.<p>
+
+
+
+<hr>
+
+<address>
+&#169 1996, Open Market, Inc. / mbrown@openmarket.com
+</address>
+
diff --git a/doc/fcgi-devel-kit.htm b/doc/fcgi-devel-kit.htm
new file mode 100644 (file)
index 0000000..c171ab4
--- /dev/null
@@ -0,0 +1,756 @@
+<html>
+<head><title>FastCGI Developer's Kit</title>
+</head>
+
+<body bgcolor="#FFFFFF" text="#000000" link="#cc0000" alink="#000011" 
+vlink="#555555">
+
+<center>
+<a href="/fastcgi/words">
+    <img border=0 src="/kit/images/fcgi-hd.gif" alt="[[FastCGI]]"></a>
+</center>
+<br clear=all>
+<h3><center>FastCGI Developer's Kit</center></h3>
+
+<!--Copyright (c) 1996 Open Market, Inc.                                    -->
+<!--See the file "LICENSE.TERMS" for information on usage and redistribution-->
+<!--of this file, and for a DISCLAIMER OF ALL WARRANTIES.                   -->
+
+<center>
+Mark R. Brown<br>
+Open Market, Inc.<br>
+<p>
+
+Document Version: 1.08<br>
+11 June 1996<br>
+</center>
+<p>
+
+<h5 align=center>
+Copyright &copy; 1996 Open Market, Inc.  245 First Street, Cambridge,
+  MA 02142 U.S.A.<br>
+Tel: 617-621-9500 Fax: 617-621-1703 URL:
+  <a href="http://www.openmarket.com/">http://www.openmarket.com/</a><br>
+$Id: fcgi-devel-kit.htm,v 1.1 1997/09/16 15:36:26 stanleyg Exp $ <br>
+</h5>
+<hr>
+
+<ul type=square>
+  <li><a HREF = "#S1">1. Introduction</a>
+  <li><a HREF = "#S2">2. Getting started</a>
+  <li><a HREF = "#S3">3. Writing applications</a>
+  <ul type=square>
+    <li><a HREF = "#S3.1">3.1 Using the <tt>fcgi_stdio</tt> library</a>
+    <li><a HREF = "#S3.2">3.2 Using the <tt>fcgiapp</tt> library</a>
+    <li><a HREF = "#S3.3">3.3 Using Perl and Tcl</a>
+    <li><a HREF = "#S3.4">3.4 Using Java</a>
+  </ul>
+  <li><a HREF = "#S4">4. Running applications</a>
+  <ul type=square>
+    <li><a HREF = "#S4.1">4.1 Using a Web server that supports FastCGI</a>
+    <li><a HREF = "#S4.2">4.2 Using <tt>cgi-fcgi</tt> with any Web server</a>
+  </ul>
+  <li><a HREF = "#S5">5. Known problems</a>
+  <li><a HREF = "#S6">6. Getting support</a>
+</ul>
+
+<hr>
+
+
+
+<h3><a NAME = "S1">1. Introduction</a></h3>
+
+FastCGI is an open extension to CGI that provides high performance
+for all Internet applications without the penalties of Web server
+APIs.<p>
+
+FastCGI is designed to be layered on top of existing Web server
+APIs.  For instance, the <tt>mod_fastcgi</tt> Apache module adds
+FastCGI support to the Apache server.  FastCGI can also be used,
+with reduced functionality and reduced performance, on any Web server
+that supports CGI.<p>
+
+This FastCGI Developer's Kit is designed to make developing FastCGI
+applications easy.  The kit currently supports FastCGI
+applications written in C/C++, Perl, Tcl, and Java.<p>
+
+This document:
+
+<ul type=square>
+  <li>Describes how to configure and build the
+      kit for your development platform.
+
+  <li>Tells how to write applications using the
+      libraries in the kit.
+
+  <li>Tells how to run applications using Web servers that support
+      FastCGI or using any Web server and <tt>cgi-fcgi</tt>.
+</ul>
+
+The kit includes a <a href="fastcgi-whitepaper/fastcgi.htm">technical white
+paper</a>, <tt>doc/fastcgi-whitepaper/fastcgi.htm</tt>.
+You should read at least the first three sections of the
+technical white paper before starting to write FastCGI applications.
+The <a href="fcgi-perf.htm">performance paper</a> will help you understand
+how application design affects performance with FastCGI.<p>
+
+The <a href ="fcgi-spec.html">FastCGI Specification</a>,
+<tt>doc/fcgi-spec.html</tt>, defines the interface between a FastCGI
+application and a Web server that supports FastCGI.  The software in
+the kit implements the specification.  You don't need to
+read the specification in order to write applications.<p>
+
+Additional information is provided in the 
+<a href ="http://www.fastcgi.com/words/FAQ.htm">FAQ</a> document, which
+contains frequently asked questions about application development 
+using FastCGI, as well as some general information.<p>
+
+Experience with CGI programming will be extremely valuable in writing FastCGI
+applications.  If you don't have enough experience with CGI programming,
+you should read one of the popular books on the topic or study the
+<a href = "http://hoohoo.ncsa.uiuc.edu/cgi/">NCSA CGI page</a>.
+For a more formal treatment of CGI/1.1 see the
+<a href = "http://ds.internic.net/internet-drafts/draft-robinson-www-interface-01.txt">Internet Draft CGI 1.1 Specification</a>.<p>
+
+
+
+<h3><a NAME = "S2">2. Getting started</a></h3>
+
+The kit is a compressed tar (tar.Z) file,
+distributed via the
+<a href = "http://www.fastcgi.com/applibs/">www.fastcgi.com/applibs</a>
+Web page.  Unpacking the tar file creates a new directory
+<tt>fcgi-devel-kit</tt>.<p>
+
+Open the kit's index page, <tt>fcgi-devel-kit/index.html</tt>, using
+the "Open File" command in your Web browser.  The index page gives you
+an overview of the kit structure and helps you navigate the kit. The
+index page also contains links that run some example applications, but
+the applications won't work when index.html is opened using the "Open
+File" command because they aren't aren't being accessed through a Web
+server.<p>
+
+In order to use the kit in earnest you'll need a Web server that you
+control, a Web server running with your user ID.  The Web server will
+be starting FastCGI applications that you will need to debug; this
+will be a lot more convenient for you if these processes run with your
+user ID.  It is best to have a Web server that supports FastCGI.
+<a href = "#S4">Section 4</a> discusses Web server issues.<p>
+
+If you can, keep the kit on a file system accessible from your
+personal workstation, do your builds on your workstation, and run your
+Web server on your workstation.  If that's not possible, arrange a
+configuration such that the kit is accessible from the machine that's
+going to run your Web server, and build the kit and your applications
+on a machine that's configured exactly the same way (same processor
+architecture, operating system, etc.) as the machine that's going to
+run your Web server.<p>
+
+To build the kit you execute this sequence of commands
+in the <tt>fcgi-devel-kit</tt> directory:<p>
+
+<pre>
+    % ./configure
+    % make
+</pre>
+
+We've built and exercised the kit on these platforms
+(listed in alphabetical order):<p>
+
+<ul type=square>
+    <li>
+        BSD/OS 1.1 (Intel Pentium), gcc
+    <li>
+        Digital UNIX V3.2 148 (Alpha), gcc/cc
+    <li>
+        Hewlett-Packard HP-UX A.09.05 C and B.10.01 A (PA-RISC), gcc/cc
+    <li>
+        IBM AIX 1 4 (RS/6000), gcc
+    <li>
+        Silicon Graphics IRIX 5.3 11091812 (MIPS), gcc
+    <li>
+        Sun Solaris 2.4 and 2.5 (SPARC), gcc/cc
+    <li>
+        Sun SunOS 4.1.4 (SPARC), gcc
+</ul>
+
+Once you've built the kit, follow the directions in
+<a href = "#S4">Section 4</a> to bring up your Web server
+and run the example applications.<p>
+
+
+
+<h3><a NAME = "S3">3. Writing applications</a></h3>
+
+
+<h4><a NAME = "S3.1">3.1 Using the <tt>fcgi_stdio</tt> library</a>
+</h4>
+
+The <tt>fcgi_stdio</tt> library provides
+the easiest transition for C CGI programs and C CGI
+programmers to FastCGI.  Using this library your application
+can run using either CGI or FastCGI, with the same binary
+for both situations.<p>
+
+To introduce the <tt>fcgi_stdio</tt> library
+we give a pair of examples: a tiny CGI program and the translation of this
+program to FastCGI.  These two example programs are included in the kit.<p>
+
+The CGI program is <tt>examples/tiny-cgi.c</tt>:<p>
+
+<pre>
+    #include &lt;stdio.h&gt;
+    #include &lt;stdlib.h&gt;
+
+    void main(void)
+    {
+        int count = 0;
+        printf("Content-type: text/html\r\n"
+               "\r\n"
+               "&lt;title&gt;CGI Hello!&lt;/title&gt;"
+               "&lt;h1&gt;CGI Hello!&lt;/h1&gt;"
+               "Request number %d running on host &lt;i&gt;%s&lt;/i&gt;\n",
+               ++count, getenv("SERVER_NAME"));
+    }
+</pre>
+
+The key features of this tiny CGI program are:<p>
+
+<ul type = square>
+    <li>
+        The program sends data to the Web server by writing to
+        <tt>stdout</tt>, using <tt>printf</tt> in this example.  The CGI
+        program first sends a <tt>Content-type</tt> header, then a
+        small HTML document.  The program includes <tt>stdio.h</tt> in
+        order to get access to the <tt>printf</tt> function.<p>
+    <li>
+        The program obtains parameters provided by the Web server by
+        reading environment variables.  The CGI program reads the
+        <tt>SERVER_NAME</tt> variable using <tt>getenv</tt> and
+        includes the value in the HTML document.  The program includes
+        <tt>stdlib.h</tt> in order to get access to the
+        <tt>getenv</tt> function.<p>
+</ul>
+
+The <tt>count</tt> variable is degenerate in this example;
+the CGI program runs a single request, so the request number
+is always one.  This variable will be more interesting
+in the FastCGI example.<p>
+
+<a NAME = "S3.1.1">The</a>
+corresponding FastCGI program is <tt>examples/tiny-fcgi.c</tt>:<p>
+
+<pre>
+    #include "fcgi_stdio.h"
+    #include &lt;stdlib.h&gt;
+
+    void main(void)
+    {
+        int count = 0;
+        while(FCGI_Accept() &gt;= 0)
+            printf("Content-type: text/html\r\n"
+                   "\r\n"
+                   "&lt;title&gt;FastCGI Hello!&lt;/title&gt;"
+                   "&lt;h1&gt;FastCGI Hello!&lt;/h1&gt;"
+                   "Request number %d running on host &lt;i&gt;%s&lt;/i&gt;\n",
+                    ++count, getenv("SERVER_NAME"));
+    }
+</pre>
+
+The key features of this tiny FastCGI program are:<p>
+
+<ul type = square>
+    <li>
+        The program is structured as a loop that begins by calling the
+        function <tt>FCGI_Accept</tt>.  The <tt>FCGI_Accept</tt>
+        function blocks until a new request arrives for the program to
+        execute.  The program includes <tt>fcgi_stdio.h</tt> in order
+        to get access to the <tt>FCGI_Accept</tt> function.<p>
+    <li>
+        Within the loop, <tt>FCGI_Accept</tt> creates a
+        CGI-compatible world.  <tt>printf</tt> and <tt>getenv</tt>
+        operate just as in the CGI program.  <tt>stdin</tt> and
+        <tt>stderr</tt>, not used by this tiny program, also
+        operate just as in a CGI program.<p>
+</ul>
+
+The <tt>count</tt> variable increments each time through the loop, so
+the program displays a new request number each time.  You can use
+the reload button in your browser to demonstrate this, once you've
+got the program built and running.<p>
+
+
+<h4>Building the program</h4>
+
+If you can build <tt>examples/tiny-cgi.c</tt>,
+it will be straightforward for you to build
+<tt>examples/tiny-fcgi.c</tt>.  You need to:<p>
+
+<ul type = square>
+    <li>
+        Add the directory containing the
+        <tt>fcgi_stdio.h</tt> header to the compiler's include search
+        path.  The kit calls this directory <tt>include</tt>.<p>
+    <li>
+        Add the library <tt>libfcgi.a</tt> to the linker's command
+        line so that it will be searched when linking.  The
+        <tt>libfcgi.a</tt> library implements the functions defined in
+        <tt>fcgi_stdio.h</tt>.  The kit calls the directory containing
+        this library <tt>libfcgi</tt>.<p>
+    <li>
+        Determine whether or not the linker on your
+        platform searches the Berkeley socket
+        library by default, and if not, add linker directives to
+        force this search.<p>
+</ul>
+
+See <tt>examples/Makefile</tt> (created by <tt>configure</tt>)
+for a Makefile that builds both
+programs.  Autoconf handles the platform-dependent linking issues; to
+see how, examine <tt>configure.in</tt> and
+<tt>examples/Makefile.in</tt>.<p>
+
+
+<h4>Running the program</h4>
+
+<a href = "#S4">Section 4</a> is all about
+how to run FastCGI applications.<p>
+
+You can use CGI to run application binaries built with the
+<tt>fcgi_stdio</tt> library.  The <tt>FCGI_Accept</tt>
+function tests its environment to determine how the application was
+invoked.  If it was invoked as a CGI program, the first
+call to FCGI_Accept is essentially a no-op and the second call
+returns <tt>-1</tt>.  In effect, the request loop disappears.<p>
+
+Of course, when a FastCGI application is run using CGI it does not
+get the benefits of FastCGI.  For instance, the application exits
+after servicing a single request, so it cannot maintain
+cached information.<p>
+
+
+<h4>Implementation details</h4>
+
+<tt>fcgi_stdio.h</tt> works by first including <tt>stdio.h</tt>, then
+defining macros to replace essentially all of the types and procedures
+defined in <tt>stdio.h</tt>.  (<tt>stdio.h</tt> defines a few
+procedures that have nothing to do with <tt>FILE *</tt>, such as
+<tt>sprintf</tt> and <tt>sscanf</tt>; <tt>fcgi_stdio.h</tt> doesn't
+replace these.)  For instance, <tt>FILE</tt> becomes
+<tt>FCGI_FILE</tt> and <tt>printf</tt> becomes <tt>FCGI_printf</tt>.
+You'll only see these new names if you read <tt>fcgi_stdio.h</tt> or
+examine your C source code after preprocessing.<p>
+
+Here are some
+consequences of this implementation technique:<p>
+
+<ul type = square>
+    <li>
+        On some platforms the implementation will break if you include
+        <tt>stdio.h</tt> after including <tt>fcgi_stdio.h</tt>,
+        because <tt>stdio.h</tt> often defines macros for functions such
+        as <tt>getc</tt> and <tt>putc</tt>.  Fortunately, on most
+        platforms <tt>stdio.h</tt> is protected against multiple
+        includes by lines near the top of the file that look like
+    <pre>
+    #ifndef _STDIO_H
+    #define _STDIO_H
+    </pre>
+        The specific symbol used for multiple-include protection,
+        <tt>_STDIO_H</tt> in this example, varies from platform to
+        platform.  As long as your platform protects <tt>stdio.h</tt>
+        against multiple includes, you can forget about this issue.<p>
+    <li>
+        If your application passes <tt>FILE *</tt> to functions
+        implemented in libraries for which you have source code, then
+        you'll want to recompile these
+        libraries with <tt>fcgi_stdio.h</tt> included.
+        Most C compilers provide a command-line option for including
+        headers in a program being compiled; using such a compiler feature
+        allows you to rebuild your libraries without making source changes.
+        For instance the gcc command line
+    <pre>
+    gcc -include /usr/local/include/fcgi_stdio.h wonderlib.c
+    </pre>
+        causes gcc to include <tt>fcgi_stdio.h</tt> before it even
+        begins to read the module <tt>wonderlib.c</tt>.<p>
+    <li>
+        If your application passes <tt>FILE *</tt> to functions
+        implemented in libraries
+        for which you do not have source code, then
+        you'll need to include the headers for these libraries
+        <i>before</i> you include <tt>fcgi_stdio.h</tt>.
+        You can't pass the <tt>stdin</tt>,
+        <tt>stdout</tt>, or <tt>stderr</tt> streams produced by
+        <tt>FCGI_Accept</tt> to any functions implemented by these
+        libraries.  You can pass a stream on a Unix file to a library
+        function by following this pattern:
+    <pre>
+    FILE *myStream = fopen(path, "r");
+    answer = MungeStream(FCGI_ToFile(myStream));
+    </pre>
+        Here <tt>MungeStream</tt> is a library function that you can't
+        recompile and <tt>FCGI_ToFile</tt> is a macro that converts
+        from <tt>FCGI_FILE *</tt> to <tt>FILE *</tt>.  The macro
+        <tt>FCGI_ToFile</tt> is defined in <tt>fcgi_stdio.h</tt>.<p>
+</ul>
+
+
+<h4>Converting CGI programs</h4>
+
+The main task in converting a CGI program into a FastCGI program is
+separating the code that needs to execute once, initializing the
+program, from the code that needs to run for each request.
+In our tiny example, initializing the <tt>count</tt> variable
+is outside the loop, while incrementing the <tt>count</tt> variable
+goes inside.<p>
+
+Retained application state may be an issue.  You must ensure that
+any application state created in processing one request has no
+unintended effects on later requests.  FastCGI offers the possibility
+of significant application performance improvements, through caching;
+it is up to you to make the caches work
+correctly.<p>
+
+Storage leaks may be an issue.  Many CGI programs don't worry about
+storage leaks because the programs don't run for long enough for
+bloating to be a problem.  When converting to FastCGI, you
+can either use a tool such as
+<a href ="http://www.pure.com/"><i>Purify</i></a> from Pure Software
+to discover and fix storage
+leaks, or you can run a C garbage collector such as
+<a href ="http://www.geodesic.com/"><i>Great Circle</i></a>
+from Geodesic Systems.<p>
+
+
+<h4>Limitations</h4>
+
+Currently there are some limits to the compatibility provided
+by the <tt>fcgi_stdio</tt> library:<p>
+
+<ul type = square>
+    <li>
+        The library does not provide FastCGI versions of
+        the functions <tt>fscanf</tt> and <tt>scanf</tt>.  If you wish
+        to apply <tt>fscanf</tt> or <tt>scanf</tt> to <tt>stdin</tt>
+        of a FastCGI program, the workaround is to read lines or other
+        natural units into memory and then call <tt>sscanf</tt>.  If
+        you wish to apply <tt>fscanf</tt> to a stream on a Unix file,
+        the workaround is to follow the pattern:
+    <pre>
+    FILE *myStream = fopen(path, "r");
+    count = fscanf(FCGI_ToFile(myStream), format, ...);
+    </pre>
+</ul>
+
+<h4>Reference documentation</h4>
+
+The <a href = "FCGI_Accept.3"><tt>FCGI_Accept</tt> manpage</a>,
+<tt>doc/FCGI_Accept.3</tt>, describes the function in the traditional
+format.<p>
+
+The <a href = "FCGI_Finish.3"><tt>FCGI_Finish</tt></a>
+(<tt>doc/FCGI_Finish.3</tt>),
+<a href = "FCGI_SetExitStatus.3"><tt>FCGI_SetExitStatus</tt></a>
+(<tt>doc/FCGI_SetExitStatus.3</tt>), and
+<a href = "FCGI_StartFilterData.3"><tt>FCGI_StartFilterData</tt></a>
+(<tt>doc/FCGI_StartFilterData.3</tt>)
+manpages document capabilities of the <tt>fcgi-stdio</tt>
+library that are not illustrated above.<p>
+
+
+<h4><a NAME = "S3.2">3.2 Using the <tt>fcgiapp</tt> library</a></h4>
+
+The <tt>fcgiapp</tt> library is a second C library for FastCGI.  It
+does not provide the high degree of source code compatibility provided
+by <tt>fcgi_stdio</tt>; in return, it does not make such heavy use of
+<tt>#define</tt>.  <tt>fcgi_stdio</tt> is implemented as
+a thin layer on top of <tt>fcgiapp</tt>.<p>
+
+Applications built using the <tt>fcgiapp</tt> library cannot run
+as CGI programs; that feature is provided at the <tt>fcgi_stdio</tt>
+level.<p>
+
+Functions defined in <tt>fcgiapp</tt> are named using the prefix
+<tt>FCGX_</tt> rather than <tt>FCGI_</tt>.  For instance,
+<tt>FCGX_Accept</tt> is the <tt>fcgiapp</tt> version of
+<tt>FCGI_Accept</tt>.<p>
+
+Documentation of the <tt>fcgiapp</tt> library takes the form
+of extensive comments in the header file <tt>include/fcgiapp.h</tt>.
+The sample programs <tt>examples/tiny-fcgi2.c</tt> and
+<tt>examples/echo2.c</tt> illustrate how to use
+<tt>fcgiapp</tt>.<p>
+
+
+<h4><a NAME = "S3.3">3.3 Using Perl and Tcl</a></h4>
+
+A major advantage of the FastCGI approach to high-performance Web
+applications is its language-neutrality.  CGI scripts written in
+popular languages such as Perl and Tcl can be evolved into
+high-performance FastCGI applications.<p>
+
+We have produced FastCGI-integrated Perl and Tcl
+interpreters.  Doing so was easy, since Perl and Tcl
+are conventional C applications and <tt>fcgi_stdio</tt> was
+designed for converting conventional C applications.  Essentially no source
+code changes were required in these programs; a small amount
+of code was added in order to make <tt>FCGI_Accept</tt> and other
+FastCGI primitives available in these languages.  And because
+these interpreters were developed using <tt>fcgi_stdio</tt>, they
+run standard Perl and Tcl applications (e.g. CGI scripts) as well
+as FastCGI applications.<p>
+
+See the
+<a href = "http://www.fastcgi.com/applibs/">www.fastcgi.com/applibs</a>
+Web page for downloadable Perl and Tcl binaries for selected platforms.
+Because many users of Perl and Tcl run extended versions of these languages,
+the kit includes separate companion documents describing
+<a href ="fcgi-perl.htm">how to build FastCGI-integrated Perl</a>
+and <a href ="fcgi-tcl.htm">how to build FastCGI-integrated Tcl</a>.<p>
+
+Here are the Perl and Tcl versions of <tt>tiny-fcgi</tt>:<p>
+<pre>
+#!./perl
+use FCGI;
+$count = 0;
+while(FCGI::accept() >= 0) {
+    print("Content-type: text/html\r\n\r\n",
+          "&lt;title&gt;FastCGI Hello! (Perl)&lt;/title&gt;\n",
+          "&lt;h1&gt;FastCGI Hello! (Perl)&lt;/h1&gt;\n";
+          "Request number ",  ++$count,
+          " running on host &lt;i&gt";$env(SERVER_NAME)&lt;/i&gt;");
+}
+</pre>
+
+<pre>
+#!./tclsh
+set count 0 
+while {[FCGI_Accept] &gt;= 0 } {
+    incr count
+    puts -nonewline "Content-type: text/html\r\n\r\n"
+    puts "&lt;title&gt;FastCGI Hello! (Tcl)&lt;/title&gt;"
+    puts "&lt;h1&gt;FastCGI Hello! (Tcl)&lt;/h1&gt;"
+    puts "Request number $count running on host &lt;i&gt;$env(SERVER_NAME)&lt;/i&gt;"
+}
+</pre>
+
+Converting a Perl or Tcl CGI application to FastCGI is not fundamentally
+different from converting a C CGI application to FastCGI.  You separate
+the portion of the application that performs one-time
+initialization from the portion that performs per-request
+processing.  You put the per-request processing into a loop
+controlled by <tt>FCGI::accept</tt> (Perl) or <tt>FCGI_Accept</tt>
+(Tcl).
+
+
+
+<h4><a NAME = "S3.4">3.4 Using Java</a></h4>
+
+Java is not just for browser-based applets.  It is already suitable for
+writing some Web server applications, and its range of applicability will
+only grow as Java compilers and other Java tools improve.  Java's
+modules, garbage collection, and threads are especially valuable
+for writing long-lived application servers.<p>
+
+The <tt>FCGIInterface</tt> class provides facilities for Java
+applications analogous to what <tt>fcgi_stdio</tt> provides for C
+applications.  Using this library your Java application can run using
+either CGI or FastCGI.<p>
+
+The kit includes separate companion document on
+<a href ="fcgi-java.htm">using FastCGI with Java</a>.  The
+source code for FastCGI classes is contained in
+directory <tt>java/src</tt>
+and the compiled code in <tt>java/classes</tt>.<p>
+
+Here is the Java version of <tt>tiny-fcgi</tt>:<p>
+
+<pre>
+import FCGIInterface;
+
+class TinyFCGI {       
+    public static void main (String args[]) {          
+        int count = 0;
+        while(new FCGIInterface().FCGIaccept()>= 0) {
+            count ++;
+            System.out.println("Content-type: text/html\r\n\r\n");
+            System.out.println(
+                    "&lt;title&gt;FastCGI Hello! (Java)&lt;/title&gt;");
+            System.out.println("&lt;h1&gt;FastCGI Hello! (Java)&lt;/h1&gt;");
+            System.out.println(
+                    "request number " + count + " running on host &lt;i&gt;" +
+                    System.getProperty("SERVER_NAME") + "&lt;/i&gt;");
+        }
+    }
+}
+</pre>
+<p>
+
+
+<h3><a NAME = "S4">4. Running applications</a></h3>
+
+
+<h3><a NAME = "S4.1">4.1 Using a Web server that supports FastCGI</a></h3>
+
+For a current listing of Web servers that support FastCGI,
+see the <a HREF = "http://www.fastcgi.com/servers">www.fastcgi.com/servers</a>
+Web page.<p>
+
+All of the Web servers that support FastCGI perform management of
+FastCGI applications.  You don't need to start and stop FastCGI
+applications; the Web server takes care of this.  If an application
+process should crash, the Web server restarts it.<p>
+
+Web servers support FastCGI via new configuration directives.
+Since these directives are server-specific, get more information
+from the documentation that accompanies each server.<p>
+
+The directory
+<a HREF = "../examples/conf"><tt>examples/conf</tt></a> contains config
+files designed to run the example programs included in the FastCGI
+Developer's Kit.  Each config file contains specific installation
+instructions.<p>
+
+The more advanced example programs take advantage of special features
+of the Open Market Secure WebServer, such as anonymous ticketing
+and support for the Authorizer role.  If you don't have this server,
+download a
+<a HREF= "http://www.openmarket.com/store/eval/swsg.htm">free
+evaluation copy</a> to run the examples.<p>
+
+
+<h3><a NAME = "S4.2">4.2 Using <tt>cgi-fcgi</tt> with any Web server</a></h3>
+
+The program <tt>cgi-fcgi</tt> allows you to run FastCGI applications
+using any Web server that supports CGI.<p>
+
+Here is how <tt>cgi-fcgi</tt> works.  <tt>cgi-fcgi</tt> is a
+standard CGI program that uses Unix domain or TCP/IP sockets
+to communicate with
+a FastCGI application.  <tt>cgi-fcgi</tt> takes the path name
+or host/port name of a
+listening socket as a parameter and <tt>connect</tt>s to the FastCGI
+application listening on that socket.  <tt>cgi-fcgi</tt> then forwards the CGI
+environment variables and <tt>stdin</tt> data to the FastCGI
+application, and forwards the <tt>stdout</tt> and <tt>stderr</tt> data
+from the FastCGI application to the Web server.  When the FastCGI
+application signals the end of its response, <tt>cgi-fcgi</tt> flushes
+its buffers and exits.<p>
+
+Obviously, having <tt>cgi-fcgi</tt> is not as good as having
+a server with integrated FastCGI support:<p>
+
+<ul>
+  <li>Communication is slower than with a Web server that avoids the
+      fork/exec overhead on every FastCGI request.
+
+  <li><tt>cgi-fcgi</tt> does not perform application management,
+      so you need to provide this yourself.
+
+  <li><tt>cgi-fcgi</tt> supports only the Responder role.
+</ul>
+
+But <tt>cgi-fcgi</tt> does allow you to
+develop applications that retain state in memory between connections,
+which often provides a major performance boost over normal CGI.  And
+all the applications you develop using <tt>cgi-fcgi</tt>
+will work with Web servers that have integrated support for
+FastCGI.<p>
+
+The file <tt>examples/tiny-fcgi.cgi</tt> demonstrates
+a way to use <tt>cgi-fcgi</tt> to run a typical application,
+in this case the <tt>examples/tiny-fcgi</tt> application:
+
+<pre>
+    #!../cgi-fcgi/cgi-fcgi -f
+    -connect sockets/tiny-fcgi tiny-fcgi
+</pre>
+
+On most Unix platforms, executing this command-interpreter file runs
+<tt>cgi-fcgi</tt> with arguments <tt>-f</tt> and
+<tt>examples/tiny-fcgi.cgi</tt>.  (Beware: On some
+Unix platforms, including HP-UX, the first line of a command-interpreter file
+cannot contain more than 32 characters, including the newline;
+you may need to install the <tt>cgi-fcgi</tt> application in a standard
+place like <tt>/usr/local/bin</tt> or create a symbolic link
+to the <tt>cgi-fcgi</tt> application in the directory containing
+your application.)
+The <tt>cgi-fcgi</tt> program reads the command-interpreter file and
+connects to the FastCGI application whose listening socket is
+<tt>examples/sockets/tiny-fcgi</tt>.<p>
+
+Continuing the example, if
+<tt>cgi-fcgi</tt>'s connection attempt fails, it
+creates a new process
+running the program <tt>examples/tiny-fcgi</tt> and listening on
+socket <tt>examples/sockets/tiny-fcgi</tt>.  Then <tt>cgi-fcgi</tt>
+retries the connection attempt, which now should succeed.<p>
+
+The <tt>cgi-fcgi</tt> program has two other modes of operation.
+In one mode it connects to applications but does not start them;
+in the other it starts applications but does not connect
+to them.  These modes are required when using TCP/IP.  The
+<a href = "cgi-fcgi.1"><tt>cgi-fcgi</tt> manpage</a>,
+<tt>doc/cgi-fcgi.1</tt>, tells the full story.<p>
+
+To run the example applications using <tt>cgi-fcgi</tt>, start your
+Web server and give it the directory <tt>fcgi-devel-kit</tt> as the
+root of its URL space.  If the machine running your server is called
+<tt>bowser</tt> and your server is running on port <tt>8888</tt>,
+you'd then open the URL <tt>http://bowser:8888/index.html</tt> to
+reach the kit's index page.  Now the links on the index page that run
+example applications via <tt>cgi-fcgi</tt> should be active.<p>
+
+
+
+<h3><a NAME = "S5">5. Known problems</a></h3>
+
+On Digital UNIX 3.0 there's a problem with Unix domain listening
+sockets on NFS file systems.  The symptom when using cgi-fcgi is an
+exit status of 38 (<tt>ENOTSOCK</tt>: socket operation on non-socket),
+but cgi-fcgi may dump core in this case when compiled optimized.
+Work-around: Store your Unix domain listening
+sockets on a non NFS file system, upgrade to Digital UNIX 3.2, or use
+TCP sockets.<p>
+
+On AIX there's a problem with shared listening sockets.
+The symptoms can include application core dumps and kernel panic.
+Work-around: Run a single FastCGI application server per listening
+socket.<p>
+
+
+
+<h3><a NAME = "S6">6. Getting support</a></h3>
+
+The mailing list <tt>fastcgi-developers@openmarket.com</tt>
+is used for discussions of issues in developing FastCGI applications.
+Topics include announcement of FastCGI-capable Web servers or
+changes to such servers, announcement of new application libraries
+or changes to such libraries, announcement of known bugs, discussion
+of design trade-offs in FastCGI application programming, and discussion
+of development plans and experiences.  To join the
+list, send a message to
+<a href = "mailto:fastcgi-developers-request@openmarket.com">fastcgi-developers-request@openmarket.com</a>
+with a message body consisting of the word "subscribe"
+(leaving off the quotes).<p>
+
+Mail sent to this list is archived and available on the
+World-Wide Web at
+<pre>
+    http://www.fastcgi.com/mail/
+</pre>
+
+Open Market Secure WebServer customers get FastCGI
+support where they get server support.<p>
+
+
+
+<hr>
+
+<address>
+&#169 1996, Open Market, Inc. / mbrown@openmarket.com
+</address>
+
+</body>
+</html>
diff --git a/doc/fcgi-java.gut b/doc/fcgi-java.gut
new file mode 100644 (file)
index 0000000..cd376bd
--- /dev/null
@@ -0,0 +1,425 @@
+Integrating FastCGI with Java
+/fastcgi/words
+fcgi-hd.gif
+[FastCGI]
+<center>Integrating FastCGI with Java</center>
+
+<!--Copyright (c) 1996 Open Market, Inc.                                    -->
+<!--See the file "LICENSE.TERMS" for information on usage and redistribution-->
+<!--of this file, and for a DISCLAIMER OF ALL WARRANTIES.                   -->
+<!-- $Id: fcgi-java.gut,v 1.1 1997/09/16 15:36:26 stanleyg Exp $ -->
+
+<P ALIGN=CENTER>
+Steve Harris
+<BR>
+Open Market, Inc.
+<BR>
+<EM>7 May 1996</EM>
+</P>
+
+<h5 align=center>
+Copyright &copy; 1996 Open Market, Inc.  245 First Street, Cambridge,
+  MA 02142 U.S.A.<br>
+Tel: 617-621-9500 Fax: 617-621-1703 URL:
+  <a href="http://www.openmarket.com/">http://www.openmarket.com/</a><br>
+</h5>
+<hr>
+
+<H3><A NAME = "S1"> 1. Introduction</A></H3>
+Java is an object-oriented programming language developed by Sun
+Microsystems.  The Java Depvelopers Kit (JDK), which contains the basic
+Java class packages, is available from Sun in both source and binary
+forms at Sun's
+<a href="http://java.sun.com/java.sun.com/JDK-1.0/index.html">JavaSoft</a>
+site.  This document assumes that you have some familiarity with the
+basics of compiling and running Java programs. 
+<p>
+There are two kinds of applications built using Java.
+<ul>
+  <li> <i>Java Applets</i> are graphical components which are run off
+       HTML pages via the <tt>&lt;APPLET&gt;</tt> HTML extention tag.<br><br>
+       
+  <li> <i>Java Applications (Apps) </i> are stand-alone programs
+       that are run by invoking the Java interpreter directly. Like
+       C programs, they have a <tt>main()</tt> method which the interpreter
+       uses as an entry point.
+</ul>
+The initial emphasis on using Java for client side applets should not
+obscure the fact that Java is a full strength programming language
+which can be used to develop server side stand alone applications,
+including CGI and now FastCGI applications.
+<p>
+The remainder of this document explains how to write and run FastCGI Java
+applications. It also illustrates the conversion of a sample Java CGI program
+to a FastCGI program.
+
+
+
+<H3><A NAME = "S2"> 2. Writing FastCGI applications in Java</A></H3>
+
+Writing a FastCGI application in Java is as simple as writing one in C.
+
+<ol>
+  <li> Import the <tt>FCGIInterface</tt> class.
+  <li> Perform one-time initialization at the top of the
+       <tt>main()</tt> method.
+  <li> Create a new <tt>FCGIInterface</tt> object and send it an
+       <tt>FCGIaccept()</tt> message in a loop.
+  <li> Put the per-request application code inside that loop.
+</ol>
+
+On return from <tt>FCGIaccept()</tt> you can access the request's environment
+variables using <tt>System.getProperty</tt> and perform request-related
+I/O through the standard variables <tt>System.in</tt>,
+<tt>System.out</tt>, and <tt>System.err</tt>.<p>
+
+To illustrate these points, the kit includes <tt>examples/TinyCGI</tt>,
+a CGI Java application, and <tt>examples/TinyFCGI</tt>, the FastCGI
+version of TinyCGI.  These programs perform the same
+functions as the C programs <tt>examples/tiny-cgi.c</tt> and
+<tt>examples/tiny-fcgi.c</tt> that are used as examples in the
+<A HREF="fcgi-devel-kit.htm#S3.1.1">FastCGI Developer's Kit document</A>.
+
+
+<h4>A. TinyCGI</h4>
+<PRE> 
+class TinyCGI {        
+       public static void main (String args[]) {               
+               int count = 0;
+                ++count;
+               System.out.println("Content-type: text/html\n\n");
+               System.out.println("&lt;html&gt;");
+               System.out.println(
+                       "&lt;head&gt;&lt;TITLE&gt;CGI-Hello&lt;/TITLE&gt;&lt;/head&gt;");
+               System.out.println("&lt;body&gt;");
+               System.out.println("&lt;H3&gt;CGI Hello&lt;/H3&gt;");
+               System.out.println("request number " + count + 
+                                       " running on host " 
+                               + System.getProperty&lt;"SERVER_NAME"));
+               System.out.println("&lt;/body&gt;");
+               System.out.println("&lt;/html&gt;"); 
+               }
+       }
+
+</PRE>
+<h4>B. TinyFCGI</h4>
+<PRE> 
+import FCGIInterface;
+
+class TinyFCGI {       
+       public static void main (String args[]) {               
+               int count = 0;
+               while(new FCGIInterface().FCGIaccept()>= 0) {
+                       count ++;
+                       System.out.println("Content-type: text/html\n\n");
+                       System.out.println("&lt;html&gt;");
+                       System.out.println(
+                         "&lt;head&gt;&lt;TITLE&gt;FastCGI-Hello Java stdio&lt;/TITLE&gt;&lt;/head&gt;");
+                       System.out.println("&lt;body&gt;");
+                       System.out.println("&lt;H3&gt;FastCGI-Hello Java stdio&lt;/H3&gt;");
+                       System.out.println("request number " + count + 
+                                       " running on host " 
+                               + System.getProperty&lt;"SERVER_NAME"));
+                       System.out.println("&lt;/body&gt;");
+                       System.out.println("&lt;/html&gt;"); 
+                       }
+               }
+       }
+
+</PRE>
+<h4>C. Running these Examples</h4>
+
+We assume that you have downloaded the JDK and the FastCGI Developer's
+Kit, and that you have a Web server running that can access the
+<tt>fcgi-devel-kit/examples</tt> directory. In all cases where we
+specify paths, we are using relative paths within
+<tt>fcgi-devel-kit</tt> or the JDK which will need to be enlarged to a
+full path by the user.
+
+<h5>Configuring</h5>
+<ol>
+  <li> Add your JDK's <tt>java/bin</tt> directory to your Unix <tt>PATH</tt>
+       if it isn't there already.<br><br>
+       
+  <li> Add the directories <tt>fcgi-devel-kit/examples</tt> and
+       <tt>fcgi-devel-kit/java/classes</tt> to your Java
+       <tt>CLASSPATH</tt>.<br><br>
+       
+  <li>In your Open Market Secure WebServer configuration file,
+      <tt>httpd.config</tt>, add the following two lines:<br><br>
+       
+       <tt>ExternalAppClass TinyFCGI -host </tt><i>hostName:portNum</i><br>
+       <tt>Responder TinyFCGI fcgi-devel-kit/examples/TinyFCGI</tt><br><br>
+              
+       <ul>
+       <li><i>hostName</i> is the name of your host machine.<br>
+       <li><i>portNum</i> is the port that you've selected for
+            communication between the Web server and the Java application.<br>
+       </ul><br>
+
+       On other servers you can use <tt>cgi-fcgi</tt> to get a
+       similar effect.
+       
+  <li> Create a soft link <tt>examples/javexe</tt> to the
+       <tt>java/bin</tt> directory in your JDK.
+       This link is required only to run the
+       CGI scripts <tt>examples/TinyCGI.cgi</tt> and
+       <tt>examples/TinyFCGI.cgi</tt>, which use it to
+       invoke the Java interpreter <tt>java/bin/java</tt>.
+       It is not used by FastCGI applications.
+
+   <li> You might have to modify <tt>examples/TinyFCGI.cgi</tt> to use a
+       Unix shell for which your CLASSPATH is defined.
+</ol>
+
+<h5> Running </h5>
+
+<ul>
+  <li> To run TinyFCGI as FastCGI, you invoke the Java interpreter
+       with the -D option, giving it the <tt>FCGI_PORT</tt> environment
+       variable
+       and the same <i>portNum</i> that was used in the Web server
+       configuration. The command is:
+       <br><br>
+       <tt>java -DFCGI_PORT=portNum TinyFCGI</tt>
+       <br><br>
+       Then point your browser at <tt>fcgi-devel-kit/examples/TinyFCGI</tt>.
+       Notice that each time you reload, the count increments.<br><br>
+       
+  <li> To run TinyCGI, point your browser at
+       <tt>fcgi-devel-kit/examples/TinyCGI.cgi</tt> on your host machine.
+       Notice that the count does not increment.<br><br>
+
+  <li> Finally, you can run TinyFCGI as a straight CGI program by pointing
+       your browser at <tt>fcgi-devel-kit/examples/TinyFCGI.cgi.</tt> The results
+       are exactly the same as when you ran TinyCGI. Invoking a FastCGI
+       program without an <tt>FCGI_PORT</tt> parameter tells the
+       FastCGI interface
+       to leave the normal CGI environment in place.
+</ul>
+<p>
+Due to gaps in the Java interpreter's support for listening
+sockets, Java FastCGI applications are currently limited to
+being started as external applications.  They can't be started and
+managed by the Web server because they are incapable of using
+a listening socket that the Web server creates.
+
+
+
+<H3><A NAME = "S3"> 3. Standard I/O and Application Libraries</A></H3>
+
+As we have seen above, FastCGI for Java offers a redefinition
+of standard I/O corresponding to the the <i>fcgi_stdio</i> functionality.
+It also offers a set of directly callable I/O methods corresponding to
+the <i>fcgiapp</i> C library. To understand where these methods occur
+we need to look briefly at the FastCGI redefinition of standard I/O.<p>
+
+Java defines standard I/O in the <i>java.System</i> class as follows:<p>
+
+public static InputStream in = new BufferedInputStream(new FileInputStream(FileDescriptor.in), 128);<br>
+public static PrintStream out = new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.out), 128), true);<br>
+public static PrintStream err = new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.err), 128), true);<p>
+
+The File Descriptors <i>in</i>, <i>out</i>, <i>err</i> are constants set to 0, 1 and 2 respectively.<p>
+
+The FastCGI interface redefines <i>java.System in, out</i>, and <i>err</i>
+by replacing the File streams with Socket streams and inserting streams
+which know how to manage the FastCGI protocol between the Socket streams
+and the Buffered streams in the above definitions.
+<p>
+For those cases where the FCGI application needs to bypass the standard I/O
+streams, it can directly access the methods of the FCGI input and output
+streams which roughly correspond to the functions in the C <i>fcgiapp</i>
+library. These streams can be accessed via the <i>request</i> class variable
+in FCGIInterface. Each Request object has instance variables that refer to an
+FCGIInputStream, and to two FCGIOutputStreams associated with that request.
+
+<H3><A NAME = "S4"> 4. Environment Variables</A></H3>
+
+Java does not use the  C <i/>environ<i/> list. Nor is there a <i>getenv</i>
+command that reads system environment variables. This is intentional for
+reasons of portability and security. Java has an internal dictionary of
+properties which belongs to the System class. These System properties
+are <i>name/value</i> associations that constitute the Java environment.
+When a Java application starts up, it reads in a file with default properties.
+As we have seen, additional System properties may be inserted by using
+the -D <i>Java</i> command argument.<p>
+
+For CGI, where the Java application is invoked from a .cgi script that,
+in turn, invokes the Java interpreter, this script could read the environment
+and pass the variables to the Java application either by writing a file
+or by creating -D options on the fly. Both of these methods are somewhat
+awkward.<p>
+
+For FastCGI Java applications, the environment variables are obtained from
+the FastCGI web server via <tt>FCGI_PARAMS</tt> records that are sent to the
+application at the start of each request. The FastCGI interface stores the
+original startup properties, combines these with the properties obtained
+from the server, and puts the new set of properties in the System properties
+dictionary. The only parameter that has to be specifically added at startup
+time is the FCGI_PORT parameter for the Socket creation.  In the future, we
+expect that even this parameter won't be needed, since its use is due to an
+acknowledged rigidity in the JDK's implementation of sockets.<p>
+
+<H3><A NAME = "S4"> 5. Further examples: EchoFCGI and Echo2FCGI</A></H3>
+
+The next two examples illustrate the points made in the last two sections.
+EchoFCGI and Echo2FCGI both echo user input and display the application's
+environment variables. EchoFCGI reads the user input from System.in, while
+Echo2FCGI reads the user input directly from the intermediate FastCGI input
+stream.
+
+<h4>A. EchoFCGI</h4>
+<pre>
+import FCGIInterface;
+import FCGIGlobalDefs;
+import java.io.*;
+
+class EchoFCGI {
+       
+       public static void main (String args[]) {
+               int status = 0;
+               while(new FCGIInterface().FCGIaccept()>= 0) {
+               System.out.println("Content-type: text/html\n\n");
+                       System.out.println("&lt;html&gt;");
+                       System.out.println(
+                               "&lt;head%gt;&lt;TITLE&gt;FastCGI echo
+                                      &lt;/TITLE&gt;&lt;/head&gt;");
+                       System.out.println("&lt;body&gt;");     
+                       System.out.println(
+                                         "&lt;H2&gt;FastCGI echo&lt;/H2&gt;");
+                       System.out.println("&lt;H3&gt;STDIN&lt;/H3&gt;");
+                       for ( int c = 0; c != -1; ) {
+                               try {
+                                       c = System.in.read();
+                               } catch(IOException e) {
+                                       System.out.println(
+                                       "&lt;br&gt;&lt;b&gt;SYSTEM EXCEPTION");
+                                       Runtime rt = Runtime.getRuntime();
+                                       rt.exit(status);
+                                       }
+                               if (c != -1) {  
+                                       System.out.print((char)c);
+                                       }
+                               }
+                       System.out.println(
+                               "&lt;H3&gt;Environment Variables:&lt;/H3&gt;");
+       
+                       System.getProperties().list(System.out);
+                       System.out.println("&lt;/body&gt;");
+                       System.out.println("&lt;/html&gt;");
+                       }
+               }
+       }
+</pre>
+<h4>B. Echo2FCGI</h4>
+<pre>
+import FCGIInterface;
+import FCGIGlobalDefs;
+import FCGIInputStream;
+import FCGIOutputStream;
+import FCGIMessage;
+import FCGIRequest;
+import java.io.*;
+
+class Echo2FCGI {
+
+       public static void main (String args[]) {
+               int status = 0;
+                FCGIInterface intf = new FCGIInterface();
+               while(intf.FCGIaccept()>= 0) {
+               System.out.println("Content-type: text/html\n\n");
+                       System.out.println("&lt;html&gt;");
+                       System.out.println(
+                               "&lt;head&gt;&lt;TITLE&gt;FastCGI echo
+                                    &lt;/TITLE&gt;&lt;/head&gt;");
+                       System.out.println("&lt;body&gt;");                     
+                       System.out.println("&lt;H2&gt;FastCGI echo&lt;/H2&gt;");
+                       System.out.println("&lt;H3&gt;STDIN:&lt;/H3"&gt;);
+                       for ( int c = 0; c != -1; ) {
+                               try {
+                                       c = intf.request.inStream.read();
+                               } catch(IOException e) {
+                                       System.out.println(
+                                       "&lt;br&gt;&lt;b&gt;SYSTEM EXCEPTION");
+                                       Runtime rt = Runtime.getRuntime();
+                                       rt.exit(status);
+                                       }
+                               if (c != -1) {  
+                                       System.out.print((char)c);
+                                       }
+                               }
+                       System.out.println(
+                               "&lt;H3&gt;Environment Variables:&lt;/H3&gt;");
+       
+                       System.getProperties().list(System.out);
+                       System.out.println(&lt;"/body&gt;");
+                       System.out.println("&lt;/html&gt;");
+                       }
+               }
+       }
+</pre>
+<h4>C. Running these Examples</h4>
+
+<h5>Configuring</h5>
+
+As with TinyFCGI, you need to configure the web server to recognize these
+two FastCGI applications. Your configuration now looks like this:<p> 
+<pre>
+ExternalAppClass java1 -host hostname:portNum
+Responder java1 fcgi-devel-kit/examples/TinyFCGI
+ExternalAppClass java2 -host hostname:portNumA
+Responder java2 fcgi-devel-kit/examples/EchoFCGI
+ExternalAppClass java3 -host hostname:porNumB
+Responder java3 fcgi-devel-kit/examples/Echo2FCGI
+</pre>
+<p>
+Note that the application classes and port numbers are different for each
+application.
+
+<h5>Running</h5>
+
+As with TinyFCGI, you need to run these programs with the -D option
+using FCGI_PORT and the appropriate port number.
+
+To get some data for standard input we have created two html pages with
+forms that use a POST method. These are echo.html and echo2.html. You must
+edit these .html files to expand the path to <i>fcgi-devel-kit/examples</i>
+to a full path. Once the appropriate Java program is running, point your browser at the corresponding HTML page, enter some data and select the <i>go_find</i> button.
+
+
+<H3><A NAME = "S6"> 6. FastCGI Java Classes</A></H3>
+
+The Java FastCGI classes are included in both source and byte code format in
+<i>fcgi-devel-kit/java/src</i> and :<i>fcgi-devel-kit/java/classes</i>
+respectively. The following is a brief description of these classes:<p>
+
+<dl>
+<dt><i>FCGIInterface</i><dd> This class contains the FCGIaccept method called
+     by the FastCGI user application. This method sets up the appropriate
+     FastCGI environment for communication with the web server and manages
+     FastCGI requests.<br>
+     
+ <dt><i>FCGIInputStream</i><dd> This input stream manages FastCGI
+      internal buffers to ensure that the user gets all of the FastCGI
+      messages associated with a request. It uses FCGIMessage objects
+      to interpret these incoming messages.<br>
+      
+  <dt><i>FCGIOutputStream</i><dd> This output stream manages FastCGI
+       internal buffers to send user data back to the web server
+       and to notify the server of various FCGI protocol conditions.
+       It uses FCGIMessage objects to format outgoing FastCGI messages.<br>
+      
+<dt><i>FCGIMessage</i><dd> This is the only class that understands the
+     actual structure of the FastCGI messages. It interprets incoming
+     FastCGI records and constructs outgoing ones..<br>
+     
+<dt><i>FCGIRequest</i><dd>This class currently contains data fields
+     used by FastCGI to manage user requests.  In a multi-threaded
+     version of FastCGI, the role of this class will be expanded.<br>
+
+<dt><i>FCGIGlobalDefs</i><dd>This class contains definitions of FastCGI
+     constants.
+</dl>
+<HR>
+<ADDRESS><A HREF="mailto:harris@openmarket.com">Steve Harris // harris@openmarket.com</A></ADDRESS>
diff --git a/doc/fcgi-java.htm b/doc/fcgi-java.htm
new file mode 100644 (file)
index 0000000..e0aced2
--- /dev/null
@@ -0,0 +1,435 @@
+<html>
+<head><title>Integrating FastCGI with Java</title>
+</head>
+
+<body bgcolor="#FFFFFF" text="#000000" link="#cc0000" alink="#000011" 
+vlink="#555555">
+
+<center>
+<a href="/fastcgi/words">
+    <img border=0 src="../images/fcgi-hd.gif" alt="[[FastCGI]]"></a>
+</center>
+<br clear=all>
+<h3><center>Integrating FastCGI with Java</center></h3>
+
+<!--Copyright (c) 1996 Open Market, Inc.                                    -->
+<!--See the file "LICENSE.TERMS" for information on usage and redistribution-->
+<!--of this file, and for a DISCLAIMER OF ALL WARRANTIES.                   -->
+<!-- $Id: fcgi-java.htm,v 1.1 1997/09/16 15:36:26 stanleyg Exp $ -->
+
+<P ALIGN=CENTER>
+Steve Harris
+<BR>
+Open Market, Inc.
+<BR>
+<EM>7 May 1996</EM>
+</P>
+
+<h5 align=center>
+Copyright &copy; 1996 Open Market, Inc.  245 First Street, Cambridge,
+  MA 02142 U.S.A.<br>
+Tel: 617-621-9500 Fax: 617-621-1703 URL:
+  <a href="http://www.openmarket.com/">http://www.openmarket.com/</a><br>
+</h5>
+<hr>
+
+<H3><A NAME = "S1"> 1. Introduction</A></H3>
+Java is an object-oriented programming language developed by Sun
+Microsystems.  The Java Depvelopers Kit (JDK), which contains the basic
+Java class packages, is available from Sun in both source and binary
+forms at Sun's
+<a href="http://java.sun.com/java.sun.com/JDK-1.0/index.html">JavaSoft</a>
+site.  This document assumes that you have some familiarity with the
+basics of compiling and running Java programs. 
+<p>
+There are two kinds of applications built using Java.
+<ul>
+  <li> <i>Java Applets</i> are graphical components which are run off
+       HTML pages via the <tt>&lt;APPLET&gt;</tt> HTML extention tag.<br><br>
+       
+  <li> <i>Java Applications (Apps) </i> are stand-alone programs
+       that are run by invoking the Java interpreter directly. Like
+       C programs, they have a <tt>main()</tt> method which the interpreter
+       uses as an entry point.
+</ul>
+The initial emphasis on using Java for client side applets should not
+obscure the fact that Java is a full strength programming language
+which can be used to develop server side stand alone applications,
+including CGI and now FastCGI applications.
+<p>
+The remainder of this document explains how to write and run FastCGI Java
+applications. It also illustrates the conversion of a sample Java CGI program
+to a FastCGI program.
+
+
+
+<H3><A NAME = "S2"> 2. Writing FastCGI applications in Java</A></H3>
+
+Writing a FastCGI application in Java is as simple as writing one in C.
+
+<ol>
+  <li> Import the <tt>FCGIInterface</tt> class.
+  <li> Perform one-time initialization at the top of the
+       <tt>main()</tt> method.
+  <li> Create a new <tt>FCGIInterface</tt> object and send it an
+       <tt>FCGIaccept()</tt> message in a loop.
+  <li> Put the per-request application code inside that loop.
+</ol>
+
+On return from <tt>FCGIaccept()</tt> you can access the request's environment
+variables using <tt>System.getProperty</tt> and perform request-related
+I/O through the standard variables <tt>System.in</tt>,
+<tt>System.out</tt>, and <tt>System.err</tt>.<p>
+
+To illustrate these points, the kit includes <tt>examples/TinyCGI</tt>,
+a CGI Java application, and <tt>examples/TinyFCGI</tt>, the FastCGI
+version of TinyCGI.  These programs perform the same
+functions as the C programs <tt>examples/tiny-cgi.c</tt> and
+<tt>examples/tiny-fcgi.c</tt> that are used as examples in the
+<A HREF="fcgi-devel-kit.htm#S3.1.1">FastCGI Developer's Kit document</A>.
+
+
+<h4>A. TinyCGI</h4>
+<PRE> 
+class TinyCGI {        
+       public static void main (String args[]) {               
+               int count = 0;
+                ++count;
+               System.out.println("Content-type: text/html\n\n");
+               System.out.println("&lt;html&gt;");
+               System.out.println(
+                       "&lt;head&gt;&lt;TITLE&gt;CGI Hello&lt;/TITLE&gt;&lt;/head&gt;");
+               System.out.println("&lt;body&gt;");
+               System.out.println("&lt;H3&gt;CGI-Hello&lt;/H3&gt;");
+               System.out.println("request number " + count + 
+                                       " running on host " 
+                               + System.getProperty&lt;"SERVER_NAME"));
+               System.out.println("&lt;/body&gt;");
+               System.out.println("&lt;/html&gt;"); 
+               }
+       }
+
+</PRE>
+<h4>B. TinyFCGI</h4>
+<PRE> 
+import FCGIInterface;
+
+class TinyFCGI {       
+       public static void main (String args[]) {               
+               int count = 0;
+               while(new FCGIInterface().FCGIaccept()>= 0) {
+                       count ++;
+                       System.out.println("Content-type: text/html\n\n");
+                       System.out.println("&lt;html&gt;");
+                       System.out.println(
+                         "&lt;head&gt;&lt;TITLE&gt;FastCGI-Hello Java stdio&lt;/TITLE&gt;&lt;/head&gt;");
+                       System.out.println("&lt;body&gt;");
+                       System.out.println("&lt;H3&gt;FastCGI-HelloJava stdio&lt;/H3&gt;");
+                       System.out.println("request number " + count + 
+                                       " running on host " 
+                               + System.getProperty&lt;"SERVER_NAME"));
+                       System.out.println("&lt;/body&gt;");
+                       System.out.println("&lt;/html&gt;"); 
+                       }
+               }
+       }
+
+</PRE>
+<h4>C. Running these Examples</h4>
+
+We assume that you have downloaded the JDK and the FastCGI Developer's
+Kit, and that you have a Web server running that can access the
+<tt>fcgi-devel-kit/examples</tt> directory. In all cases where we
+specify paths, we are using relative paths within
+<tt>fcgi-devel-kit</tt> or the JDK which will need to be enlarged to a
+full path by the user.
+
+<h5>Configuring</h5>
+<ol>
+  <li> Add your JDK's <tt>java/bin</tt> directory to your Unix <tt>PATH</tt>
+       if it isn't there already.<br><br>
+       
+  <li> Add the directories <tt>fcgi-devel-kit/examples</tt> and
+       <tt>fcgi-devel-kit/java/classes</tt> to your Java
+       <tt>CLASSPATH</tt>.<br><br>
+       
+  <li>In your Open Market Secure WebServer configuration file,
+      <tt>httpd.config</tt>, add the following two lines:<br><br>
+       
+       <tt>ExternalAppClass TinyFCGI -host </tt><i>hostName:portNum</i><br>
+       <tt>Responder TinyFCGI fcgi-devel-kit/examples/TinyFCGI</tt><br><br>
+              
+       <ul>
+       <li><i>hostName</i> is the name of your host machine.<br>
+       <li><i>portNum</i> is the port that you've selected for
+            communication between the Web server and the Java application.<br>
+       </ul><br>
+
+       On other servers you can use <tt>cgi-fcgi</tt> to get a
+       similar effect.
+       
+  <li> Create a soft link <tt>examples/javexe</tt> to the
+       <tt>java/bin</tt> directory in your JDK.
+       This link is required only to run the
+       CGI scripts <tt>examples/TinyCGI.cgi</tt> and
+       <tt>examples/TinyFCGI.cgi</tt>, which use it to
+       invoke the Java interpreter <tt>java/bin/java</tt>.
+       It is not used by FastCGI applications.
+
+   <li> You might have to modify <tt>examples/TinyFCGI.cgi</tt> to use a
+       Unix shell for which your CLASSPATH is defined.
+</ol>
+
+<h5> Running </h5>
+
+<ul>
+  <li> To run TinyFCGI as FastCGI, you invoke the Java interpreter
+       with the -D option, giving it the <tt>FCGI_PORT</tt> environment
+       variable
+       and the same <i>portNum</i> that was used in the Web server
+       configuration. The command is:
+       <br><br>
+       <tt>java -DFCGI_PORT=portNum TinyFCGI</tt>
+       <br><br>
+       Then point your browser at <tt>fcgi-devel-kit/examples/TinyFCGI</tt>.
+       Notice that each time you reload, the count increments.<br><br>
+       
+  <li> To run TinyCGI, point your browser at
+       <tt>fcgi-devel-kit/examples/TinyCGI.cgi</tt> on your host machine.
+       Notice that the count does not increment.<br><br>
+
+  <li> Finally, you can run TinyFCGI as a straight CGI program by pointing
+       your browser at <tt>fcgi-devel-kit/examplesi/TinyFCGI.cgi.</tt> The results
+       are exactly the same as when you ran TinyCGI. Invoking a FastCGI
+       program without an <tt>FCGI_PORT</tt> parameter tells the
+       FastCGI interface
+       to leave the normal CGI environment in place.
+</ul>
+<p>
+Due to gaps in the Java interpreter's support for listening
+sockets, Java FastCGI applications are currently limited to
+being started as external applications.  They can't be started and
+managed by the Web server because they are incapable of using
+a listening socket that the Web server creates.
+
+
+
+<H3><A NAME = "S3"> 3. Standard I/O and Application Libraries</A></H3>
+
+As we have seen above, FastCGI for Java offers a redefinition
+of standard I/O corresponding to the the <i>fcgi_stdio</i> functionality.
+It also offers a set of directly callable I/O methods corresponding to
+the <i>fcgiapp</i> C library. To understand where these methods occur
+we need to look briefly at the FastCGI redefinition of standard I/O.<p>
+
+Java defines standard I/O in the <i>java.System</i> class as follows:<p>
+
+public static InputStream in = new BufferedInputStream(new FileInputStream(FileDescriptor.in), 128);<br>
+public static PrintStream out = new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.out), 128), true);<br>
+public static PrintStream err = new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.err), 128), true);<p>
+
+The File Descriptors <i>in</i>, <i>out</i>, <i>err</i> are constants set to 0, 1 and 2 respectively.<p>
+
+The FastCGI interface redefines <i>java.System in, out</i>, and <i>err</i>
+by replacing the File streams with Socket streams and inserting streams
+which know how to manage the FastCGI protocol between the Socket streams
+and the Buffered streams in the above definitions.
+<p>
+For those cases where the FCGI application needs to bypass the standard I/O
+streams, it can directly access the methods of the FCGI input and output
+streams which roughly correspond to the functions in the C <i>fcgiapp</i>
+library. These streams can be accessed via the <i>request</i> class variable
+in FCGIInterface. Each Request object has instance variables that refer to an
+FCGIInputStream, and to two FCGIOutputStreams associated with that request.
+
+<H3><A NAME = "S4"> 4. Environment Variables</A></H3>
+
+Java does not use the  C <i/>environ<i/> list. Nor is there a <i>getenv</i>
+command that reads system environment variables. This is intentional for
+reasons of portability and security. Java has an internal dictionary of
+properties which belongs to the System class. These System properties
+are <i>name/value</i> associations that constitute the Java environment.
+When a Java application starts up, it reads in a file with default properties.
+As we have seen, additional System properties may be inserted by using
+the -D <i>Java</i> command argument.<p>
+
+For CGI, where the Java application is invoked from a .cgi script that,
+in turn, invokes the Java interpreter, this script could read the environment
+and pass the variables to the Java application either by writing a file
+or by creating -D options on the fly. Both of these methods are somewhat
+awkward.<p>
+
+For FastCGI Java applications, the environment variables are obtained from
+the FastCGI web server via <tt>FCGI_PARAMS</tt> records that are sent to the
+application at the start of each request. The FastCGI interface stores the
+original startup properties, combines these with the properties obtained
+from the server, and puts the new set of properties in the System properties
+dictionary. The only parameter that has to be specifically added at startup
+time is the FCGI_PORT parameter for the Socket creation.  In the future, we
+expect that even this parameter won't be needed, since its use is due to an
+acknowledged rigidity in the JDK's implementation of sockets.<p>
+
+<H3><A NAME = "S4"> 5. Further examples: EchoFCGI and Echo2FCGI</A></H3>
+
+The next two examples illustrate the points made in the last two sections.
+EchoFCGI and Echo2FCGI both echo user input and display the application's
+environment variables. EchoFCGI reads the user input from System.in, while
+Echo2FCGI reads the user input directly from the intermediate FastCGI input
+stream.
+
+<h4>A. EchoFCGI</h4>
+<pre>
+import FCGIInterface;
+import FCGIGlobalDefs;
+import java.io.*;
+
+class EchoFCGI {
+       
+       public static void main (String args[]) {
+               int status = 0;
+               while(new FCGIInterface().FCGIaccept()>= 0) {
+               System.out.println("Content-type: text/html\n\n");
+                       System.out.println("&lt;html&gt;");
+                       System.out.println(
+                               "&lt;head%gt;&lt;TITLE&gt;FastCGI echo
+                                      &lt;/TITLE&gt;&lt;/head&gt;");
+                       System.out.println("&lt;body&gt;");     
+                       System.out.println(
+                                         "&lt;H2&gt;FastCGI echo&lt;/H2&gt;");
+                       System.out.println("&lt;H3&gt;STDIN&lt;/H3&gt;");
+                       for ( int c = 0; c != -1; ) {
+                               try {
+                                       c = System.in.read();
+                               } catch(IOException e) {
+                                       System.out.println(
+                                       "&lt;br&gt;&lt;b&gt;SYSTEM EXCEPTION");
+                                       Runtime rt = Runtime.getRuntime();
+                                       rt.exit(status);
+                                       }
+                               if (c != -1) {  
+                                       System.out.print((char)c);
+                                       }
+                               }
+                       System.out.println(
+                               "&lt;H3&gt;Environment Variables:&lt;/H3&gt;");
+       
+                       System.getProperties().list(System.out);
+                       System.out.println("&lt;/body&gt;");
+                       System.out.println("&lt;/html&gt;");
+                       }
+               }
+       }
+</pre>
+<h4>B. Echo2FCGI</h4>
+<pre>
+import FCGIInterface;
+import FCGIGlobalDefs;
+import FCGIInputStream;
+import FCGIOutputStream;
+import FCGIMessage;
+import FCGIRequest;
+import java.io.*;
+
+class Echo2FCGI {
+
+       public static void main (String args[]) {
+               int status = 0;
+                FCGIInterface intf = new FCGIInterface();
+               while(intf.FCGIaccept()>= 0) {
+               System.out.println("Content-type: text/html\n\n");
+                       System.out.println("&lt;html&gt;");
+                       System.out.println(
+                               "&lt;head&gt;&lt;TITLE&gt;FastCGI echo
+                                    &lt;/TITLE&gt;&lt;/head&gt;");
+                       System.out.println("&lt;body&gt;");                     
+                       System.out.println("&lt;H2&gt;FastCGI echo&lt;/H2&gt;");
+                       System.out.println("&lt;H3&gt;STDIN:&lt;/H3"&gt;);
+                       for ( int c = 0; c != -1; ) {
+                               try {
+                                       c = intf.request.inStream.read();
+                               } catch(IOException e) {
+                                       System.out.println(
+                                       "&lt;br&gt;&lt;b&gt;SYSTEM EXCEPTION");
+                                       Runtime rt = Runtime.getRuntime();
+                                       rt.exit(status);
+                                       }
+                               if (c != -1) {  
+                                       System.out.print((char)c);
+                                       }
+                               }
+                       System.out.println(
+                               "&lt;H3&gt;Environment Variables:&lt;/H3&gt;");
+       
+                       System.getProperties().list(System.out);
+                       System.out.println(&lt;"/body&gt;");
+                       System.out.println("&lt;/html&gt;");
+                       }
+               }
+       }
+</pre>
+<h4>C. Running these Examples</h4>
+
+<h5>Configuring</h5>
+
+As with TinyFCGI, you need to configure the web server to recognize these
+two FastCGI applications. Your configuration now looks like this:<p> 
+<pre>
+ExternalAppClass java1 -host hostname:portNum
+Responder java1 fcgi-devel-kit/examples/TinyFCGI
+ExternalAppClass java2 -host hostname:portNumA
+Responder java2 fcgi-devel-kit/examples/EchoFCGI
+ExternalAppClass java3 -host hostname:porNumB
+Responder java3 fcgi-devel-kit/examples/Echo2FCGI
+</pre>
+<p>
+Note that the application classes and port numbers are different for each
+application.
+
+<h5>Running</h5>
+
+As with TinyFCGI, you need to run these programs with the -D option
+using FCGI_PORT and the appropriate port number.
+
+To get some data for standard input we have created two html pages with
+forms that use a POST method. These are echo.html and echo2.html. You must
+edit these .html files to expand the path to <i>fcgi-devel-kit/examples</i>
+to a full path. Once the appropriate Java program is running, point your browser at the corresponding HTML page, enter some data and select the <i>go_find</i> button.
+
+
+<H3><A NAME = "S6"> 6. FastCGI Java Classes</A></H3>
+
+The Java FastCGI classes are included in both source and byte code format in
+<i>fcgi-devel-kit/java/src</i> and :<i>fcgi-devel-kit/java/classes</i>
+respectively. The following is a brief description of these classes:<p>
+
+<dl>
+<dt><i>FCGIInterface</i><dd> This class contains the FCGIaccept method called
+     by the FastCGI user application. This method sets up the appropriate
+     FastCGI environment for communication with the web server and manages
+     FastCGI requests.<br>
+     
+ <dt><i>FCGIInputStream</i><dd> This input stream manages FastCGI
+      internal buffers to ensure that the user gets all of the FastCGI
+      messages associated with a request. It uses FCGIMessage objects
+      to interpret these incoming messages.<br>
+      
+  <dt><i>FCGIOutputStream</i><dd> This output stream manages FastCGI
+       internal buffers to send user data back to the web server
+       and to notify the server of various FCGI protocol conditions.
+       It uses FCGIMessage objects to format outgoing FastCGI messages.<br>
+      
+<dt><i>FCGIMessage</i><dd> This is the only class that understands the
+     actual structure of the FastCGI messages. It interprets incoming
+     FastCGI records and constructs outgoing ones..<br>
+     
+<dt><i>FCGIRequest</i><dd>This class currently contains data fields
+     used by FastCGI to manage user requests.  In a multi-threaded
+     version of FastCGI, the role of this class will be expanded.<br>
+
+<dt><i>FCGIGlobalDefs</i><dd>This class contains definitions of FastCGI
+     constants.
+</dl>
+<HR>
+<ADDRESS><A HREF="mailto:harris@openmarket.com">Steve Harris // harris@openmarket.com</A></ADDRESS>
+</body>
+</html>
diff --git a/doc/fcgi-perf.gut b/doc/fcgi-perf.gut
new file mode 100644 (file)
index 0000000..d7d6219
--- /dev/null
@@ -0,0 +1,431 @@
+Understanding FastCGI Application Performance
+/fastcgi/words
+fcgi-hd.gif
+[FastCGI]
+<center>Understanding FastCGI Application Performance</center>
+
+<!--Copyright (c) 1996 Open Market, Inc.                                    -->
+<!--See the file "LICENSE.TERMS" for information on usage and redistribution-->
+<!--of this file, and for a DISCLAIMER OF ALL WARRANTIES.                   -->
+
+<center>
+Mark R. Brown<br>
+Open Market, Inc.<br>
+<p>
+
+10 June 1996<br>
+</center>
+<p>
+
+<h5 align=center>
+Copyright &copy; 1996 Open Market, Inc.  245 First Street, Cambridge,
+  MA 02142 U.S.A.<br>
+Tel: 617-621-9500 Fax: 617-621-1703 URL:
+  <a href="http://www.openmarket.com/">http://www.openmarket.com/</a><br>
+$Id: fcgi-perf.gut,v 1.1 1997/09/16 15:36:26 stanleyg Exp $ <br>
+</h5>
+<hr>
+
+<ul type=square>
+    <li><a HREF = "#S1">1. Introduction</a>
+    <li><a HREF = "#S2">2. Performance Basics</a>
+    <li><a HREF = "#S3">3. Caching</a>
+    <li><a HREF = "#S4">4. Database Access</a>
+    <li><a HREF = "#S5">5. A Performance Test</a>
+    <ul type=square>
+        <li><a HREF = "#S5.1">5.1 Application Scenario</a>
+        <li><a HREF = "#S5.2">5.2 Application Design</a>
+        <li><a HREF = "#S5.3">5.3 Test Conditions</a>
+        <li><a HREF = "#S5.4">5.4 Test Results and Discussion</a>
+    </ul>
+    <li><a HREF = "#S6">6. Multi-threaded APIs</a>
+    <li><a HREF = "#S7">7. Conclusion</a>
+</ul>
+<p>
+
+<hr>
+
+
+<h3><a name = "S1">1. Introduction</a></h3>
+
+
+Just how fast is FastCGI?  How does the performance of a FastCGI
+application compare with the performance of the same
+application implemented using a Web server API?<p>
+
+Of course, the answer is that it depends upon the application.
+A more complete answer is that FastCGI often wins by a significant
+margin, and seldom loses by very much.<p>
+
+Papers on computer system performance can be laden with complex graphs
+showing how this varies with that.  Seldom do the graphs shed much
+light on <i>why</i> one system is faster than another.  Advertising copy is
+often even less informative.  An ad from one large Web server vendor
+says that its server "executes web applications up to five times
+faster than all other servers," but the ad gives little clue where the
+number "five" came from.<p>
+
+This paper is meant to convey an understanding of the primary factors
+that influence the performance of Web server applications and to show
+that architectural differences between FastCGI and server APIs often
+give an "unfair" performance advantage to FastCGI applications.  We
+run a test that shows a FastCGI application running three times faster
+than the corresponding Web server API application.  Under different
+conditions this factor might be larger or smaller.  We show you what
+you'd need to measure to figure that out for the situation you face,
+rather than just saying "we're three times faster" and moving on.<p>
+
+This paper makes no attempt to prove that FastCGI is better than Web
+server APIs for every application.  Web server APIs enable lightweight
+protocol extensions, such as Open Market's SecureLink extension, to be
+added to Web servers, as well as allowing other forms of server
+customization.  But APIs are not well matched to mainstream applications
+such as personalized content or access to corporate databases, because
+of API drawbacks including high complexity, low security, and
+limited scalability.  FastCGI shines when used for the vast
+majority of Web applications.<p>
+
+
+
+<h3><a name = "S2">2. Performance Basics</a></h3>
+
+
+Since this paper is about performance we need to be clear on
+what "performance" is.<p>
+
+The standard way to measure performance in a request-response system
+like the Web is to measure peak request throughput subject to a
+response time constriaint.  For instance, a Web server application
+might be capable of performing 20 requests per second while responding
+to 90% of the requests in less than 2 seconds.<p>
+
+Response time is a thorny thing to measure on the Web because client
+communications links to the Internet have widely varying bandwidth.
+If the client is slow to read the server's response, response time at
+both the client and the server will go up, and there's nothing the
+server can do about it.  For the purposes of making repeatable
+measurements the client should have a high-bandwidth communications
+link to the server.<p>
+
+[Footnote: When designing a Web server application that will be
+accessed over slow (e.g. 14.4 or even 28.8 kilobit/second modem)
+channels, pay attention to the simultaneous connections bottleneck.
+Some servers are limited by design to only 100 or 200 simultaneous
+connections.  If your application sends 50 kilobytes of data to a
+typical client that can read 2 kilobytes per second, then a request
+takes 25 seconds to complete.  If your server is limited to 100
+simultaneous connections, throughput is limited to just 4 requests per
+second.]<p>
+
+Response time is seldom an issue when load is light, but response
+times rise quickly as the system approaches a bottleneck on some
+limited resource.  The three resources that typical systems run out of
+are network I/O, disk I/O, and processor time.  If short response time
+is a goal, it is a good idea to stay at or below 50% load on each of
+these resources.  For instance, if your disk subsystem is capable of
+delivering 200 I/Os per second, then try to run your application at
+100 I/Os per second to avoid having the disk subsystem contribute to
+slow response times.  Through careful management it is possible to
+succeed in running closer to the edge, but careful management is both
+difficult and expensive so few systems get it.<p>
+
+If a Web server application is local to the Web server machine, then
+its internal design has no impact on network I/O.  Application design
+can have a big impact on usage of disk I/O and processor time.<p>
+
+
+
+<h3><a name = "S3">3. Caching</a></h3>
+
+
+It is a rare Web server application that doesn't run fast when all the
+information it needs is available in its memory.  And if the
+application doesn't run fast under those conditions, the possible
+solutions are evident: Tune the processor-hungry parts of the
+application, install a faster processor, or change the application's
+functional specification so it doesn't need to do so much work.<p>
+
+The way to make information available in memory is by caching.  A
+cache is an in-memory data structure that contains information that's
+been read from its permanent home on disk.  When the application needs
+information, it consults the cache, and uses the information if it is
+there.  Otherwise is reads the information from disk and places a copy
+in the cache.  If the cache is full, the application discards some old
+information before adding the new.  When the application needs to
+change cached information, it changes both the cache entry and the
+information on disk.  That way, if the application crashes, no
+information is lost; the application just runs more slowly for awhile
+after restarting, because the cache doesn't improve performance
+when it is empty.<p>
+
+Caching can reduce both disk I/O and processor time, because reading
+information from disk uses more processor time than reading it from
+the cache.  Because caching addresses both of the potential
+bottlenecks, it is the focal point of high-performance Web server
+application design.  CGI applications couldn't perform in-memory
+caching, because they exited after processing just one request.  Web
+server APIs promised to solve this problem.  But how effective is the
+solution?<p>
+
+Today's most widely deployed Web server APIs are based on a
+pool-of-processes server model.  The Web server consists of a parent
+process and a pool of child processes.  Processes do not share memory.
+An incoming request is assigned to an idle child at random.  The child
+runs the request to completion before accepting a new request.  A
+typical server has 32 child processes, a large server has 100 or 200.<p>
+
+In-memory caching works very poorly in this server model because
+processes do not share memory and incoming requests are assigned to
+processes at random.  For instance, to keep a frequently-used file
+available in memory the server must keep a file copy per child, which
+wastes memory.  When the file is modified all the children need to be
+notified, which is complex (the APIs don't provide a way to do it).<p>
+
+FastCGI is designed to allow effective in-memory caching.  Requests
+are routed from any child process to a FastCGI application server.
+The FastCGI application process maintains an in-memory cache.<p>
+
+In some cases a single FastCGI application server won't
+provide enough performance.  FastCGI provides two solutions:
+session affinity and multi-threading.<p>
+
+With session affinity you run a pool of application processes and the
+Web server routes requests to individual processes based on any
+information contained in the request.  For instance, the server can
+route according to the area of content that's been requested, or
+according to the user.  The user might be identified by an
+application-specific session identifier, by the user ID contained in
+an Open Market Secure Link ticket, by the Basic Authentication user
+name, or whatever.  Each process maintains its own cache, and session
+affinity ensures that each incoming request has access to the cache
+that will speed up processing the most.<p>
+
+With multi-threading you run an application process that is designed
+to handle several requests at the same time.  The threads handling
+concurrent requests share process memory, so they all have access to
+the same cache.  Multi-threaded programming is complex -- concurrency
+makes programs difficult to test and debug -- but with FastCGI you can
+write single threaded <i>or</i> multithreaded applications.<p>
+
+
+
+<h3><a name = "S4">4. Database Access</a></h3>
+
+
+Many Web server applications perform database access.  Existing
+databases contain a lot of valuable information; Web server
+applications allow companies to give wider access to the information.<p>
+
+Access to database management systems, even within a single machine,
+is via connection-oriented protocols.  An application "logs in" to a
+database, creating a connection, then performs one or more accesses.
+Frequently, the cost of creating the database connection is several
+times the cost of accessing data over an established connection.<p>
+
+To a first approximation database connections are just another type of
+state to be cached in memory by an application, so the discussion of
+caching above applies to caching database connections.<p>
+
+But database connections are special in one respect: They are often
+the basis for database licensing.  You pay the database vendor
+according to the number of concurrent connections the database system
+can sustain.  A 100-connection license costs much more than a
+5-connection license.  It follows that caching a database connection
+per Web server child process is not just wasteful of system's hardware
+resources, it could break your software budget.<p>
+
+
+
+<h3><a name = "S5">5. A Performance Test</a></h3>
+
+
+We designed a test application to illustrate performance issues.  The
+application represents a class of applications that deliver
+personalized content.  The test application is quite a bit simpler
+than any real application would be, but still illustrates the main
+performance issues.  We implemented the application using both FastCGI
+and a current Web server API, and measured the performance of each.<p>
+
+<h4><a name = "S5.1">5.1 Application Scenario</a></h4>
+
+The application is based on a user database and a set of
+content files.  When a user requests a content file, the application
+performs substitutions in the file using information from the
+user database.  The application then returns the modified
+content to the user.<p>
+
+Each request accomplishes the following:<p>
+
+<ol>
+    <li>authentication check: The user id is used to retrieve and
+        check the password.<p>
+
+    <li>attribute retrieval: The user id is used to retrieve all
+        of the user's attribute values.<p>
+
+    <li>file retrieval and filtering: The request identifies a
+        content file. This file is read and all occurrences of variable
+        names are replaced with the user's corresponding attribute values.
+        The modified HTML is returned to the user.<p>
+</ol>
+
+Of course, it is fair game to perform caching to shortcut
+any of these steps.<p>
+
+Each user's database record (including password and attribute
+values) is approximately 100 bytes long.  Each content file is 3,000
+bytes long.  Both database and content files are stored
+on disks attached to the server platform.<p>
+
+A typical user makes 10 file accesses with realistic think times
+(30-60 seconds) between accesses, then disappears for a long time.<p>
+
+
+<h4><a name = "S5.2">5.2 Application Design</a></h4>
+
+The FastCGI application maintains a cache of recently-accessed
+attribute values from the database.  When the cache misses
+the application reads from the database.  Because only a small
+number of FastCGI application processes are needed, each process
+opens a database connection on startup and keeps it open.<p>
+
+The FastCGI application is configured as multiple application
+processes.  This is desirable in order to get concurrent application
+processing during database reads and file reads.  Requests are routed
+to these application processes using FastCGI session affinity keyed on
+the user id.  This way all a user's requests after the first hit in
+the application's cache.<p>
+
+The API application does not maintain a cache; the API application has
+no way to share the cache among its processes, so the cache hit rate
+would be too low to make caching pay.  The API application opens and
+closes a database connection on every request; keeping database
+connections open between requests would result in an unrealistically
+large number of database connections open at the same time, and very
+low utilization of each connection.<p>
+
+
+<h4><a name = "S5.3">5.3 Test Conditions</a></h4>
+
+The test load is generated by 10 HTTP client processes.  The processes
+represent disjoint sets of users.  A process makes a request for a
+user, then a request for a different user, and so on until it is time
+for the first user to make another request.<p>
+
+For simplicity the 10 client processes run on the same machine
+as the Web server.  This avoids the possibility that a network
+bottleneck will obscure the test results.  The database system
+also runs on this machine, as specified in the application scenario.<p>
+
+Response time is not an issue under the test conditions.  We just
+measure throughput.<p>
+
+The API Web server is in these tests is Netscape 1.1.<p>
+
+
+<h4><a name = "S5.4">5.4 Test Results and Discussion</a></h4>
+
+Here are the test results:<p>
+
+<ul>
+<pre>
+    FastCGI  12.0 msec per request = 83 requests per second
+    API      36.6 msec per request = 27 requests per second
+</pre>
+</ul>
+
+Given the big architectural advantage that the FastCGI application
+enjoys over the API application, it is not surprising that the
+FastCGI application runs a lot faster.  To gain a deeper
+understanding of these results we measured two more conditions:<p>
+
+<ul>
+    <li>API with sustained database connections.  If you could
+        afford the extra licensing cost, how much faster would
+        your API application run?<p>
+
+<pre>
+    API      16.0 msec per request = 61 requests per second
+</pre>
+
+        Answer: Still not as fast as the FastCGI application.<p>
+
+    <li>FastCGI with cache disabled.  How much benefit does the
+        FastCGI application get from its cache?<p>
+
+<pre>
+    FastCGI  20.1 msec per request = 50 requests per second
+</pre>
+
+        Answer: A very substantial benefit, even though the database
+        access is quite simple.<p>
+</ul>
+
+What these two extra experiments show is that if the API and FastCGI
+applications are implemented in exactly the same way -- caching
+database connections but not caching user profile data -- the API
+application is slightly faster.  This is what you'd expect, since the
+FastCGI application has to pay the cost of inter-process
+communication not present in the API application.<p>
+
+In the real world the two applications would not be implemented in the
+same way.  FastCGI's architectural advantage results in much higher
+performance -- a factor of 3 in this test.  With a remote database
+or more expensive database access the factor would be higher.
+With more substantial processing of the content files the factor
+would be smaller.<p>
+
+
+
+<h3><a name = "S6">6. Multi-threaded APIs</a></h3>
+
+
+Web servers with a multi-threaded internal structure (and APIs to
+match) are now starting to become more common.  These servers don't
+have all of the disadvantages described in Section 3.  Does this mean
+that FastCGI's performance advantages will disappear?<p>
+
+A superficial analysis says yes.  An API-based application in a
+single-process, multi-threaded server can maintain caches and database
+connections the same way a FastCGI application can.  The API-based
+application does not pay for inter-process communication, so the
+API-based application will be slightly faster than the FastCGI
+application.<p>
+
+A deeper analysis says no.  Multi-threaded programming is complex,
+because concurrency makes programs much more difficult to test and
+debug.  In the case of multi-threaded programming to Web server APIs,
+the normal problems with multi-threading are compounded by the lack of
+isolation between different applications and between the applications
+and the Web server.  With FastCGI you can write programs in the
+familiar single-threaded style, get all the reliability and
+maintainability of process isolation, and still get very high
+performance.  If you truly need multi-threading, you can write
+multi-threaded FastCGI and still isolate your multi-threaded
+application from other applications and from the server.  In short,
+multi-threading makes Web server APIs unusable for practially all
+applications, reducing the choice to FastCGI versus CGI.  The
+performance winner in that contest is obviously FastCGI.<p>
+
+
+
+<h3><a name = "S7">7. Conclusion</a></h3>
+
+
+Just how fast is FastCGI?  The answer: very fast indeed.  Not because
+it has some specially-greased path through the operating system, but
+because its design is well matched to the needs of most applications.
+We invite you to make FastCGI the fast, open foundation for your Web
+server applications.<p>
+
+
+
+<hr>
+<a href="http://www.openmarket.com/"><IMG SRC="omi-logo.gif" ALT="OMI Home Page"></a>
+
+<address>
+&#169 1995, Open Market, Inc. / mbrown@openmarket.com
+</address>
+
+</body>
+</html>
diff --git a/doc/fcgi-perf.htm b/doc/fcgi-perf.htm
new file mode 100644 (file)
index 0000000..ec916e9
--- /dev/null
@@ -0,0 +1,441 @@
+<html>
+<head><title>Understanding FastCGI Application Performance</title>
+</head>
+
+<body bgcolor="#FFFFFF" text="#000000" link="#cc0000" alink="#000011" 
+vlink="#555555">
+
+<center>
+<a href="/fastcgi/words">
+    <img border=0 src="../images/fcgi-hd.gif" alt="[[FastCGI]]"></a>
+</center>
+<br clear=all>
+<h3><center>Understanding FastCGI Application Performance</center></h3>
+
+<!--Copyright (c) 1996 Open Market, Inc.                                    -->
+<!--See the file "LICENSE.TERMS" for information on usage and redistribution-->
+<!--of this file, and for a DISCLAIMER OF ALL WARRANTIES.                   -->
+
+<center>
+Mark R. Brown<br>
+Open Market, Inc.<br>
+<p>
+
+10 June 1996<br>
+</center>
+<p>
+
+<h5 align=center>
+Copyright &copy; 1996 Open Market, Inc.  245 First Street, Cambridge,
+  MA 02142 U.S.A.<br>
+Tel: 617-621-9500 Fax: 617-621-1703 URL:
+  <a href="http://www.openmarket.com/">http://www.openmarket.com/</a><br>
+$Id: fcgi-perf.htm,v 1.1 1997/09/16 15:36:26 stanleyg Exp $ <br>
+</h5>
+<hr>
+
+<ul type=square>
+    <li><a HREF = "#S1">1. Introduction</a>
+    <li><a HREF = "#S2">2. Performance Basics</a>
+    <li><a HREF = "#S3">3. Caching</a>
+    <li><a HREF = "#S4">4. Database Access</a>
+    <li><a HREF = "#S5">5. A Performance Test</a>
+    <ul type=square>
+        <li><a HREF = "#S5.1">5.1 Application Scenario</a>
+        <li><a HREF = "#S5.2">5.2 Application Design</a>
+        <li><a HREF = "#S5.3">5.3 Test Conditions</a>
+        <li><a HREF = "#S5.4">5.4 Test Results and Discussion</a>
+    </ul>
+    <li><a HREF = "#S6">6. Multi-threaded APIs</a>
+    <li><a HREF = "#S7">7. Conclusion</a>
+</ul>
+<p>
+
+<hr>
+
+
+<h3><a name = "S1">1. Introduction</a></h3>
+
+
+Just how fast is FastCGI?  How does the performance of a FastCGI
+application compare with the performance of the same
+application implemented using a Web server API?<p>
+
+Of course, the answer is that it depends upon the application.
+A more complete answer is that FastCGI often wins by a significant
+margin, and seldom loses by very much.<p>
+
+Papers on computer system performance can be laden with complex graphs
+showing how this varies with that.  Seldom do the graphs shed much
+light on <i>why</i> one system is faster than another.  Advertising copy is
+often even less informative.  An ad from one large Web server vendor
+says that its server "executes web applications up to five times
+faster than all other servers," but the ad gives little clue where the
+number "five" came from.<p>
+
+This paper is meant to convey an understanding of the primary factors
+that influence the performance of Web server applications and to show
+that architectural differences between FastCGI and server APIs often
+give an "unfair" performance advantage to FastCGI applications.  We
+run a test that shows a FastCGI application running three times faster
+than the corresponding Web server API application.  Under different
+conditions this factor might be larger or smaller.  We show you what
+you'd need to measure to figure that out for the situation you face,
+rather than just saying "we're three times faster" and moving on.<p>
+
+This paper makes no attempt to prove that FastCGI is better than Web
+server APIs for every application.  Web server APIs enable lightweight
+protocol extensions, such as Open Market's SecureLink extension, to be
+added to Web servers, as well as allowing other forms of server
+customization.  But APIs are not well matched to mainstream applications
+such as personalized content or access to corporate databases, because
+of API drawbacks including high complexity, low security, and
+limited scalability.  FastCGI shines when used for the vast
+majority of Web applications.<p>
+
+
+
+<h3><a name = "S2">2. Performance Basics</a></h3>
+
+
+Since this paper is about performance we need to be clear on
+what "performance" is.<p>
+
+The standard way to measure performance in a request-response system
+like the Web is to measure peak request throughput subject to a
+response time constriaint.  For instance, a Web server application
+might be capable of performing 20 requests per second while responding
+to 90% of the requests in less than 2 seconds.<p>
+
+Response time is a thorny thing to measure on the Web because client
+communications links to the Internet have widely varying bandwidth.
+If the client is slow to read the server's response, response time at
+both the client and the server will go up, and there's nothing the
+server can do about it.  For the purposes of making repeatable
+measurements the client should have a high-bandwidth communications
+link to the server.<p>
+
+[Footnote: When designing a Web server application that will be
+accessed over slow (e.g. 14.4 or even 28.8 kilobit/second modem)
+channels, pay attention to the simultaneous connections bottleneck.
+Some servers are limited by design to only 100 or 200 simultaneous
+connections.  If your application sends 50 kilobytes of data to a
+typical client that can read 2 kilobytes per second, then a request
+takes 25 seconds to complete.  If your server is limited to 100
+simultaneous connections, throughput is limited to just 4 requests per
+second.]<p>
+
+Response time is seldom an issue when load is light, but response
+times rise quickly as the system approaches a bottleneck on some
+limited resource.  The three resources that typical systems run out of
+are network I/O, disk I/O, and processor time.  If short response time
+is a goal, it is a good idea to stay at or below 50% load on each of
+these resources.  For instance, if your disk subsystem is capable of
+delivering 200 I/Os per second, then try to run your application at
+100 I/Os per second to avoid having the disk subsystem contribute to
+slow response times.  Through careful management it is possible to
+succeed in running closer to the edge, but careful management is both
+difficult and expensive so few systems get it.<p>
+
+If a Web server application is local to the Web server machine, then
+its internal design has no impact on network I/O.  Application design
+can have a big impact on usage of disk I/O and processor time.<p>
+
+
+
+<h3><a name = "S3">3. Caching</a></h3>
+
+
+It is a rare Web server application that doesn't run fast when all the
+information it needs is available in its memory.  And if the
+application doesn't run fast under those conditions, the possible
+solutions are evident: Tune the processor-hungry parts of the
+application, install a faster processor, or change the application's
+functional specification so it doesn't need to do so much work.<p>
+
+The way to make information available in memory is by caching.  A
+cache is an in-memory data structure that contains information that's
+been read from its permanent home on disk.  When the application needs
+information, it consults the cache, and uses the information if it is
+there.  Otherwise is reads the information from disk and places a copy
+in the cache.  If the cache is full, the application discards some old
+information before adding the new.  When the application needs to
+change cached information, it changes both the cache entry and the
+information on disk.  That way, if the application crashes, no
+information is lost; the application just runs more slowly for awhile
+after restarting, because the cache doesn't improve performance
+when it is empty.<p>
+
+Caching can reduce both disk I/O and processor time, because reading
+information from disk uses more processor time than reading it from
+the cache.  Because caching addresses both of the potential
+bottlenecks, it is the focal point of high-performance Web server
+application design.  CGI applications couldn't perform in-memory
+caching, because they exited after processing just one request.  Web
+server APIs promised to solve this problem.  But how effective is the
+solution?<p>
+
+Today's most widely deployed Web server APIs are based on a
+pool-of-processes server model.  The Web server consists of a parent
+process and a pool of child processes.  Processes do not share memory.
+An incoming request is assigned to an idle child at random.  The child
+runs the request to completion before accepting a new request.  A
+typical server has 32 child processes, a large server has 100 or 200.<p>
+
+In-memory caching works very poorly in this server model because
+processes do not share memory and incoming requests are assigned to
+processes at random.  For instance, to keep a frequently-used file
+available in memory the server must keep a file copy per child, which
+wastes memory.  When the file is modified all the children need to be
+notified, which is complex (the APIs don't provide a way to do it).<p>
+
+FastCGI is designed to allow effective in-memory caching.  Requests
+are routed from any child process to a FastCGI application server.
+The FastCGI application process maintains an in-memory cache.<p>
+
+In some cases a single FastCGI application server won't
+provide enough performance.  FastCGI provides two solutions:
+session affinity and multi-threading.<p>
+
+With session affinity you run a pool of application processes and the
+Web server routes requests to individual processes based on any
+information contained in the request.  For instance, the server can
+route according to the area of content that's been requested, or
+according to the user.  The user might be identified by an
+application-specific session identifier, by the user ID contained in
+an Open Market Secure Link ticket, by the Basic Authentication user
+name, or whatever.  Each process maintains its own cache, and session
+affinity ensures that each incoming request has access to the cache
+that will speed up processing the most.<p>
+
+With multi-threading you run an application process that is designed
+to handle several requests at the same time.  The threads handling
+concurrent requests share process memory, so they all have access to
+the same cache.  Multi-threaded programming is complex -- concurrency
+makes programs difficult to test and debug -- but with FastCGI you can
+write single threaded <i>or</i> multithreaded applications.<p>
+
+
+
+<h3><a name = "S4">4. Database Access</a></h3>
+
+
+Many Web server applications perform database access.  Existing
+databases contain a lot of valuable information; Web server
+applications allow companies to give wider access to the information.<p>
+
+Access to database management systems, even within a single machine,
+is via connection-oriented protocols.  An application "logs in" to a
+database, creating a connection, then performs one or more accesses.
+Frequently, the cost of creating the database connection is several
+times the cost of accessing data over an established connection.<p>
+
+To a first approximation database connections are just another type of
+state to be cached in memory by an application, so the discussion of
+caching above applies to caching database connections.<p>
+
+But database connections are special in one respect: They are often
+the basis for database licensing.  You pay the database vendor
+according to the number of concurrent connections the database system
+can sustain.  A 100-connection license costs much more than a
+5-connection license.  It follows that caching a database connection
+per Web server child process is not just wasteful of system's hardware
+resources, it could break your software budget.<p>
+
+
+
+<h3><a name = "S5">5. A Performance Test</a></h3>
+
+
+We designed a test application to illustrate performance issues.  The
+application represents a class of applications that deliver
+personalized content.  The test application is quite a bit simpler
+than any real application would be, but still illustrates the main
+performance issues.  We implemented the application using both FastCGI
+and a current Web server API, and measured the performance of each.<p>
+
+<h4><a name = "S5.1">5.1 Application Scenario</a></h4>
+
+The application is based on a user database and a set of
+content files.  When a user requests a content file, the application
+performs substitutions in the file using information from the
+user database.  The application then returns the modified
+content to the user.<p>
+
+Each request accomplishes the following:<p>
+
+<ol>
+    <li>authentication check: The user id is used to retrieve and
+        check the password.<p>
+
+    <li>attribute retrieval: The user id is used to retrieve all
+        of the user's attribute values.<p>
+
+    <li>file retrieval and filtering: The request identifies a
+        content file. This file is read and all occurrences of variable
+        names are replaced with the user's corresponding attribute values.
+        The modified HTML is returned to the user.<p>
+</ol>
+
+Of course, it is fair game to perform caching to shortcut
+any of these steps.<p>
+
+Each user's database record (including password and attribute
+values) is approximately 100 bytes long.  Each content file is 3,000
+bytes long.  Both database and content files are stored
+on disks attached to the server platform.<p>
+
+A typical user makes 10 file accesses with realistic think times
+(30-60 seconds) between accesses, then disappears for a long time.<p>
+
+
+<h4><a name = "S5.2">5.2 Application Design</a></h4>
+
+The FastCGI application maintains a cache of recently-accessed
+attribute values from the database.  When the cache misses
+the application reads from the database.  Because only a small
+number of FastCGI application processes are needed, each process
+opens a database connection on startup and keeps it open.<p>
+
+The FastCGI application is configured as multiple application
+processes.  This is desirable in order to get concurrent application
+processing during database reads and file reads.  Requests are routed
+to these application processes using FastCGI session affinity keyed on
+the user id.  This way all a user's requests after the first hit in
+the application's cache.<p>
+
+The API application does not maintain a cache; the API application has
+no way to share the cache among its processes, so the cache hit rate
+would be too low to make caching pay.  The API application opens and
+closes a database connection on every request; keeping database
+connections open between requests would result in an unrealistically
+large number of database connections open at the same time, and very
+low utilization of each connection.<p>
+
+
+<h4><a name = "S5.3">5.3 Test Conditions</a></h4>
+
+The test load is generated by 10 HTTP client processes.  The processes
+represent disjoint sets of users.  A process makes a request for a
+user, then a request for a different user, and so on until it is time
+for the first user to make another request.<p>
+
+For simplicity the 10 client processes run on the same machine
+as the Web server.  This avoids the possibility that a network
+bottleneck will obscure the test results.  The database system
+also runs on this machine, as specified in the application scenario.<p>
+
+Response time is not an issue under the test conditions.  We just
+measure throughput.<p>
+
+The API Web server is in these tests is Netscape 1.1.<p>
+
+
+<h4><a name = "S5.4">5.4 Test Results and Discussion</a></h4>
+
+Here are the test results:<p>
+
+<ul>
+<pre>
+    FastCGI  12.0 msec per request = 83 requests per second
+    API      36.6 msec per request = 27 requests per second
+</pre>
+</ul>
+
+Given the big architectural advantage that the FastCGI application
+enjoys over the API application, it is not surprising that the
+FastCGI application runs a lot faster.  To gain a deeper
+understanding of these results we measured two more conditions:<p>
+
+<ul>
+    <li>API with sustained database connections.  If you could
+        afford the extra licensing cost, how much faster would
+        your API application run?<p>
+
+<pre>
+    API      16.0 msec per request = 61 requests per second
+</pre>
+
+        Answer: Still not as fast as the FastCGI application.<p>
+
+    <li>FastCGI with cache disabled.  How much benefit does the
+        FastCGI application get from its cache?<p>
+
+<pre>
+    FastCGI  20.1 msec per request = 50 requests per second
+</pre>
+
+        Answer: A very substantial benefit, even though the database
+        access is quite simple.<p>
+</ul>
+
+What these two extra experiments show is that if the API and FastCGI
+applications are implemented in exactly the same way -- caching
+database connections but not caching user profile data -- the API
+application is slightly faster.  This is what you'd expect, since the
+FastCGI application has to pay the cost of inter-process
+communication not present in the API application.<p>
+
+In the real world the two applications would not be implemented in the
+same way.  FastCGI's architectural advantage results in much higher
+performance -- a factor of 3 in this test.  With a remote database
+or more expensive database access the factor would be higher.
+With more substantial processing of the content files the factor
+would be smaller.<p>
+
+
+
+<h3><a name = "S6">6. Multi-threaded APIs</a></h3>
+
+
+Web servers with a multi-threaded internal structure (and APIs to
+match) are now starting to become more common.  These servers don't
+have all of the disadvantages described in Section 3.  Does this mean
+that FastCGI's performance advantages will disappear?<p>
+
+A superficial analysis says yes.  An API-based application in a
+single-process, multi-threaded server can maintain caches and database
+connections the same way a FastCGI application can.  The API-based
+application does not pay for inter-process communication, so the
+API-based application will be slightly faster than the FastCGI
+application.<p>
+
+A deeper analysis says no.  Multi-threaded programming is complex,
+because concurrency makes programs much more difficult to test and
+debug.  In the case of multi-threaded programming to Web server APIs,
+the normal problems with multi-threading are compounded by the lack of
+isolation between different applications and between the applications
+and the Web server.  With FastCGI you can write programs in the
+familiar single-threaded style, get all the reliability and
+maintainability of process isolation, and still get very high
+performance.  If you truly need multi-threading, you can write
+multi-threaded FastCGI and still isolate your multi-threaded
+application from other applications and from the server.  In short,
+multi-threading makes Web server APIs unusable for practially all
+applications, reducing the choice to FastCGI versus CGI.  The
+performance winner in that contest is obviously FastCGI.<p>
+
+
+
+<h3><a name = "S7">7. Conclusion</a></h3>
+
+
+Just how fast is FastCGI?  The answer: very fast indeed.  Not because
+it has some specially-greased path through the operating system, but
+because its design is well matched to the needs of most applications.
+We invite you to make FastCGI the fast, open foundation for your Web
+server applications.<p>
+
+
+
+<hr>
+<a href="http://www.openmarket.com/"><IMG SRC="omi-logo.gif" ALT="OMI Home Page"></a>
+
+<address>
+&#169 1995, Open Market, Inc. / mbrown@openmarket.com
+</address>
+
+</body>
+</html>
+</body>
+</html>
diff --git a/doc/fcgi-perl.gut b/doc/fcgi-perl.gut
new file mode 100644 (file)
index 0000000..21caf6b
--- /dev/null
@@ -0,0 +1,546 @@
+Integrating FastCGI with Perl-5
+/fastcgi/words
+fcgi-hd.gif
+[FastCGI]
+<center>Integrating FastCGI with Perl-5</center>
+
+<!--Copyright (c) 1996 Open Market, Inc.                                    -->
+<!--See the file "LICENSE.TERMS" for information on usage and redistribution-->
+<!--of this file, and for a DISCLAIMER OF ALL WARRANTIES.                   -->
+
+<h5 align=center>
+Copyright &copy; 1996 Open Market, Inc.  245 First Street, Cambridge,
+  MA 02142 U.S.A.<br>
+Tel: 617-949-7000 URL:
+  <a href="http://www.openmarket.com/">http://www.openmarket.com/</a><br>
+$Id: fcgi-perl.gut,v 1.1 1997/09/16 15:36:26 stanleyg Exp $ <br>
+</h5>
+<hr>
+
+<ul type=square>
+  <li><a HREF = "#S1">1. Introduction</a>
+  <li><a HREF = "#S2">2. Perl with sfio and an FCGI module</a>
+  <li><a HREF = "#S3">3. Perl with fcgi_stdio and an FCGI module</a>
+  <ul type=square>
+    <li><a HREF = "#S3.1">3.1 Basic recipe</a>
+    <li><a HREF = "#S3.2">3.2 Semi-advanced recipe</a>
+    <li><a HREF = "#S3.3">3.3 Advanced recipe</a>
+  </ul>
+  <li><a HREF = "#S4">4. Writing FastCGI applications in Perl</a>
+</ul>
+
+
+<H3><A NAME = "S1"> 1. Introduction</A></H3>
+Perl (Practical Extraction and Report Language) is a scripting language that
+is often used for CGI programming.  Perl is freely available as a
+source kit.<p>
+
+FastCGI has been integrated with Perl in two different ways:
+<ol>
+  <li>By writing a module that plugs into any Perl interpreter that's
+      been built with sfio, a stdio alternative from AT&T.
+  <li>By writing a module that plugs into any Perl interpreter that's
+      been built with FastCGI's fcgi_stdio library
+      layering over stdio.
+</ul>
+The first approach, implemented by Sven Verdoolaege
+(skimo@breughel.ufsia.ac.be), is probably the better of the two,
+since sfio is a generally useful addition to Perl.
+The second approach, implemented by engineers at Open Market,
+predates the availability of an sfio-integrated
+Perl and demonstrates that the fcgi_stdio library
+can be used with a substantial C application.<p>
+
+The two approaches
+are compatible at the Perl source code level; a Perl
+application developed using
+one approach can be run using the other.  And both approaches
+result in a general-purpose Perl interpreter, not a Perl interpreter
+that's only usable for FastCGI applications.<p>
+
+This memo documents both approaches and explains a small
+Perl FastCGI application.<p>
+
+
+<h3><a name ="S2"> 2. Perl with sfio and an FCGI module</a></h3>
+
+As of release 5 patch 3 subpatch 2 (5.003.02), Perl has announced an optional 
+support for sfio (safe/fast string/file I/O), which is an alternative
+to stdio that AT&T distributes freely.  An advantage of sfio over stdio
+is that sfio provides the ability to implement
+new stream classes that don't simply transfer sequential bytes to or from
+a file descriptor.  This flexibility is exactly what FastCGI needs in order
+to implement the standard I/O streams in a manner that's
+transparent to applications.<p>
+
+Perl interpreters incorporating sfio are not widely available in
+binary form, so most likely you'll have to build your own.
+Your build should go smoothly if you follow the instructions
+below.  The instructions assume:<p>
+
+<ul>
+  <li>You are building Perl 5.0 patch level 3 subpatch level 2 (5.003.02) 
+      or higher.  That's the first Perl release to support sfio.<p>
+</ul>
+<P>
+
+Follow these steps to build a Perl with sfio:<p>
+
+<ol>
+  <li>Obtain sfio source code from 
+      <a href="ftp://ftp.funet.fi/pub/languages/perl/CPAN/src/misc">
+      ftp://ftp.funet.fi/pub/languages/perl/CPAN/src/misc</a><p>
+
+  <li>Unpack the tar file using <tt>tar xvf</tt> command.  <EM>$sfio</EM>
+      will be used as a shorthand for the directory in which sfio package 
+      is installed.<p>
+
+  <li>Update your $PATH variable as specified in <tt>$sfio/README</tt> and 
+      run <tt>make</tt> command in the <tt>$sfio/src/lib/sfio</tt> subdirectory.<p>
+
+  <li>Rename or delete the file <tt>$sfio/include/stdio.h</tt>, since it may
+      interfere in the further build process.<p>
+
+  <li>Obtain Perl source (version 5 subversion 003 patchlevel 2 or higher) from
+      <a href="http://fohnix.metronet.com/perlinfo/source/5.0/unsupported">
+      http://fohnix.metronet.com/perlinfo/source/5.0/unsupported</a><p>
+
+  <li>Unpack the tar file using <tt>tar xvf</tt> command.  <EM>$perl</EM> is
+      used as a shorthand for the directory that is created.<p>
+
+  <li>Configure, build, and install Perl as follows:
+
+<pre>
+% cd $perl
+% ./Configure -Duseperlio -Dusesfio
+% make 
+% make test
+% make install
+</pre><p>
+
+There are certain Configure questions that must be answered
+differently when building Perl with sfio:<p>
+
+<DL>
+
+<DT><EM>Perl5 can now use alternate file IO mechanisms to ANSI stdio.
+However these are experimental and may cause problems with some
+extension modules.
+Use stdio as with previous versions? [y] </EM></DT>
+<DD>
+You should answer no.
+</DD><P>
+
+<DT><EM>Any additional cc flags?</EM></DT>
+<DD>
+You should use the following cc flags along with any defaults that Perl
+Configure supplied:
+<UL>
+<LI> <strong>-I<em>$sfio</em>/include</strong>
+</UL>
+</DD><P>
+
+<DT><EM>Any additional ld flags (NOT including libraries):</EM></DT>
+<DD>
+You should specify the following <tt>ld</tt> flags:
+<UL>
+<LI> <strong>-L<em>$sfio</em>/lib</strong>
+</UL>
+</DD><P>
+
+<DT><EM>Additional Libraries:</EM></DT>
+<DD>
+Check that <strong>-lsfio</strong> is one of the specified libraries.  Press 
+return key to continue.  
+</DD><P>
+</DL>
+
+<b>NOTE</b>: If you did not install Perl as a root user, make sure to 
+correctly set environment variable <tt>PERL5LIB</tt> to indicate the location
+of Perl libraries.  For example, if you installed Perl binary into the 
+<tt>$INSTALL</tt> subdirectory and you are running Solaris, the following 
+will set your proper library path: 
+<pre>
+% setenv PERL5LIB $INSTALL/lib:$INSTALL/lib/sun4-solaris/perl5.003_02
+</pre>
+</ul>
+<p>
+
+  <li>Obtain Perl/Sfio module for FastCGI support from 
+      <a href="ftp://ftp.funet.fi/pub/languages/perl/CPAN/authors/id/SKIMO">
+      ftp://ftp.funet.fi/pub/languages/perl/CPAN/authors/id/SKIMO</a><p>
+
+  <li>Unpack FCGI module using <tt>tar</tt> command.  We use <tt>$sfiomod</tt>
+      to denote the subdirectory that is created in the process.<p>
+
+  <li>Build and install the module with the following commands:
+<pre>
+% cd $sfiomod
+% $INSTALL/bin/perl Makefile.PL
+% make
+% make test
+% make install
+</pre>
+</ol>
+
+
+<H3><a NAME = "S3">3. Perl with fcgi_stdio and an FCGI module</a></H3>
+
+<H4><a NAME = "S3.1">3.1 Basic recipe</a></H4>
+
+Here are the assumptions embedded in the following recipe:
+<UL>
+
+<LI>You are building Perl 5.0 Patch 2 (5.002) or higher, since 
+all examples that are provided are based on that release.
+<P></P>
+
+<LI>You have gcc version 2.7 installed on your system, and use it in the
+build.  gcc is convenient because it supports the <tt>-include</tt>
+command-line option that instructs the C preprocessor to include a specific
+file before processing any other include files.  This allows you to include
+<tt>fcgi_stdio.h</tt> without modifying Perl source files.  (The reason for
+specifying gcc version 2.7 is that I have experienced bad behavior with an
+earlier version and the <tt>-include</tt> flag -- the C preprocessor died
+with SIGABRT.)
+<P></P>
+
+<LI> <EM>$fcgi</EM> is used as shorthand for the full path of the FastCGI
+developers kit.
+</UL>
+<P>
+If those are valid assumptions, follow these steps:
+<OL>
+<LI> Pull the Perl source kit from
+<A HREF="http://www.metronet.com/perlinfo/src/latest.tar.gz">
+http://www.metronet.com/perlinfo/src/latest.tar.gz</A>
+<P>
+There are good sources of information on Perl at:
+<UL>
+<LI> <A HREF="http://www.perl.com/">http://www.perl.com/</A>
+<LI> <A HREF="http://www.metronet.com/perlinfo/">http://www.metronet.com/perlinfo/</A>
+</UL>
+</P>
+</A>
+<LI> Unpack the tar file in the parent directory of the FastCGI kit
+directory, so that the perl directory is a sibling of <tt>fcgi-devel-kit</tt>.
+<EM>$perl</EM> is used as shorthand for the full path of the directory
+in which Perl is installed.
+<p>
+<LI> Copy the version specific and the common files from
+<tt>fcgi-devel-kit/perl-5</tt> into the Perl-5 kit.
+<PRE>
+> cd $perl
+> mv perl.c perl.c.orig
+> mv proto.h proto.h.orig
+> mv Configure Configure.orig
+> cp -r ../fcgi-devel-kit/perl-5/perl5.002/* .
+> cp -r ../fcgi-devel-kit/perl-5/common/* .
+</PRE>
+<P>
+The files you are copying contain the Perl-5 FCGI extension, some
+files modified from the distribution, and a script to simplify the
+configuration process.
+</P>
+<LI> Set environment variables.
+The Perl-5 FastCGI configuration process requires that the environment
+variable <TT>FCGIDIR</TT> be set to the top level directory of the FastCGI
+development kit.
+<PRE>
+> setenv FCGIDIR <EM>$fcgi</EM>
+</PRE>
+If you do not want to use <tt>gcc</tt> to build Perl you can set the
+environment variable <TT>CC</TT> to the desired compiler. For example:
+<PRE>
+> setenv CC gcc2.7
+</PRE>
+By default Perl's installation prefix is /usr/local, so binaries get
+installed in /usr/local/bin, library files get installed into
+/usr/local/lib/perl, etc. If you want to specify a different installation
+prefix set the environment variable <tt>PERL_PREFIX</tt>.
+<PRE>
+> setenv PERL_PREFIX /usr/local/perl5-fcgi
+</PRE>
+<LI> Run fcgi-configure.
+<PRE>
+> ./fcgi-configure
+</PRE>
+<P>
+<TT>fcgi-configure</TT> is a wrapper around Perl's <tt>Configure</tt> script.
+It sets some variables according the the value of some environment variables,
+and runs Perl's <tt>Configure</tt> script
+in such a way that it does not prompt the
+user for any input. 90% of the time this should work without a problem.
+If for some reason this does not work for you, you'll have to
+follow the steps in the next section.<p>
+<LI> Run make.
+<PRE>
+> make
+</PRE>
+<LI> Install the newly built Perl-5.
+<PRE>
+> make install
+</PRE>
+</OL><p>
+
+
+<H4><a NAME = "S3.2">3.2 Semi-advanced recipe</a></H4>
+
+If you do not have experience configuring and building Perl, you
+should find someone who does.  Perl can be pretty intimidating to configure
+since it asks you a large number of irrelevant-seeming
+questions that you won't know how to answer.<p>
+<P>
+<OL>
+<LI>Go into the top level directory of the Perl distribution and run
+<tt>Configure</tt>.
+<PRE>
+> cd $perl
+> ./Configure
+</PRE>
+<LI>
+There are some questions that you are going to
+have to answer differently when building FastCGI into Perl.
+These are described below:
+<P></P>
+<DL>
+<DT><EM>Use which C compiler?</EM></DT>
+<DD>
+You should specify <tt>gcc</tt>.
+</DD>
+<P></P>
+<DT><EM>Any additional cc flags?</EM></DT>
+<DD>
+You should use the following cc flags along with any defaults that Perl
+Configure supplied:
+<UL>
+<LI> <strong>-I<em>$fcgi</em>/include</strong>
+<LI> <strong>-include <em>$fcgi</em>/include/fcgi_stdio.h</strong>
+</UL>
+This assumes you are using GCC.
+</DD>
+<P></P>
+
+<DT><EM>Any additional ld flags (NOT including libraries):</EM></DT>
+<DD>
+You should specify the following <tt>ld</tt> flags:
+<UL>
+<LI> <strong>-L<em>$fcgi</em>/libfcgi</strong>
+</UL
+</DD>
+<P></P>
+
+<DT><EM>Additional Libraries:</EM></DT>
+<DD>
+add <strong>-lfcgi</strong> to the list of additional libraries.
+It should be added before -lc.
+</DD>
+<P></P>
+
+<DT><EM>What extensions do you wish to load dynamically?</EM></DT>
+<DD>
+If you can support dynamic extensions, <tt>Configure</tt>
+will ask which of the
+supplied extensions should be loaded dynamically. Since we copied the FCGI
+extension into the Perl source directory it should be one of the ones in the
+default list. If you want FCGI to be dynamically loaded you should specify
+it here, otherwise leave it out.
+</DD>
+<P></P>
+
+<DT><EM>What extensions do you wish to load statically?</EM></DT>
+<DD>
+If you do not support Dynamic extensions this is the only question about
+extensions you would get asked. You should specify FCGI here if you did not
+get asked about dynamic extensions (or did not specify FCGI as a dynamic
+extension).
+</DD>
+</DL>
+<P></P>
+<LI> Copy in the new <tt>proto.h</tt>.
+<P>
+The file proto.h has some macros that conflict with the FastCGI macros.
+The version of <tt>proto.h</tt> supplied in the FastCGI kit
+includes these changes:<p>
+<UL>
+<LI> At the beginning of the file it adds the following lines:
+<PRE>
+#ifdef _FCGI_STDIO
+#undef printf
+#endif
+</PRE>
+<LI> At the bottom it adds:
+<PRE>
+#ifdef _FCGI_STDIO
+#define printf FCGI_printf
+#endif
+</PRE>
+</UL>
+<LI> Copy in the new <tt>perl.c</tt>.
+<P>
+Perl-5.002 has a bug in <tt>perl.c</tt> that has a great
+chance of getting exercised
+with FastCGI.  A fix has been sumbitted to the Perl developers and hopefully
+it'll make it into perl-5.003. It was a one line fix, here is a diff for the
+curious:
+<PRE>
+*** perl.c      1996/03/15 17:10:10     1.1
+--- perl.c      1996/03/15 17:11:23
+***************
+*** 405,410 ****
+--- 405,411 ----
+      if (e_fp) {
+       if (Fflush(e_fp) || ferror(e_fp) || fclose(e_fp))
+           croak("Can't write to temp file for -e: %s", Strerror(errno));
++      e_fp = Nullfp;
+       argc++,argv--;
+       scriptname = e_tmpname;
+      }
+</PRE>
+Pretty straightforward.<p>
+<LI> Build and install Perl.
+<PRE>
+> make
+<EM>[...]</EM>
+> make install
+</PRE>
+</UL>
+</P>
+<H4><a NAME = "S3.3">3.3 Advanced recipe</a></H4>
+
+<P>
+If you already have a Perl-5 package that has been configured, and you do
+not really want to re-run Configure, you can take the following steps.
+</P>
+<P ALIGN=CENTER><STRONG>THIS IS NOT RECOMMENDED</STRONG></P>
+<P>
+Edit config.sh with your favorite editor and modify the following lines:
+<DL>
+<DT><EM>cc</EM></DT>
+<DD>
+Change to use gcc if you are not using it already. 
+</DD>
+<P></P>
+
+<DT><EM>ccflags</EM> AND <EM>cppflags</EM></DT>
+<DD>
+Add the following flags:
+<UL>
+<LI> <strong>-I<em>$fcgi</em>/include</strong>
+<LI> <strong>-include <em>$fcgi</em>/include/fcgi_stdio.h</strong>
+</UL>
+This assumes you are using GCC. See the above section on assumptions
+</DD>
+<P></P>
+
+<DT><EM>extensions</EM> AND <EM>known_extensions</EM></DT>
+<DD>
+Add FCGI to the list of extensions
+</DD>
+<P></P>
+
+<DT><EM>ldflags</EM></DT>
+<DD>
+Add -L $fcgi/libfcgi to the list.
+</DD>
+<P></P>
+
+<DT><EM>libs</EM></DT>
+<DD>
+Add -lfcgi to the list of libraries, should be added before -lc.
+</DD>
+<P></P>
+<DT><EM>static_ext</EM><STRONG> or </STRONG><EM>dynamic_ext</EM></DT>
+<DD>
+Add FCGI to the list of statically or dynamically loaded extensions.
+</DD>
+<P></P>
+<DT><EM>d_stdio_cnt_lval, d_stdio_ptr_lval, d_stdiobase, d_stdstdio</EM></DT>
+<DL>
+Change all of these to undef.
+</DL>
+<P>
+One you have edited config.sh you should do a "make Makefile depend all".
+If you're paranoid like me you may want to do a "make clean" first.
+</P>
+
+
+<H3><A NAME = "S4"> 4. Writing FastCGI applications in Perl</A></H3>
+<P>
+The Perl program <tt>examples/tiny-perl-fcgi</tt> performs the same function as
+the C program <tt>examples/tiny-fcgi</tt> that's used as an example in the
+<A HREF="fcgi-devel-kit.html#S3.1.1">FastCGI Developer's Kit document</A>.
+Here's what the Perl version looks like:
+</P>
+<pre>
+#!./perl
+use FCGI;
+$count = 0;
+while(FCGI::accept() >= 0) {
+    print("Content-type: text/html\r\n\r\n",
+          "&lt;title&gt;FastCGI Hello! (Perl)&lt;/title&gt;\n",
+          "&lt;h1&gt;FastCGI Hello! (Perl)&lt;/h1&gt;\n",
+          "Request number ", $++count,
+          " running on host &lt;i&gt;$ENV('SERVER_NAME')&lt;/i&gt;");
+}
+</pre>
+
+If you've built Perl according to the recipe and you have a Web server set
+up to run FastCGI applications, load the FastCGI Developer's Kit Index Page
+in that server and run this Perl application now.<p>
+
+The script invokes Perl indirectly via the symbolic link
+<tt>examples/perl</tt>.  It does this because HP-UX has a limit of 32
+characters for the first line of a command-interpreter file such as
+<tt>examples/tiny-perl-fcgi</tt>.  If you run on HP-UX you won't want
+to sprinkle symbolic links to perl everywhere, so you should
+choose a <tt>PERL_PREFIX</tt> shorter than <tt>/usr/local/perl5-fcgi</tt>.<p>
+
+You need to be aware of the following bug.  If the
+initial environment to a FastCGI Perl application is empty (contains
+no name-value pairs) then when the first call to <tt>FCGI::accept</tt>
+returns, the environment will <i>still</i> be empty,
+i.e. <tt>%ENV</tt> will contain no associations.  All the variables
+associated with the first request are lost.  There are two known
+workarounds:<p>
+
+<ul>
+  <li>
+    In your Perl application, enumerate <tt>%ENV</tt> using
+    <tt>each</tt> before entering the <tt>FCGI::accept</tt>
+    loop.  The program <tt>examples/tiny-perl-fcgi</tt>
+    contains code for this.<p>
+  <li>
+    In configuring your application be sure to set at least one
+    initial environment variable.  You do this with the
+    <tt>AppClass -initial-env</tt> directive to the Web server,
+    or by running <tt>cgi-fcgi</tt> in a non-empty environment.
+</ul><p>
+
+The Perl subroutine <tt>FCGI::accept</tt> treats the initial
+environment differently than the C function <tt>FCGI_Accept</tt>.  The
+first call to the
+C function <tt>FCGI_Accept</tt> replaces the initial environment with
+the environment of the first request.  The first call to the Perl subroutine
+<tt>FCGI::accept</tt> adds the variable bindings of the first request
+to the bindings present in the initial environment.  So when the first
+call to <tt>FCGI::accept</tt> returns, bindings from the initial
+environment are still there (unless, due to naming conflicts, some of
+them have been overwritten by the first request).  The next call to
+<tt>FCGI::accept</tt> removes the bindings made on the previous call
+before adding a new set for the request just accepted, again preserving
+the initial environment.<p>
+
+The Perl <tt>FCGI</tt> module also includes
+subroutines <tt>FCGI::finish</tt>, <tt>FCGI::set_exit_status</tt>,
+and <tt>FCGI::start_filter_data</tt> that correspond to
+C functions in <tt>fcgi_stdio.h</tt>; see the manpages for
+full information.<p>
+
+Converting a Perl CGI application to FastCGI is not fundamentally
+different from converting a C CGI application.  You separate
+the portion of the application that performs one-time
+initialization from the portion that performs per-request
+processing.  You put the per-request processing into a loop
+controlled by <tt>FCGI::accept</tt>.<p>
+
diff --git a/doc/fcgi-perl.htm b/doc/fcgi-perl.htm
new file mode 100644 (file)
index 0000000..058f10d
--- /dev/null
@@ -0,0 +1,556 @@
+<html>
+<head><title>Integrating FastCGI with Perl-5</title>
+</head>
+
+<body bgcolor="#FFFFFF" text="#000000" link="#cc0000" alink="#000011" 
+vlink="#555555">
+
+<center>
+<a href="/fastcgi/words">
+    <img border=0 src="/kit/images/fcgi-hd.gif" alt="[[FastCGI]]"></a>
+</center>
+<br clear=all>
+<h3><center>Integrating FastCGI with Perl-5</center></h3>
+
+<!--Copyright (c) 1996 Open Market, Inc.                                    -->
+<!--See the file "LICENSE.TERMS" for information on usage and redistribution-->
+<!--of this file, and for a DISCLAIMER OF ALL WARRANTIES.                   -->
+
+<h5 align=center>
+Copyright &copy; 1996 Open Market, Inc.  245 First Street, Cambridge,
+  MA 02142 U.S.A.<br>
+Tel: 617-949-7000 URL:
+  <a href="http://www.openmarket.com/">http://www.openmarket.com/</a><br>
+$Id: fcgi-perl.htm,v 1.1 1997/09/16 15:36:26 stanleyg Exp $ <br>
+</h5>
+<hr>
+
+<ul type=square>
+  <li><a HREF = "#S1">1. Introduction</a>
+  <li><a HREF = "#S2">2. Perl with sfio and an FCGI module</a>
+  <li><a HREF = "#S3">3. Perl with fcgi_stdio and an FCGI module</a>
+  <ul type=square>
+    <li><a HREF = "#S3.1">3.1 Basic recipe</a>
+    <li><a HREF = "#S3.2">3.2 Semi-advanced recipe</a>
+    <li><a HREF = "#S3.3">3.3 Advanced recipe</a>
+  </ul>
+  <li><a HREF = "#S4">4. Writing FastCGI applications in Perl</a>
+</ul>
+
+
+<H3><A NAME = "S1"> 1. Introduction</A></H3>
+Perl (Practical Extraction and Report Language) is a scripting language that
+is often used for CGI programming.  Perl is freely available as a
+source kit.<p>
+
+FastCGI has been integrated with Perl in two different ways:
+<ol>
+  <li>By writing a module that plugs into any Perl interpreter that's
+      been built with sfio, a stdio alternative from AT&T.
+  <li>By writing a module that plugs into any Perl interpreter that's
+      been built with FastCGI's fcgi_stdio library
+      layering over stdio.
+</ul>
+The first approach, implemented by Sven Verdoolaege
+(skimo@breughel.ufsia.ac.be), is probably the better of the two,
+since sfio is a generally useful addition to Perl.
+The second approach, implemented by engineers at Open Market,
+predates the availability of an sfio-integrated
+Perl and demonstrates that the fcgi_stdio library
+can be used with a substantial C application.<p>
+
+The two approaches
+are compatible at the Perl source code level; a Perl
+application developed using
+one approach can be run using the other.  And both approaches
+result in a general-purpose Perl interpreter, not a Perl interpreter
+that's only usable for FastCGI applications.<p>
+
+This memo documents both approaches and explains a small
+Perl FastCGI application.<p>
+
+
+<h3><a name ="S2"> 2. Perl with sfio and an FCGI module</a></h3>
+
+As of release 5 patch 3 subpatch 2 (5.003.02), Perl has announced an optional 
+support for sfio (safe/fast string/file I/O), which is an alternative
+to stdio that AT&T distributes freely.  An advantage of sfio over stdio
+is that sfio provides the ability to implement
+new stream classes that don't simply transfer sequential bytes to or from
+a file descriptor.  This flexibility is exactly what FastCGI needs in order
+to implement the standard I/O streams in a manner that's
+transparent to applications.<p>
+
+Perl interpreters incorporating sfio are not widely available in
+binary form, so most likely you'll have to build your own.
+Your build should go smoothly if you follow the instructions
+below.  The instructions assume:<p>
+
+<ul>
+  <li>You are building Perl 5.0 patch level 3 subpatch level 2 (5.003.02) 
+      or higher.  That's the first Perl release to support sfio.<p>
+</ul>
+<P>
+
+Follow these steps to build a Perl with sfio:<p>
+
+<ol>
+  <li>Obtain sfio source code from 
+      <a href="ftp://ftp.funet.fi/pub/languages/perl/CPAN/src/misc">
+      ftp://ftp.funet.fi/pub/languages/perl/CPAN/src/misc</a><p>
+
+  <li>Unpack the tar file using <tt>tar xvf</tt> command.  <EM>$sfio</EM>
+      will be used as a shorthand for the directory in which sfio package 
+      is installed.<p>
+
+  <li>Update your $PATH variable as specified in <tt>$sfio/README</tt> and 
+      run <tt>make</tt> command in the <tt>$sfio/src/lib/sfio</tt> subdirectory.<p>
+
+  <li>Rename or delete the file <tt>$sfio/include/stdio.h</tt>, since it may
+      interfere in the further build process.<p>
+
+  <li>Obtain Perl source (version 5 subversion 003 patchlevel 2 or higher) from
+      <a href="http://fohnix.metronet.com/perlinfo/source/5.0/unsupported">
+      http://fohnix.metronet.com/perlinfo/source/5.0/unsupported</a><p>
+
+  <li>Unpack the tar file using <tt>tar xvf</tt> command.  <EM>$perl</EM> is
+      used as a shorthand for the directory that is created.<p>
+
+  <li>Configure, build, and install Perl as follows:
+
+<pre>
+% cd $perl
+% ./Configure -Duseperlio -Dusesfio
+% make 
+% make test
+% make install
+</pre><p>
+
+There are certain Configure questions that must be answered
+differently when building Perl with sfio:<p>
+
+<DL>
+
+<DT><EM>Perl5 can now use alternate file IO mechanisms to ANSI stdio.
+However these are experimental and may cause problems with some
+extension modules.
+Use stdio as with previous versions? [y] </EM></DT>
+<DD>
+You should answer no.
+</DD><P>
+
+<DT><EM>Any additional cc flags?</EM></DT>
+<DD>
+You should use the following cc flags along with any defaults that Perl
+Configure supplied:
+<UL>
+<LI> <strong>-I<em>$sfio</em>/include</strong>
+</UL>
+</DD><P>
+
+<DT><EM>Any additional ld flags (NOT including libraries):</EM></DT>
+<DD>
+You should specify the following <tt>ld</tt> flags:
+<UL>
+<LI> <strong>-L<em>$sfio</em>/lib</strong>
+</UL>
+</DD><P>
+
+<DT><EM>Additional Libraries:</EM></DT>
+<DD>
+Check that <strong>-lsfio</strong> is one of the specified libraries.  Press 
+return key to continue.  
+</DD><P>
+</DL>
+
+<b>NOTE</b>: If you did not install Perl as a root user, make sure to 
+correctly set environment variable <tt>PERL5LIB</tt> to indicate the location
+of Perl libraries.  For example, if you installed Perl binary into the 
+<tt>$INSTALL</tt> subdirectory and you are running Solaris, the following 
+will set your proper library path: 
+<pre>
+% setenv PERL5LIB $INSTALL/lib:$INSTALL/lib/sun4-solaris/perl5.003_02
+</pre>
+</ul>
+<p>
+
+  <li>Obtain Perl/Sfio module for FastCGI support from 
+      <a href="ftp://ftp.funet.fi/pub/languages/perl/CPAN/authors/id/SKIMO">
+      ftp://ftp.funet.fi/pub/languages/perl/CPAN/authors/id/SKIMO</a><p>
+
+  <li>Unpack FCGI module using <tt>tar</tt> command.  We use <tt>$sfiomod</tt>
+      to denote the subdirectory that is created in the process.<p>
+
+  <li>Build and install the module with the following commands:
+<pre>
+% cd $sfiomod
+% $INSTALL/bin/perl Makefile.PL
+% make
+% make test
+% make install
+</pre>
+</ol>
+
+
+<H3><a NAME = "S3">3. Perl with fcgi_stdio and an FCGI module</a></H3>
+
+<H4><a NAME = "S3.1">3.1 Basic recipe</a></H4>
+
+Here are the assumptions embedded in the following recipe:
+<UL>
+
+<LI>You are building Perl 5.0 Patch 2 (5.002) or higher, since 
+all examples that are provided are based on that release.
+<P></P>
+
+<LI>You have gcc version 2.7 installed on your system, and use it in the
+build.  gcc is convenient because it supports the <tt>-include</tt>
+command-line option that instructs the C preprocessor to include a specific
+file before processing any other include files.  This allows you to include
+<tt>fcgi_stdio.h</tt> without modifying Perl source files.  (The reason for
+specifying gcc version 2.7 is that I have experienced bad behavior with an
+earlier version and the <tt>-include</tt> flag -- the C preprocessor died
+with SIGABRT.)
+<P></P>
+
+<LI> <EM>$fcgi</EM> is used as shorthand for the full path of the FastCGI
+developers kit.
+</UL>
+<P>
+If those are valid assumptions, follow these steps:
+<OL>
+<LI> Pull the Perl source kit from
+<A HREF="http://www.metronet.com/perlinfo/src/latest.tar.gz">
+http://www.metronet.com/perlinfo/src/latest.tar.gz</A>
+<P>
+There are good sources of information on Perl at:
+<UL>
+<LI> <A HREF="http://www.perl.com/">http://www.perl.com/</A>
+<LI> <A HREF="http://www.metronet.com/perlinfo/">http://www.metronet.com/perlinfo/</A>
+</UL>
+</P>
+</A>
+<LI> Unpack the tar file in the parent directory of the FastCGI kit
+directory, so that the perl directory is a sibling of <tt>fcgi-devel-kit</tt>.
+<EM>$perl</EM> is used as shorthand for the full path of the directory
+in which Perl is installed.
+<p>
+<LI> Copy the version specific and the common files from
+<tt>fcgi-devel-kit/perl-5</tt> into the Perl-5 kit.
+<PRE>
+> cd $perl
+> mv perl.c perl.c.orig
+> mv proto.h proto.h.orig
+> mv Configure Configure.orig
+> cp -r ../fcgi-devel-kit/perl-5/perl5.002/* .
+> cp -r ../fcgi-devel-kit/perl-5/common/* .
+</PRE>
+<P>
+The files you are copying contain the Perl-5 FCGI extension, some
+files modified from the distribution, and a script to simplify the
+configuration process.
+</P>
+<LI> Set environment variables.
+The Perl-5 FastCGI configuration process requires that the environment
+variable <TT>FCGIDIR</TT> be set to the top level directory of the FastCGI
+development kit.
+<PRE>
+> setenv FCGIDIR <EM>$fcgi</EM>
+</PRE>
+If you do not want to use <tt>gcc</tt> to build Perl you can set the
+environment variable <TT>CC</TT> to the desired compiler. For example:
+<PRE>
+> setenv CC gcc2.7
+</PRE>
+By default Perl's installation prefix is /usr/local, so binaries get
+installed in /usr/local/bin, library files get installed into
+/usr/local/lib/perl, etc. If you want to specify a different installation
+prefix set the environment variable <tt>PERL_PREFIX</tt>.
+<PRE>
+> setenv PERL_PREFIX /usr/local/perl5-fcgi
+</PRE>
+<LI> Run fcgi-configure.
+<PRE>
+> ./fcgi-configure
+</PRE>
+<P>
+<TT>fcgi-configure</TT> is a wrapper around Perl's <tt>Configure</tt> script.
+It sets some variables according the the value of some environment variables,
+and runs Perl's <tt>Configure</tt> script
+in such a way that it does not prompt the
+user for any input. 90% of the time this should work without a problem.
+If for some reason this does not work for you, you'll have to
+follow the steps in the next section.<p>
+<LI> Run make.
+<PRE>
+> make
+</PRE>
+<LI> Install the newly built Perl-5.
+<PRE>
+> make install
+</PRE>
+</OL><p>
+
+
+<H4><a NAME = "S3.2">3.2 Semi-advanced recipe</a></H4>
+
+If you do not have experience configuring and building Perl, you
+should find someone who does.  Perl can be pretty intimidating to configure
+since it asks you a large number of irrelevant-seeming
+questions that you won't know how to answer.<p>
+<P>
+<OL>
+<LI>Go into the top level directory of the Perl distribution and run
+<tt>Configure</tt>.
+<PRE>
+> cd $perl
+> ./Configure
+</PRE>
+<LI>
+There are some questions that you are going to
+have to answer differently when building FastCGI into Perl.
+These are described below:
+<P></P>
+<DL>
+<DT><EM>Use which C compiler?</EM></DT>
+<DD>
+You should specify <tt>gcc</tt>.
+</DD>
+<P></P>
+<DT><EM>Any additional cc flags?</EM></DT>
+<DD>
+You should use the following cc flags along with any defaults that Perl
+Configure supplied:
+<UL>
+<LI> <strong>-I<em>$fcgi</em>/include</strong>
+<LI> <strong>-include <em>$fcgi</em>/include/fcgi_stdio.h</strong>
+</UL>
+This assumes you are using GCC.
+</DD>
+<P></P>
+
+<DT><EM>Any additional ld flags (NOT including libraries):</EM></DT>
+<DD>
+You should specify the following <tt>ld</tt> flags:
+<UL>
+<LI> <strong>-L<em>$fcgi</em>/libfcgi</strong>
+</UL
+</DD>
+<P></P>
+
+<DT><EM>Additional Libraries:</EM></DT>
+<DD>
+add <strong>-lfcgi</strong> to the list of additional libraries.
+It should be added before -lc.
+</DD>
+<P></P>
+
+<DT><EM>What extensions do you wish to load dynamically?</EM></DT>
+<DD>
+If you can support dynamic extensions, <tt>Configure</tt>
+will ask which of the
+supplied extensions should be loaded dynamically. Since we copied the FCGI
+extension into the Perl source directory it should be one of the ones in the
+default list. If you want FCGI to be dynamically loaded you should specify
+it here, otherwise leave it out.
+</DD>
+<P></P>
+
+<DT><EM>What extensions do you wish to load statically?</EM></DT>
+<DD>
+If you do not support Dynamic extensions this is the only question about
+extensions you would get asked. You should specify FCGI here if you did not
+get asked about dynamic extensions (or did not specify FCGI as a dynamic
+extension).
+</DD>
+</DL>
+<P></P>
+<LI> Copy in the new <tt>proto.h</tt>.
+<P>
+The file proto.h has some macros that conflict with the FastCGI macros.
+The version of <tt>proto.h</tt> supplied in the FastCGI kit
+includes these changes:<p>
+<UL>
+<LI> At the beginning of the file it adds the following lines:
+<PRE>
+#ifdef _FCGI_STDIO
+#undef printf
+#endif
+</PRE>
+<LI> At the bottom it adds:
+<PRE>
+#ifdef _FCGI_STDIO
+#define printf FCGI_printf
+#endif
+</PRE>
+</UL>
+<LI> Copy in the new <tt>perl.c</tt>.
+<P>
+Perl-5.002 has a bug in <tt>perl.c</tt> that has a great
+chance of getting exercised
+with FastCGI.  A fix has been sumbitted to the Perl developers and hopefully
+it'll make it into perl-5.003. It was a one line fix, here is a diff for the
+curious:
+<PRE>
+*** perl.c      1996/03/15 17:10:10     1.1
+--- perl.c      1996/03/15 17:11:23
+***************
+*** 405,410 ****
+--- 405,411 ----
+      if (e_fp) {
+       if (Fflush(e_fp) || ferror(e_fp) || fclose(e_fp))
+           croak("Can't write to temp file for -e: %s", Strerror(errno));
++      e_fp = Nullfp;
+       argc++,argv--;
+       scriptname = e_tmpname;
+      }
+</PRE>
+Pretty straightforward.<p>
+<LI> Build and install Perl.
+<PRE>
+> make
+<EM>[...]</EM>
+> make install
+</PRE>
+</UL>
+</P>
+<H4><a NAME = "S3.3">3.3 Advanced recipe</a></H4>
+
+<P>
+If you already have a Perl-5 package that has been configured, and you do
+not really want to re-run Configure, you can take the following steps.
+</P>
+<P ALIGN=CENTER><STRONG>THIS IS NOT RECOMMENDED</STRONG></P>
+<P>
+Edit config.sh with your favorite editor and modify the following lines:
+<DL>
+<DT><EM>cc</EM></DT>
+<DD>
+Change to use gcc if you are not using it already. 
+</DD>
+<P></P>
+
+<DT><EM>ccflags</EM> AND <EM>cppflags</EM></DT>
+<DD>
+Add the following flags:
+<UL>
+<LI> <strong>-I<em>$fcgi</em>/include</strong>
+<LI> <strong>-include <em>$fcgi</em>/include/fcgi_stdio.h</strong>
+</UL>
+This assumes you are using GCC. See the above section on assumptions
+</DD>
+<P></P>
+
+<DT><EM>extensions</EM> AND <EM>known_extensions</EM></DT>
+<DD>
+Add FCGI to the list of extensions
+</DD>
+<P></P>
+
+<DT><EM>ldflags</EM></DT>
+<DD>
+Add -L $fcgi/libfcgi to the list.
+</DD>
+<P></P>
+
+<DT><EM>libs</EM></DT>
+<DD>
+Add -lfcgi to the list of libraries, should be added before -lc.
+</DD>
+<P></P>
+<DT><EM>static_ext</EM><STRONG> or </STRONG><EM>dynamic_ext</EM></DT>
+<DD>
+Add FCGI to the list of statically or dynamically loaded extensions.
+</DD>
+<P></P>
+<DT><EM>d_stdio_cnt_lval, d_stdio_ptr_lval, d_stdiobase, d_stdstdio</EM></DT>
+<DL>
+Change all of these to undef.
+</DL>
+<P>
+One you have edited config.sh you should do a "make Makefile depend all".
+If you're paranoid like me you may want to do a "make clean" first.
+</P>
+
+
+<H3><A NAME = "S4"> 4. Writing FastCGI applications in Perl</A></H3>
+<P>
+The Perl program <tt>examples/tiny-perl-fcgi</tt> performs the same function as
+the C program <tt>examples/tiny-fcgi</tt> that's used as an example in the
+<A HREF="fcgi-devel-kit.html#S3.1.1">FastCGI Developer's Kit document</A>.
+Here's what the Perl version looks like:
+</P>
+<pre>
+#!./perl
+use FCGI;
+$count = 0;
+while(FCGI::accept() >= 0) {
+    print("Content-type: text/html\r\n\r\n",
+          "&lt;title&gt;FastCGI Hello! (Perl)&lt;/title&gt;\n",
+          "&lt;h1&gt;FastCGI Hello! (Perl)&lt;/h1&gt;\n",
+          "Request number ", $++count,
+          " running on host &lt;i&gt;$ENV('SERVER_NAME')&lt;/i&gt;");
+}
+</pre>
+
+If you've built Perl according to the recipe and you have a Web server set
+up to run FastCGI applications, load the FastCGI Developer's Kit Index Page
+in that server and run this Perl application now.<p>
+
+The script invokes Perl indirectly via the symbolic link
+<tt>examples/perl</tt>.  It does this because HP-UX has a limit of 32
+characters for the first line of a command-interpreter file such as
+<tt>examples/tiny-perl-fcgi</tt>.  If you run on HP-UX you won't want
+to sprinkle symbolic links to perl everywhere, so you should
+choose a <tt>PERL_PREFIX</tt> shorter than <tt>/usr/local/perl5-fcgi</tt>.<p>
+
+You need to be aware of the following bug.  If the
+initial environment to a FastCGI Perl application is empty (contains
+no name-value pairs) then when the first call to <tt>FCGI::accept</tt>
+returns, the environment will <i>still</i> be empty,
+i.e. <tt>%ENV</tt> will contain no associations.  All the variables
+associated with the first request are lost.  There are two known
+workarounds:<p>
+
+<ul>
+  <li>
+    In your Perl application, enumerate <tt>%ENV</tt> using
+    <tt>each</tt> before entering the <tt>FCGI::accept</tt>
+    loop.  The program <tt>examples/tiny-perl-fcgi</tt>
+    contains code for this.<p>
+  <li>
+    In configuring your application be sure to set at least one
+    initial environment variable.  You do this with the
+    <tt>AppClass -initial-env</tt> directive to the Web server,
+    or by running <tt>cgi-fcgi</tt> in a non-empty environment.
+</ul><p>
+
+The Perl subroutine <tt>FCGI::accept</tt> treats the initial
+environment differently than the C function <tt>FCGI_Accept</tt>.  The
+first call to the
+C function <tt>FCGI_Accept</tt> replaces the initial environment with
+the environment of the first request.  The first call to the Perl subroutine
+<tt>FCGI::accept</tt> adds the variable bindings of the first request
+to the bindings present in the initial environment.  So when the first
+call to <tt>FCGI::accept</tt> returns, bindings from the initial
+environment are still there (unless, due to naming conflicts, some of
+them have been overwritten by the first request).  The next call to
+<tt>FCGI::accept</tt> removes the bindings made on the previous call
+before adding a new set for the request just accepted, again preserving
+the initial environment.<p>
+
+The Perl <tt>FCGI</tt> module also includes
+subroutines <tt>FCGI::finish</tt>, <tt>FCGI::set_exit_status</tt>,
+and <tt>FCGI::start_filter_data</tt> that correspond to
+C functions in <tt>fcgi_stdio.h</tt>; see the manpages for
+full information.<p>
+
+Converting a Perl CGI application to FastCGI is not fundamentally
+different from converting a C CGI application.  You separate
+the portion of the application that performs one-time
+initialization from the portion that performs per-request
+processing.  You put the per-request processing into a loop
+controlled by <tt>FCGI::accept</tt>.<p>
+
+</body>
+</html>
diff --git a/doc/fcgi-spec.html b/doc/fcgi-spec.html
new file mode 100644 (file)
index 0000000..2fce425
--- /dev/null
@@ -0,0 +1,1259 @@
+<html>
+<!--Copyright (c) 1996 Open Market, Inc.                                    -->
+<!--See the file "LICENSE.TERMS" for information on usage and redistribution-->
+<!--of this file, and for a DISCLAIMER OF ALL WARRANTIES.                   -->
+<head>
+<title>FastCGI Specification</title>
+</head>
+
+<body>
+<center>
+<h2>FastCGI Specification</h2>
+</center>
+
+<center>
+Mark R. Brown<br>
+Open Market, Inc.<br>
+<p>
+
+Document Version: 1.0<br>
+29 April 1996<br>
+</center>
+<p>
+
+<h5 align=center>
+Copyright &copy; 1996 Open Market, Inc.  245 First Street, Cambridge,
+  MA 02142 U.S.A.<br>
+Tel: 617-621-9500 Fax: 617-621-1703 URL:
+  <a href="http://www.openmarket.com/">http://www.openmarket.com/</a><br>
+<br>
+$Id: fcgi-spec.html,v 1.1 1997/09/16 15:36:26 stanleyg Exp $
+</h5>
+<hr>
+
+
+<ul type=square>
+    <li><a HREF = "#S1">1. Introduction</a>
+    <li><a HREF = "#S2">2. Initial Process State</a>
+    <ul type=square>
+        <li><a HREF = "#S2.1">2.1 Argument list</a>
+        <li><a HREF = "#S2.2">2.2 File descriptors</a>
+        <li><a HREF = "#S2.3">2.3 Environment variables</a>
+        <li><a HREF = "#S2.4">2.4 Other state</a>
+    </ul>
+    <li><a HREF = "#S3">3. Protocol Basics</a>
+    <ul type=square>
+        <li><a HREF = "#S3.1">3.1 Notation</a>
+        <li><a HREF = "#S3.2">3.2 Accepting Transport Connections</a>
+        <li><a HREF = "#S3.3">3.3 Records</a>
+        <li><a HREF = "#S3.4">3.4 Name-Value Pairs</a>
+        <li><a HREF = "#S3.5">3.5 Closing Transport Connections</a>
+    </ul>
+    <li><a HREF = "#S4">4. Management Record Types</a>
+    <ul type=square>
+        <li><a HREF = "#S4.1">4.1 <tt>FCGI_GET_VALUES, FCGI_GET_VALUES_RESULT</tt></a>
+        <li><a HREF = "#S4.2">4.2 <tt>FCGI_UNKNOWN_TYPE</tt></a>
+    </ul>
+    <li><a HREF = "#S5">5. Application Record Types</a>
+    <ul type=square>
+        <li><a HREF = "#S5.1">5.1 <tt>FCGI_BEGIN_REQUEST</tt></a>
+        <li><a HREF = "#S5.2">5.2 Name-Value Pair Streams: <tt>FCGI_PARAMS</tt>, <tt>FCGI_RESULTS</tt></a>
+        <li><a HREF = "#S5.3">5.3 Byte Streams: <tt>FCGI_STDIN</tt>, <tt>FCGI_DATA</tt>, <tt>FCGI_STDOUT</tt>, <tt>FCGI_STDERR</tt></a>
+        <li><a HREF = "#S5.4">5.4 <tt>FCGI_ABORT_REQUEST</tt></a>
+        <li><a HREF = "#S5.5">5.5 <tt>FCGI_END_REQUEST</tt></a>
+    </ul>
+    <li><a HREF = "#S6">6. Roles</a>
+    <ul type=square>
+        <li><a HREF = "#S6.1">6.1 Role Protocols</a>
+        <li><a HREF = "#S6.2">6.2 Responder</a>
+        <li><a HREF = "#S6.3">6.3 Authorizer</a>
+        <li><a HREF = "#S6.5">6.4 Filter</a>
+    </ul>
+    <li><a HREF = "#S7">7. Errors</a>
+    <li><a HREF = "#S8">8. Types and Constants</a>
+    <li><a HREF = "#S9">9. References</a>
+    <li><a HREF = "#SA">A. Table: Properties of the record types</a>
+    <li><a HREF = "#SB">B. Typical Protocol Message Flow</a>
+</ul>
+<p>
+
+<hr>
+
+
+<h3><a name = "S1">1. Introduction</a></h3>
+
+FastCGI is an open extension to CGI that provides high performance
+for all Internet applications without the penalties of Web server
+APIs.<p>
+
+This specification has narrow
+goal: to specify, from an application perspective, the
+interface between a FastCGI application and a Web server that supports
+FastCGI.  Many Web server features related to FastCGI,
+e.g. application management facilities, have nothing to do with the
+application to Web server interface, and are not described here.<p>
+
+This specification is for Unix (more precisely, for POSIX systems that support
+Berkeley Sockets).  The bulk of the specification is a simple
+communications protocol that is independent of byte ordering
+and will extend to other systems.<p>
+
+We'll introduce FastCGI by comparing it with conventional Unix
+implementations of CGI/1.1.
+
+FastCGI is designed to support long-lived application processes,
+i.e. <i>application servers</i>.  That's a major difference
+compared with conventional Unix implementations of CGI/1.1,
+which construct an application process, use
+it respond to one request, and have it exit.<p>
+
+The initial state of a FastCGI process is more spartan than the initial
+state of a CGI/1.1 process, because the FastCGI process doesn't begin life
+connected to anything.  It doesn't have the conventional open files
+<tt>stdin</tt>, <tt>stdout</tt>, and <tt>stderr</tt>, and it doesn't
+receive much information through environment variables.  The key
+piece of initial state in a FastCGI process is a listening
+socket, through which it accepts connections from a Web server.<p>
+
+After a FastCGI process accepts a connection on its listening socket,
+the process executes a simple protocol to receive and send data.  The
+protocol serves two purposes.  First, the protocol
+multiplexes a single transport connection between several independent
+FastCGI requests.  This supports applications that are able to process
+concurrent requests using event-driven or multi-threaded programming
+techniques.  Second, within each request the protocol provides several
+independent data streams in each direction.  This way, for instance,
+both <tt>stdout</tt> and <tt>stderr</tt> data pass over a single
+transport connection from the application to the Web server, rather
+than requiring separate pipes as with CGI/1.1.<p>
+
+A FastCGI application plays one of several well-defined <i>roles</i>.
+The most familiar is the <i>Responder</i> role, in which the
+application receives all the information associated with an HTTP
+request and generates an HTTP response; that's the role CGI/1.1
+programs play.  A second role is <i>Authorizer</i>, in which the
+application receives all the information associated with an HTTP
+request and generates an authorized/unauthorized decision.
+A third role is <i>Filter</i>, in which the
+application receives all the information associated with an HTTP
+request, plus an extra stream of data from a file stored on the Web
+server, and generates a "filtered" version of the data stream as
+an HTTP response.  The framework is extensible so that more FastCGI
+can be defined later.<p>
+
+In the remainder of this specification the terms "FastCGI
+application," "application process," or "application server" are
+abbreviated to "application" whenever that won't cause confusion.<p>
+
+
+
+<h3><a name = "S2">2. Initial Process State</a></h3>
+
+
+<h4><a name = "S2.1">2.1 Argument list</a></h4>
+
+By default the Web server creates an argument list containing a single
+element, the name of the application, taken to be the last component
+of the executable's path name.  The Web server may provide a way
+to specify a different application name, or a more elaborate argument
+list.<p>
+
+Note that the file executed by the Web server might be an interpreter
+file (a text file that starts with the characters <tt>#!</tt>), in
+which case the application's argument list is constructed as described
+in the <tt>execve</tt> manpage.<p>
+
+
+<h4><a name = "S2.2">2.2 File descriptors</a></h4>
+
+The Web server leaves a single file descriptor,
+<tt>FCGI_LISTENSOCK_FILENO</tt>, open when the application begins
+execution.  This descriptor refers to a listening socket created by
+the Web server.<p>
+
+<tt>FCGI_LISTENSOCK_FILENO</tt> equals <tt>STDIN_FILENO</tt>.
+The standard descriptors
+<tt>STDOUT_FILENO</tt> and <tt>STDERR_FILENO</tt> are closed when
+the application begins execution.  A reliable method for an application
+to determine whether it was invoked using CGI or FastCGI is to call
+<tt>getpeername(FCGI_LISTENSOCK_FILENO)</tt>, which returns
+-1 with <tt>errno</tt> set to <tt>ENOTCONN</tt> for
+a FastCGI application.<p>
+
+The Web server's choice of reliable transport, Unix stream pipes
+(<tt>AF_UNIX</tt>) or TCP/IP (<tt>AF_INET</tt>), is implicit in the
+internal state of the <tt>FCGI_LISTENSOCK_FILENO</tt> socket.<p>
+
+
+<h4><a name = "S2.3">2.3 Environment variables</a></h4>
+
+The Web server may use environment variables to pass parameters
+to the application.  This specification defines one such
+variable, <tt>FCGI_WEB_SERVER_ADDRS</tt>; we expect more to
+be defined as the specification evolves.
+
+The Web server may provide a way to bind other environment
+variables, such as the <tt>PATH</tt> variable.<p>
+
+
+<h4><a name = "S2.4">2.4 Other state</a></h4>
+
+The Web server may provide a way to specify other components of an
+application's initial process state, such as the priority,
+user ID, group ID, root directory, and working directory of the
+process.<p>
+
+
+
+<h3><a name = "S3">3. Protocol Basics</a></h3>
+
+
+<h4><a name = "S3.1">3.1 Notation</a></h4>
+
+We use C language notation to define protocol message
+formats.  All structure elements are defined in terms
+of the <tt>unsigned char</tt> type, and are arranged
+so that an ISO C compiler lays them out in the obvious
+manner, with no padding.  The first byte defined in the
+structure is transmitted first, the second byte second, etc.<p>
+
+We use two conventions to abbreviate our definitions.<p>
+
+First, when two adjacent structure components are named identically
+except for the suffixes "<tt>B1</tt>" and "<tt>B0</tt>," it means that
+the two components may be viewed as a single number, computed as
+<tt>B1<<8 + B0</tt>.  The name of this single number is the name of
+the components, minus the suffixes.  This convention generalizes in an
+obvious way to handle numbers represented in more than two bytes.<p>
+
+Second, we extend C <tt>struct</tt>s to allow the form
+<pre>
+        struct {
+            unsigned char mumbleLengthB1;
+            unsigned char mumbleLengthB0;
+            ... /* other stuff */
+            unsigned char mumbleData[mumbleLength];
+        };
+</pre>
+meaning a structure of varying length, where the length of
+a component is determined by the values of the indicated
+earlier component or components.<p>
+
+
+<h4><a name = "S3.2">3.2 Accepting Transport Connections</a></h4>
+
+A FastCGI application calls <tt>accept()</tt> on the socket referred to
+by file descriptor <tt>FCGI_LISTENSOCK_FILENO</tt> to accept a new
+transport connection.
+
+If the <tt>accept()</tt> succeeds, and the <tt>FCGI_WEB_SERVER_ADDRS</tt>
+environment variable is bound, the application
+application immediately performs the following
+special processing:<p>
+
+<ul type=square>
+    <li><tt>FCGI_WEB_SERVER_ADDRS</tt>:
+
+        The value is a list of valid IP addresses for the Web server.<p>
+
+        If <tt>FCGI_WEB_SERVER_ADDRS</tt>
+        was bound, the application checks the peer
+        IP address of the new connection for membership in the list.
+        If the check fails (including the possibility that the
+        connection didn't use TCP/IP transport), the application
+        responds by closing the connection.<p>
+
+        <tt>FCGI_WEB_SERVER_ADDRS</tt>
+        is expressed as a comma-separated list of IP
+        addresses.  Each IP address is written as four decimal numbers
+        in the range [0..255] separated by decimal points.  So one
+        legal binding for this variable is
+        <tt>FCGI_WEB_SERVER_ADDRS=199.170.183.28,199.170.183.71</tt>.<p>
+</ul>
+
+An application may accept several concurrent transport
+connections, but it need not do so.<p>
+
+
+<h4><a name = "S3.3">3.3 Records</a></h4>
+
+Applications execute requests from a Web server using a simple
+protocol.  Details of the protocol depend upon the application's role,
+but roughly speaking the Web server first sends parameters and other
+data to the application, then the application sends result data to the
+Web server, and finally the application sends the Web server an
+indication that the request is complete.<p>
+
+All data that flows over the transport connection is carried in
+<i>FastCGI records</i>.  FastCGI records accomplish two
+things.  First, records multiplex the transport connection between
+several independent FastCGI requests.  This multiplexing supports
+applications that are able to process concurrent requests using
+event-driven or multi-threaded programming techniques.  Second,
+records provide several independent data streams in each direction
+within a single request.  This way, for instance, both <tt>stdout</tt>
+and <tt>stderr</tt> data can pass over a single transport connection
+from the application to the Web server, rather than requiring separate
+connections.<p>
+
+<pre>
+        typedef struct {
+            unsigned char version;
+            unsigned char type;
+            unsigned char requestIdB1;
+            unsigned char requestIdB0;
+            unsigned char contentLengthB1;
+            unsigned char contentLengthB0;
+            unsigned char paddingLength;
+            unsigned char reserved;
+            unsigned char contentData[contentLength];
+            unsigned char paddingData[paddingLength];
+        } FCGI_Record;
+</pre>
+
+A FastCGI record consists of a fixed-length prefix followed by a
+variable number of content and padding bytes.  A record contains seven
+components:<p>
+
+<ul type=square>
+    <li><tt>version</tt>:
+
+        Identifies the FastCGI protocol version.  This specification
+        documents <tt>FCGI_VERSION_1</tt>.<p>
+
+    <li><tt>type</tt>:
+
+        Identifies the FastCGI record type, i.e. the general function
+        that the record performs.  Specific record types and their
+        functions are detailed in later sections.<p>
+
+    <li><tt>requestId</tt>:
+
+        Identifies the <i>FastCGI request</i> to which the record
+        belongs.<p>
+
+    <li><tt>contentLength</tt>:
+
+        The number of bytes in the <tt>contentData</tt> component of the
+        record.<p>
+
+    <li><tt>paddingLength</tt>:
+
+        The number of bytes in the <tt>paddingData</tt> component of the
+        record.<p>
+
+    <li><tt>contentData</tt>:
+
+        Between 0 and 65535 bytes of data, interpreted
+        according to the record type.<p>
+
+    <li><tt>paddingData</tt>:
+
+        Between 0 and 255 bytes of data, which are ignored.<p>
+</ul>
+
+We use a relaxed C <tt>struct</tt> initializer syntax to specify
+constant FastCGI records.  We omit the <tt>version</tt> component,
+ignore padding, and treat
+<tt>requestId</tt> as a number.  Thus <tt>{FCGI_END_REQUEST, 1,
+{FCGI_REQUEST_COMPLETE,0}}</tt> is a record with <tt>type ==
+FCGI_END_REQUEST</tt>, <tt>requestId == 1</tt>, and <tt>contentData ==
+{FCGI_REQUEST_COMPLETE,0}</tt>.<p>
+
+
+<h5>Padding</h5>
+
+The protocol allows senders to pad the records they send, and requires
+receivers to interpret the <tt>paddingLength</tt> and skip the
+<tt>paddingData</tt>.  Padding allows senders to keep data aligned
+for more efficient processing.  Experience with the X window
+system protocols shows the performance benefit of such alignment.<p>
+
+We recommend that records be placed on boundaries that are multiples
+of eight bytes.  The fixed-length portion of a <tt>FCGI_Record</tt>
+is eight bytes.<p>
+
+
+<h5>Managing Request IDs</h5>
+
+The Web server re-uses FastCGI request IDs; the application keeps
+track of the current state of each request ID on a given transport
+connection.  A request ID <tt>R</tt> becomes active when the application
+receives a record <tt>{FCGI_BEGIN_REQUEST, R, ...}</tt> and
+becomes inactive when the application sends a record
+<tt>{FCGI_END_REQUEST, R, ...}</tt> to the Web server.<p>
+
+While a request ID <tt>R</tt> is inactive, the application ignores
+records with <tt>requestId == R</tt>, except for <tt>FCGI_BEGIN_REQUEST</tt>
+records as just described.<p>
+
+The Web server attempts to keep FastCGI request IDs small.  That way
+the application can keep track of request ID states using a short
+array rather than a long array or a hash table.  An application
+also has the option of accepting only one request at a time.
+In this case the application simply checks incoming <tt>requestId</tt>
+values against the current request ID.<p>
+
+
+<h5>Types of Record Types</h5>
+
+There are two useful ways of classifying FastCGI record types.<p>
+
+The first distinction is between <i>management</i> records and
+<i>application</i> records.  A management record contains information
+that is not specific to any Web server request, such as information
+about the protocol capabilities of the application.
+An application record contains information
+about a particular request, identified by the <tt>requestId</tt>
+component.<p>
+
+Management records have a <tt>requestId</tt> value of zero,
+also called the <i>null request ID</i>.  Application
+records have a nonzero <tt>requestId</tt>.<p>
+
+The second distinction is between <i>discrete</i> and <i>stream</i>
+records.  A discrete record contains a meaningful unit of data all by
+itself.  A stream record is part of a <i>stream</i>, i.e. a series of
+zero or more non-empty records (<tt>length != 0</tt>) of the stream
+type, followed by an empty record (<tt>length == 0</tt>) of the stream
+type.  The <tt>contentData</tt> components of a stream's records, when
+concatenated, form a byte sequence; this byte sequence is the value of
+the stream.  Therefore the value of a stream is independent of how
+many records it contains or how its bytes are divided among the
+non-empty records.<p>
+
+These two classifications are independent.  Among the
+record types defined in this version of the FastCGI protocol,
+all management record types are also discrete record types,
+and nearly all application record types are stream record types.
+But three application record types are discrete, and nothing
+prevents defining a management record type that's a stream
+in some later version of the protocol.<p>
+
+
+<h4><a name = "S3.4">3.4 Name-Value Pairs</a></h4>
+
+In many of their roles, FastCGI applications need to read and write
+varying numbers of variable-length values.  So it is useful to adopt a
+standard format for encoding a name-value pair.<p>
+
+FastCGI  transmits a name-value pair as the length of the name,
+followed by the length of the value, followed by the name,
+followed by the value.  Lengths of 127 bytes and less can be encoded
+in one byte, while longer lengths are always encoded in four bytes:<p>
+
+<pre>
+        typedef struct {
+            unsigned char nameLengthB0;  /* nameLengthB0  >> 7 == 0 */
+            unsigned char valueLengthB0; /* valueLengthB0 >> 7 == 0 */
+            unsigned char nameData[nameLength];
+            unsigned char valueData[valueLength];
+        } FCGI_NameValuePair11;
+
+        typedef struct {
+            unsigned char nameLengthB0;  /* nameLengthB0  >> 7 == 0 */
+            unsigned char valueLengthB3; /* valueLengthB3 >> 7 == 1 */
+            unsigned char valueLengthB2;
+            unsigned char valueLengthB1;
+            unsigned char valueLengthB0;
+            unsigned char nameData[nameLength];
+            unsigned char valueData[valueLength
+                    ((B3 & 0x7f) << 24) + (B2 << 16) + (B1 << 8) + B0];
+        } FCGI_NameValuePair14;
+
+        typedef struct {
+            unsigned char nameLengthB3;  /* nameLengthB3  >> 7 == 1 */
+            unsigned char nameLengthB2;
+            unsigned char nameLengthB1;
+            unsigned char nameLengthB0;
+            unsigned char valueLengthB0; /* valueLengthB0 >> 7 == 0 */
+            unsigned char nameData[nameLength
+                    ((B3 & 0x7f) << 24) + (B2 << 16) + (B1 << 8) + B0];
+            unsigned char valueData[valueLength];
+        } FCGI_NameValuePair41;
+
+        typedef struct {
+            unsigned char nameLengthB3;  /* nameLengthB3  >> 7 == 1 */
+            unsigned char nameLengthB2;
+            unsigned char nameLengthB1;
+            unsigned char nameLengthB0;
+            unsigned char valueLengthB3; /* valueLengthB3 >> 7 == 1 */
+            unsigned char valueLengthB2;
+            unsigned char valueLengthB1;
+            unsigned char valueLengthB0;
+            unsigned char nameData[nameLength
+                    ((B3 & 0x7f) << 24) + (B2 << 16) + (B1 << 8) + B0];
+            unsigned char valueData[valueLength
+                    ((B3 & 0x7f) << 24) + (B2 << 16) + (B1 << 8) + B0];
+        } FCGI_NameValuePair44;
+</pre>
+
+The high-order bit of the first byte of a length indicates the length's
+encoding.  A high-order zero implies a one-byte encoding, a one a four-byte
+encoding.<p>
+
+This name-value pair format allows the sender to transmit binary values
+without additional encoding, and enables the receiver to allocate the correct
+amount of storage immediately even for large values.<p>
+
+
+<h4><a name = "S3.5">3.5 Closing Transport Connections</a></h4>
+
+The Web server controls the lifetime of transport connections.
+The Web server can close a connection when no requests are active.
+Or the Web server can delegate close authority to the application
+(see <tt>FCGI_BEGIN_REQUEST</tt>).
+In this case the application closes the connection at the end of
+a specified request.<p>
+
+This flexibility accommodates a variety of application styles.
+Simple applications will process one request at a time and
+accept a new transport connection for each request.  More
+complex applications
+will process concurrent requests, over one or multiple transport
+connections, and will keep transport connections open for long
+periods of time.<p>
+
+A simple application gets a significant performance boost by
+closing the transport connection when it has finished writing its
+response.  The Web server needs to control the connection lifetime
+for long-lived connections.<p>
+
+When an application closes a connection or finds that a connection
+has closed, the application initiates a new connection.<p>
+
+
+
+<h3><a name = "S4">4. Management Record Types</a></h3>
+
+
+<h4><a name = "S4.1">4.1 <tt>FCGI_GET_VALUES, FCGI_GET_VALUES_RESULT</tt></a></h4>
+
+The Web server can query specific variables within the application.
+The server will typically perform a query on application startup
+in order to to automate certain aspects of system configuration.<p>
+
+The application receives a query as a record <tt>{FCGI_GET_VALUES, 0,
+...}</tt>.  The <tt>contentData</tt> portion of a <tt>FCGI_GET_VALUES</tt>
+record contains a sequence of name-value pairs with empty values.<p>
+
+The application responds by sending a record
+<tt>{FCGI_GET_VALUES_RESULT, 0, ...}</tt> with the values supplied.  If the
+application doesn't understand a variable name that was
+included in the query, it omits that name from the
+response.<p>
+
+<tt>FCGI_GET_VALUES</tt> is designed to allow an open-ended
+set of variables.  The initial set provides information to help
+the server perform application and connection management:<p>
+
+<ul type=square>
+    <li><tt>FCGI_MAX_CONNS</tt>:
+        The maximum number of concurrent transport connections this
+        application will accept, e.g.
+        <tt>"1"</tt> or <tt>"10"</tt>.<p>
+
+    <li><tt>FCGI_MAX_REQS</tt>:
+        The maximum number of concurrent requests this application
+        will accept, e.g.
+        <tt>"1"</tt> or <tt>"50"</tt>.<p>
+
+    <li><tt>FCGI_MPXS_CONNS</tt>:
+        <tt>"0"</tt> if this application does not multiplex
+        connections (i.e. handle concurrent requests over each
+        connection), <tt>"1"</tt> otherwise.<p>
+</ul>
+
+An application may receive a <tt>FCGI_GET_VALUES</tt> record at any
+time.  The application's response should not involve the application
+proper but only the FastCGI library.<p>
+
+
+<h4><a name = "S4.2">4.2 <tt>FCGI_UNKNOWN_TYPE</tt></a></h4>
+
+The set of management record types is likely to grow in future versions
+of this protocol.  To provide for this evolution, the protocol
+includes the <tt>FCGI_UNKNOWN_TYPE</tt> management record.
+When an application receives a management record whose type <tt>T</tt>
+it does not understand, the application responds with
+<tt>{FCGI_UNKNOWN_TYPE, 0, {T}}</tt>.<p>
+
+The <tt>contentData</tt> component of a <tt>FCGI_UNKNOWN_TYPE</tt> record
+has the form:
+<pre>
+        typedef struct {
+            unsigned char type;    
+            unsigned char reserved[7];
+        } FCGI_UnknownTypeBody;
+</pre>
+<p>
+
+The <tt>type</tt> component is the type of the unrecognized management
+record.<p>
+
+
+
+<h3><a name = "S5">5. Application Record Types</a></h3>
+
+
+<h4><a name = "S5.1">5.1 <tt>FCGI_BEGIN_REQUEST</tt></a></h4>
+
+The Web server sends a <tt>FCGI_BEGIN_REQUEST</tt> record
+to start a request.<p>
+
+The <tt>contentData</tt> component of a <tt>FCGI_BEGIN_REQUEST</tt> record
+has the form:
+<pre>
+        typedef struct {
+            unsigned char roleB1;
+            unsigned char roleB0;
+            unsigned char flags;
+            unsigned char reserved[5];
+        } FCGI_BeginRequestBody;
+</pre>
+<p>
+
+The <tt>role</tt> component sets the role the Web server expects
+the application to play.  The currently-defined roles are:<p>
+
+<ul type=square>
+    <li><tt>FCGI_RESPONDER</tt>
+    <li><tt>FCGI_AUTHORIZER</tt>
+    <li><tt>FCGI_FILTER</tt>
+</ul>
+
+Roles are described in more detail in
+<a href = "#S6">Section 6</a> below.<p>
+
+The <tt>flags</tt> component contains a bit that
+controls connection shutdown:<p>
+
+<ul type=square>
+    <li><tt>flags & FCGI_KEEP_CONN</tt>:
+        If zero, the application closes the connection after responding to
+        this request.  If not zero, the application does not close
+        the connection after responding to this request; the Web server
+        retains responsibility for the connection.<p>
+</ul>
+
+
+<h4><a name = "S5.2">5.2 Name-Value Pair Stream: <tt>FCGI_PARAMS</tt></a></h4>
+
+<tt>FCGI_PARAMS</tt> is a stream record type used in sending
+name-value pairs from the Web server to the application.
+The name-value pairs are sent down the stream one after the other,
+in no specified order.<p>
+
+
+<h4><a name = "S5.3">5.3 Byte Streams: <tt>FCGI_STDIN</tt>, <tt>FCGI_DATA</tt>, <tt>FCGI_STDOUT</tt>, <tt>FCGI_STDERR</tt></a></h4>
+
+<tt>FCGI_STDIN</tt> is a stream record type used in sending arbitrary
+data from the Web server to the application.  <tt>FCGI_DATA</tt>
+is a second stream record type used to send additional
+data to the application.<p>
+
+<tt>FCGI_STDOUT</tt> and <tt>FCGI_STDERR</tt> are stream record types
+for sending arbitrary data and error data respectively from the
+application to the Web server.<p>
+
+
+<h4><a name = "S5.4">5.4 <tt>FCGI_ABORT_REQUEST</tt></a></h4>
+
+The Web server sends a <tt>FCGI_ABORT_REQUEST</tt> record to
+abort a request.  After receiving <tt>{FCGI_ABORT_REQUEST, R}</tt>,
+the application responds as soon as possible with
+<tt>{FCGI_END_REQUEST, R, {FCGI_REQUEST_COMPLETE, appStatus}}</tt>.
+This is truly a response from the application, not a low-level
+acknowledgement from the FastCGI library.<p>
+
+A Web server aborts a FastCGI request when an HTTP client closes its
+transport connection while the FastCGI request is running on behalf of
+that client.  The situation may seem unlikely; most FastCGI
+requests will have short response times, with the Web server providing
+output buffering if the client is slow.  But the FastCGI application
+may be delayed communicating with another system, or performing a
+server push.<p>
+
+When a Web server is not multiplexing requests over a transport
+connection, the Web server can abort a request by closing the request's
+transport connection.  But with multiplexed requests, closing the
+transport connection has the unfortunate effect of aborting <i>all</i>
+the requests on the connection.<p>
+
+
+<h4><a name = "S5.5">5.5 <tt>FCGI_END_REQUEST</tt></a></h4>
+
+The application sends a <tt>FCGI_END_REQUEST</tt> record
+to terminate a request, either because the application
+has processed the request or because the application has rejected
+the request.<p>
+
+The <tt>contentData</tt> component of a <tt>FCGI_END_REQUEST</tt> record
+has the form:
+<pre>
+        typedef struct {
+            unsigned char appStatusB3;
+            unsigned char appStatusB2;
+            unsigned char appStatusB1;
+            unsigned char appStatusB0;
+            unsigned char protocolStatus;
+            unsigned char reserved[3];
+        } FCGI_EndRequestBody;
+</pre>
+<p>
+
+The <tt>appStatus</tt> component is an application-level status code.
+Each role documents its usage of <tt>appStatus</tt>.<p>
+
+The <tt>protocolStatus</tt> component is a protocol-level status code;
+the possible <tt>protocolStatus</tt> values are:<p>
+
+<ul type=square>
+    <li><tt>FCGI_REQUEST_COMPLETE</tt>:
+        normal end of request.<p>
+
+    <li><tt>FCGI_CANT_MPX_CONN</tt>:
+        rejecting a new request.  This happens when a Web server sends
+        concurrent requests over one connection to an application that
+        is designed to process one request at a time per
+        connection.<p>
+
+    <li><tt>FCGI_OVERLOADED</tt>:
+        rejecting a new request.  This happens when the application
+        runs out of some resource, e.g. database connections.<p>
+
+    <li><tt>FCGI_UNKNOWN_ROLE</tt>:
+        rejecting a new request.  This happens when the Web server
+        has specified a role that is unknown to the application.<p>
+</ul>
+
+
+
+<h3><a name = "S6">6. Roles</a></h3>
+
+
+<h4><a name = "S6.1">6.1 Role Protocols</a></h4>
+
+Role protocols only include records with application record
+types.  They transfer essentially all data using streams.<p>
+
+To make the protocols reliable and to simplify application
+programming, role protocols are designed to use <i>nearly sequential
+marshalling</i>.  In a protocol with strictly sequential marshalling,
+the application receives its first input, then its second, etc. until
+it has received them all.  Similarly, the application sends its first
+output, then its second, etc. until it has sent them all.  Inputs are
+not interleaved with each other, and outputs are not interleaved with
+each other.<p>
+
+The sequential marshalling rule is too restrictive for some
+FastCGI roles, because CGI programs can write to both <tt>stdout</tt>
+and <tt>stderr</tt> without timing restrictions.  So role
+protocols that use both <tt>FCGI_STDOUT</tt> and <tt>FCGI_STDERR</tt>
+allow these two streams to be interleaved.<p>
+
+All role protocols use the <tt>FCGI_STDERR</tt> stream just the way
+<tt>stderr</tt> is used in conventional applications programming: to
+report application-level errors in an intelligible way.  Use of the
+<tt>FCGI_STDERR</tt> stream is always optional.  If an application has
+no errors to report, it sends either no <tt>FCGI_STDERR</tt> records or
+one zero-length <tt>FCGI_STDERR</tt> record.<p>
+
+When a role protocol calls for transmitting a stream other than
+<tt>FCGI_STDERR</tt>, at least one record of the stream type is always
+transmitted, even if the stream is empty.<p>
+
+Again in the interests of reliable protocols and simplified application
+programming, role protocols are designed to be <i>nearly
+request-response</i>.  In a truly request-response protocol, the
+application receives all of its input records before sending its first
+output record.  Request-response protocols don't allow pipelining.<p>
+
+The request-response rule is too restrictive for some FastCGI roles;
+after all, CGI programs aren't restricted to read all of
+<tt>stdin</tt> before starting to write <tt>stdout</tt>.  So some role
+protocols allow that specific possibility.  First the application
+receives all of its inputs except for a final stream input.  As the
+application begins to receive the final stream input, it can
+begin writing its output.<p>
+
+When a role protocol uses <tt>FCGI_PARAMS</tt> to transmit textual
+values, such as the values that CGI programs obtain from environment
+variables, the length of the value does not include the terminating
+null byte, and the value itself does not include a null byte.  An
+application that needs to provide <tt>environ(7)</tt> format
+name-value pairs must insert an equal sign between the name and value
+and append a null byte after the value.<p>
+
+Role protocols do not support the non-parsed header feature
+of CGI.  FastCGI applications set response status using
+the <tt>Status</tt> and <tt>Location</tt> CGI headers.<p>
+
+
+<h4><a name = "S6.2">6.2 Responder</a></h4>
+
+A Responder FastCGI application has the same purpose as a CGI/1.1 program:
+It receives all the information associated with an HTTP request and
+generates an HTTP response.<p>
+
+It suffices to explain how each element of CGI/1.1 is emulated by a
+Responder:<p>
+<ul type=square>
+    <li>
+        The Responder application receives CGI/1.1 environment variables from
+        the Web server over <tt>FCGI_PARAMS</tt>.<p>
+    <li>
+        Next the Responder application receives CGI/1.1
+        <tt>stdin</tt> data from
+        the Web server over <tt>FCGI_STDIN</tt>.  The application receives
+        at most <tt>CONTENT_LENGTH</tt> bytes from this stream before
+        receiving the end-of-stream indication.  (The application
+        receives less than <tt>CONTENT_LENGTH</tt> bytes only if the
+        HTTP client fails to provide them, e.g. because the client
+        crashed.)<p>
+    <li>
+        The Responder application sends CGI/1.1
+        <tt>stdout</tt> data to the Web
+        server over <tt>FCGI_STDOUT</tt>, and CGI/1.1 <tt>stderr</tt> data
+        over <tt>FCGI_STDERR</tt>.  The application sends these
+        concurrently, not one after the other.  The application must
+        wait to finish reading <tt>FCGI_PARAMS</tt> before it begins
+        writing <tt>FCGI_STDOUT</tt> and <tt>FCGI_STDERR</tt>, but
+        it needn't finish reading from <tt>FCGI_STDIN</tt> before it
+        begins writing these two streams.<p>
+    <li>
+        After sending all its <tt>stdout</tt> and <tt>stderr</tt> data,
+        the Responder application sends a <tt>FCGI_END_REQUEST</tt> record.
+        The application sets the <tt>protocolStatus</tt> component to
+        <tt>FCGI_REQUEST_COMPLETE</tt> and the <tt>appStatus</tt> component
+        to the status code that the CGI program would have returned
+        via the <tt>exit</tt> system call.<p>
+</ul>
+
+A Responder performing an update, e.g. implementing a <tt>POST</tt>
+method, should compare the number of bytes received on <tt>FCGI_STDIN</tt>
+with <tt>CONTENT_LENGTH</tt> and abort the update if the two numbers
+are not equal.<p>
+
+
+<h4><a name = "S6.3">6.3 Authorizer</a></h4>
+
+An Authorizer FastCGI application receives all the information
+associated with an HTTP request and generates an
+authorized/unauthorized decision.  In case of an authorized
+decision the Authorizer can also associate name-value pairs
+with the HTTP request; when giving an unauthorized
+decision the Authorizer sends a complete response to the HTTP client.
+<p>
+
+Since CGI/1.1 defines a perfectly good way to represent the information
+associated with an HTTP request, Authorizers use the same
+representation:<p>
+
+<ul type=square>
+    <li>
+        The Authorizer application receives
+        HTTP request information from the Web
+        server on the <tt>FCGI_PARAMS</tt> stream,
+        in the same format as a Responder.  The Web server does not
+        send <tt>CONTENT_LENGTH</tt>,
+        <tt>PATH_INFO</tt>, <tt>PATH_TRANSLATED</tt>, and
+        <tt>SCRIPT_NAME</tt> headers.<p>
+    <li>
+       The Authorizer application sends
+        <tt>stdout</tt> and <tt>stderr</tt> data in
+       the same manner as a Responder.  The CGI/1.1 response
+       status specifies the disposition of the request.  If the
+        application sends status 200 (OK), the Web server allows
+        access.  Depending upon its configuration the Web server
+        may proceed with other access checks, including requests to other
+        Authorizers.<p>
+
+        An Authorizer application's 200 response may include headers
+        whose names are prefixed with <tt>Variable-</tt>.  These
+        headers communicate name-value pairs from the
+        application to the Web server.  For instance, the response header
+<pre>
+        Variable-AUTH_METHOD: database lookup
+</pre>
+        transmits the value <tt>"database lookup"</tt> with name
+        <tt>AUTH-METHOD</tt>.  The server associates such name-value
+        pairs with the HTTP request and includes them in subsequent
+        CGI or FastCGI requests performed in processing the HTTP
+        request.  When the application gives a 200 response, the
+        server ignores response headers whose names aren't prefixed
+        with <tt>Variable-</tt> prefix, and ignores any response
+        content.<p>
+
+       For Authorizer response status values other than "200" (OK), the
+        Web server denies access and sends
+       the response status, headers, and content
+        back to the HTTP client.<p>
+</ul>
+
+
+<h4><a name = "S6.4">6.4 Filter</a></h4>
+
+A Filter FastCGI application receives all the information associated
+with an HTTP request, plus an extra stream of data from a file stored
+on the Web server, and generates a "filtered" version of the data
+stream as an HTTP response.<p>
+
+A Filter is similar in functionality to a Responder that takes a data
+file as a parameter.  The difference is that with a Filter, both the
+data file and the Filter itself can be access controlled using the Web
+server's access control mechanisms, while a Responder that takes the
+name of a data file as a parameter must perform its own access control
+checks on the data file.<p>
+
+The steps taken by a Filter are similar to those of a Responder.
+The server presents the Filter with environment variables first,
+then standard input (normally form <tt>POST</tt> data), finally
+the data file input:<p>
+<ul type=square>
+    <li>
+        Like a Responder, the Filter application receives name-value
+        pairs from the Web server over <tt>FCGI_PARAMS</tt>.
+        Filter applications receive two Filter-specific variables:
+        <tt>FCGI_DATA_LAST_MOD</tt> and <tt>FCGI_DATA_LENGTH</tt>.<p>
+    <li>
+        Next the Filter application receives CGI/1.1 <tt>stdin</tt> data from
+        the Web server over <tt>FCGI_STDIN</tt>.  The application receives
+        at most <tt>CONTENT_LENGTH</tt> bytes from this stream before
+        receiving the end-of-stream indication.  (The application
+        receives less than <tt>CONTENT_LENGTH</tt> bytes only if the
+        HTTP client fails to provide them, e.g. because the client
+        crashed.)<p>
+    <li>
+        Next the Filter application receives the file data from the
+        Web server over <tt>FCGI_DATA</tt>.  This file's last
+        modification time (expressed as an integer number of seconds
+        since the epoch January 1, 1970 UTC) is
+        <tt>FCGI_DATA_LAST_MOD</tt>; the application may consult
+        this variable and respond from a cache without reading
+        the file data.  The application
+        reads at most <tt>FCGI_DATA_LENGTH</tt> bytes from this stream
+        before receiving the end-of-stream indication.<p>
+    <li>
+        The Filter application sends CGI/1.1 <tt>stdout</tt> data to the Web
+        server over <tt>FCGI_STDOUT</tt>, and CGI/1.1 <tt>stderr</tt> data
+        over <tt>FCGI_STDERR</tt>.  The application sends these
+        concurrently, not one after the other.  The application must
+        wait to finish reading <tt>FCGI_STDIN</tt> before it begins
+        writing <tt>FCGI_STDOUT</tt> and <tt>FCGI_STDERR</tt>, but
+        it needn't finish reading from <tt>FCGI_DATA</tt> before it
+        begins writing these two streams.<p>
+    <li>
+        After sending all its <tt>stdout</tt> and <tt>stderr</tt> data,
+        the application sends a <tt>FCGI_END_REQUEST</tt> record.
+        The application sets the <tt>protocolStatus</tt> component to
+        <tt>FCGI_REQUEST_COMPLETE</tt> and the <tt>appStatus</tt> component
+        to the status code that a similar CGI program would have returned
+        via the <tt>exit</tt> system call.<p>
+</ul>
+
+A Filter should compare the number of bytes received on <tt>FCGI_STDIN</tt>
+with <tt>CONTENT_LENGTH</tt> and on <tt>FCGI_DATA</tt>
+with <tt>FCGI_DATA_LENGTH</tt>.  If the numbers don't match
+and the Filter is a query, the Filter
+response should provide an indication that data is missing.
+If the numbers don't match and the Filter is an update, the Filter
+should abort the update.<p>
+
+
+
+<h3><a name = "S7">7. Errors</a></h3>
+
+A FastCGI application exits with zero status to indicate that it
+terminated on purpose, e.g. in order to perform a crude form of
+garbage collection.  A FastCGI application that exits with nonzero
+status is assumed to have crashed.  How a Web server or other
+application manager responds to
+applications that exit with zero or nonzero status is outside the
+scope of this specification.<p>
+
+A Web server can request that a FastCGI application exit by sending it
+<tt>SIGTERM</tt>.  If the application ignores <tt>SIGTERM</tt> the Web
+server can resort to <tt>SIGKILL</tt>.<p>
+
+FastCGI applications report application-level errors with the
+<tt>FCGI_STDERR</tt> stream and the <tt>appStatus</tt> component of
+the <tt>FCGI_END_REQUEST</tt> record.  In many cases an error will be
+reported directly to the user via the <tt>FCGI_STDOUT</tt> stream.<p>
+
+On Unix, applications report lower-level errors, including
+FastCGI protocol errors and syntax errors in FastCGI environment
+variables, to <tt>syslog</tt>.  Depending upon the severity
+of the error, the application may either continue or
+exit with nonzero status.<p>
+
+
+
+<h3><a name = "S8">8. Types and Constants</a></h3>
+<pre>
+/*
+ * Listening socket file number
+ */
+#define FCGI_LISTENSOCK_FILENO 0
+
+typedef struct {
+    unsigned char version;
+    unsigned char type;
+    unsigned char requestIdB1;
+    unsigned char requestIdB0;
+    unsigned char contentLengthB1;
+    unsigned char contentLengthB0;
+    unsigned char paddingLength;
+    unsigned char reserved;
+} FCGI_Header;
+
+/*
+ * Number of bytes in a FCGI_Header.  Future versions of the protocol
+ * will not reduce this number.
+ */
+#define FCGI_HEADER_LEN  8
+
+/*
+ * Value for version component of FCGI_Header
+ */
+#define FCGI_VERSION_1           1
+
+/*
+ * Values for type component of FCGI_Header
+ */
+#define FCGI_BEGIN_REQUEST       1
+#define FCGI_ABORT_REQUEST       2
+#define FCGI_END_REQUEST         3
+#define FCGI_PARAMS              4
+#define FCGI_STDIN               5
+#define FCGI_STDOUT              6
+#define FCGI_STDERR              7
+#define FCGI_DATA                8
+#define FCGI_GET_VALUES          9
+#define FCGI_GET_VALUES_RESULT  10
+#define FCGI_UNKNOWN_TYPE       11
+#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)
+
+/*
+ * Value for requestId component of FCGI_Header
+ */
+#define FCGI_NULL_REQUEST_ID     0
+
+typedef struct {
+    unsigned char roleB1;
+    unsigned char roleB0;
+    unsigned char flags;
+    unsigned char reserved[5];
+} FCGI_BeginRequestBody;
+
+typedef struct {
+    FCGI_Header header;
+    FCGI_BeginRequestBody body;
+} FCGI_BeginRequestRecord;
+
+/*
+ * Mask for flags component of FCGI_BeginRequestBody
+ */
+#define FCGI_KEEP_CONN  1
+
+/*
+ * Values for role component of FCGI_BeginRequestBody
+ */
+#define FCGI_RESPONDER  1
+#define FCGI_AUTHORIZER 2
+#define FCGI_FILTER     3
+
+typedef struct {
+    unsigned char appStatusB3;
+    unsigned char appStatusB2;
+    unsigned char appStatusB1;
+    unsigned char appStatusB0;
+    unsigned char protocolStatus;
+    unsigned char reserved[3];
+} FCGI_EndRequestBody;
+
+typedef struct {
+    FCGI_Header header;
+    FCGI_EndRequestBody body;
+} FCGI_EndRequestRecord;
+
+/*
+ * Values for protocolStatus component of FCGI_EndRequestBody
+ */
+#define FCGI_REQUEST_COMPLETE 0
+#define FCGI_CANT_MPX_CONN    1
+#define FCGI_OVERLOADED       2
+#define FCGI_UNKNOWN_ROLE     3
+
+/*
+ * Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT records
+ */
+#define FCGI_MAX_CONNS  "FCGI_MAX_CONNS"
+#define FCGI_MAX_REQS   "FCGI_MAX_REQS"
+#define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS"
+
+typedef struct {
+    unsigned char type;    
+    unsigned char reserved[7];
+} FCGI_UnknownTypeBody;
+
+typedef struct {
+    FCGI_Header header;
+    FCGI_UnknownTypeBody body;
+} FCGI_UnknownTypeRecord;
+</pre>
+<p>
+
+
+
+<h3><a name = "S9">9. References</a></h3>
+
+
+National Center for Supercomputer Applications,
+<a href = "http://hoohoo.ncsa.uiuc.edu/cgi/">The Common Gateway Interface</a>,
+version CGI/1.1.<p>
+
+D.R.T. Robinson,
+<a href = "http://ds.internic.net/internet-drafts/draft-robinson-www-interface-01.txt">The WWW Common Gateway Interface Version 1.1</a>, Internet-Draft, 15 February 1996.<p>
+
+
+
+<h3><a name = "SA">A. Table: Properties of the record types</a></h3>
+
+The following chart lists all of the record types and
+indicates these properties of each:<p>
+
+<ul type=square>
+    <li><tt>WS->App</tt>:
+        records of this type can only be sent by the Web server to the
+        application.  Records of other types can only be sent by the
+        application to the Web server.<p>
+    <li><tt>management</tt>:
+        records of this type contain information that is not specific
+        to a Web server request, and use the null request ID.  Records
+        of other types contain request-specific information, and cannot
+        use the null request ID.<p>
+    <li><tt>stream</tt>:
+        records of this type form a stream, terminated by a
+        record with empty <tt>contentData</tt>.  Records of other types
+        are discrete; each carries a meaningful unit of data.<p>
+</ul>
+<pre>
+
+                               WS->App   management  stream
+
+        FCGI_GET_VALUES           x          x
+        FCGI_GET_VALUES_RESULT               x
+        FCGI_UNKNOWN_TYPE                    x
+
+        FCGI_BEGIN_REQUEST        x
+        FCGI_ABORT_REQUEST        x
+        FCGI_END_REQUEST
+        FCGI_PARAMS               x                    x
+        FCGI_STDIN                x                    x
+        FCGI_DATA                 x                    x
+        FCGI_STDOUT                                    x 
+        FCGI_STDERR                                    x     
+
+
+</pre>
+<p>
+
+
+
+<h3><a name = "SB">B. Typical Protocol Message Flow</a></h3>
+
+Additional notational conventions for the examples:
+
+<ul>
+    <li>The <tt>contentData</tt> of stream records (<tt>FCGI_PARAMS</tt>,
+        <tt>FCGI_STDIN</tt>, <tt>FCGI_STDOUT</tt>, and <tt>FCGI_STDERR</tt>)
+        is represented as a character string.  A string ending in
+        <tt>" ... "</tt> is too long to display, so only a prefix is shown.
+
+    <li>Messages sent to the Web server are indented
+        with respect to messages received from the Web server.
+
+    <li>Messages are shown in the time sequence experienced
+        by the application.
+</ul>
+
+1. A simple request with no data on <tt>stdin</tt>, and a successful response:
+<pre>
+{FCGI_BEGIN_REQUEST,   1, {FCGI_RESPONDER, 0}}
+{FCGI_PARAMS,          1, "\013\002SERVER_PORT80\013\016SERVER_ADDR199.170.183.42 ... "}
+{FCGI_PARAMS,          1, ""}
+{FCGI_STDIN,           1, ""}
+
+    {FCGI_STDOUT,      1, "Content-type: text/html\r\n\r\n&lt;html&gt;\n&lt;head&gt; ... "}
+    {FCGI_STDOUT,      1, ""}
+    {FCGI_END_REQUEST, 1, {0, FCGI_REQUEST_COMPLETE}}
+</pre><p>
+
+2. Similar to example 1, but this time with data on <tt>stdin</tt>.
+The Web server chooses to send the
+parameters using more <tt>FCGI_PARAMS</tt> records than before:
+<pre>
+{FCGI_BEGIN_REQUEST,   1, {FCGI_RESPONDER, 0}}
+{FCGI_PARAMS,          1, "\013\002SERVER_PORT80\013\016SER"}
+{FCGI_PARAMS,          1, "VER_ADDR199.170.183.42 ... "}
+{FCGI_PARAMS,          1, ""}
+{FCGI_STDIN,           1, "quantity=100&amp;item=3047936"}
+{FCGI_STDIN,           1, ""}
+
+    {FCGI_STDOUT,      1, "Content-type: text/html\r\n\r\n&lt;html&gt;\n&lt;head&gt; ... "}
+    {FCGI_STDOUT,      1, ""}
+    {FCGI_END_REQUEST, 1, {0, FCGI_REQUEST_COMPLETE}}
+</pre><p>
+
+3. Similar to example 1, but this time the application detects an error.
+The application logs a message to
+<tt>stderr</tt>, returns a page to the client, and returns
+non-zero exit status to the Web server.  The application
+chooses to send the page using more <tt>FCGI_STDOUT</tt> records:
+<pre>
+{FCGI_BEGIN_REQUEST,   1, {FCGI_RESPONDER, 0}}
+{FCGI_PARAMS,          1, "\013\002SERVER_PORT80\013\016SERVER_ADDR199.170.183.42 ... "}
+{FCGI_PARAMS,          1, ""}
+{FCGI_STDIN,           1, ""}
+
+    {FCGI_STDOUT,      1, "Content-type: text/html\r\n\r\n&lt;ht"}
+    {FCGI_STDERR,      1, "config error: missing SI_UID\n"}
+    {FCGI_STDOUT,      1, "ml&gt;\n&lt;head&gt; ... "}
+    {FCGI_STDOUT,      1, ""}
+    {FCGI_STDERR,      1, ""}
+    {FCGI_END_REQUEST, 1, {938, FCGI_REQUEST_COMPLETE}}
+</pre><p>
+
+4. Two instances of example 1, multiplexed onto a single connection.
+The first request is more difficult than the second, so the
+application finishes the requests out of order:
+<pre>
+{FCGI_BEGIN_REQUEST,   1, {FCGI_RESPONDER, FCGI_KEEP_CONN}}
+{FCGI_PARAMS,          1, "\013\002SERVER_PORT80\013\016SERVER_ADDR199.170.183.42 ... "}
+{FCGI_PARAMS,          1, ""}
+{FCGI_BEGIN_REQUEST,   2, {FCGI_RESPONDER, FCGI_KEEP_CONN}}
+{FCGI_PARAMS,          2, "\013\002SERVER_PORT80\013\016SERVER_ADDR199.170.183.42 ... "}
+{FCGI_STDIN,           1, ""}
+
+    {FCGI_STDOUT,      1, "Content-type: text/html\r\n\r\n"}
+
+{FCGI_PARAMS,          2, ""}
+{FCGI_STDIN,           2, ""}
+
+    {FCGI_STDOUT,      2, "Content-type: text/html\r\n\r\n&lt;html&gt;\n&lt;head&gt; ... "}
+    {FCGI_STDOUT,      2, ""}
+    {FCGI_END_REQUEST, 2, {0, FCGI_REQUEST_COMPLETE}}
+    {FCGI_STDOUT,      1, "&lt;html&gt;\n&lt;head&gt; ... "}
+    {FCGI_STDOUT,      1, ""}
+    {FCGI_END_REQUEST, 1, {0, FCGI_REQUEST_COMPLETE}}
+</pre><p>
+
+<hr>
+
+<address>
+&#169 1995, 1996 Open Market, Inc. / mbrown@openmarket.com
+</address>
+
+</body>
+</html>
diff --git a/doc/fcgi-tcl.gut b/doc/fcgi-tcl.gut
new file mode 100644 (file)
index 0000000..8176c6e
--- /dev/null
@@ -0,0 +1,307 @@
+Integrating FastCGI with Tcl
+/fastcgi/words
+fcgi-hd.gif
+[FastCGI]
+<center>Integrating FastCGI with Tcl</center>
+
+<!--Copyright (c) 1996 Open Market, Inc.                                    -->
+<!--See the file "LICENSE.TERMS" for information on usage and redistribution-->
+<!--of this file, and for a DISCLAIMER OF ALL WARRANTIES.                   -->
+
+<P ALIGN=CENTER>
+Michael S. Shanzer
+<BR>
+Open Market, Inc.
+<BR>
+<EM>19 January 1995</EM>
+</P>
+
+<h5 align=center>
+Copyright &copy; 1996 Open Market, Inc.  245 First Street, Cambridge,
+  MA 02142 U.S.A.<br>
+Tel: 617-621-9500 Fax: 617-621-1703 URL:
+  <a href="http://www.openmarket.com/">http://www.openmarket.com/</a><br>
+$Id: fcgi-tcl.gut,v 1.1 1997/09/16 15:36:26 stanleyg Exp $ <br>
+</h5>
+<hr>
+
+
+<h3><a NAME = "S1">1. Introduction</a></h3>
+
+
+Tcl (tool command language) is an embeddable scripting language
+that's often used for CGI programming.  Tcl is freely available
+as a source kit.<p>
+
+We've built a Tcl interpreter that runs as a FastCGI application.  Our
+purpose in doing so was twofold:
+
+<ul>
+    <li><i>Create a useful artifact.</i>
+        Open Market has written many CGI applications using Tcl.
+        Now we'd like to turn them into FastCGI applications.<p>
+    <li><i>Demonstrate how easy it is to integrate FastCGI with an
+        existing program.</i>
+        The Tcl interpreter is a substantial program, so integrating
+        FastCGI with the Tcl interpreter is a good test of the
+        <tt>fcgi_stdio</tt> compatability library.
+</ul>
+
+We've succeeded on both counts.  We now have a platform for
+migrating our Tcl-based CGI applications to FastCGI.  And
+the integration required a very small effort.  The only source
+code change to the Tcl interpreter was the routine addition of a
+handful of new commands: <tt>FCGI_Accept</tt>, <tt>FCGI_Finish</tt>,
+<tt>FCGI_SetExitStatus</tt>, and <tt>FCGI_StartFilterData</tt>.<p>
+
+The FastCGI-integrated Tcl interpreter works as usual when run
+from a shell or as a CGI program.  You don't need two Tcls,
+one for FastCGI and one for other uses.<p>
+
+The remainder of this document gives a recipe you can follow to
+build FastCGI into Tcl, explains what's happening in the recipe,
+and illustrates the use of FastCGI Tcl with
+an example program.<p>
+
+<h3><a NAME = "S2">2. Recipe</a></h3>
+
+Here are the assumptions embedded in the following recipe:
+<ul>
+    <li>You are building Tcl 7.4p3, the current stable Tcl release
+    as this is written.
+    You unpack the Tcl kit into a directory <tt>tcl7.4</tt>
+    that's a sibling of the FastCGI kit directory
+    <tt>fcgi-devel-kit</tt>.<p>
+
+    <li>You have gcc version 2.7
+    installed on your system, and use it in the build.
+    gcc is convenient because it supports the <tt>-include</tt>
+    command-line option
+    that instructs the C preprocessor to include a specific file before
+    processing any other include files.  This allows you to include
+    <tt>fcgi_stdio.h</tt> without modifying Tcl source files.  (The
+    reason for specifying gcc version 2.7 is that I have
+    experienced bad behavior with an earlier version and the <tt>-include</tt>
+    flag -- the C preprocessor died with SIGABRT.)<p>
+
+    <li>You have GNU autoconf
+    installed on your system.  If you don't have GNU autoconf,
+    you will have to make certain edits by hand and
+    repeat these edits for each build platform.<p>
+</ul>
+
+If those are valid assumptions, follow these steps:
+<ol>
+    <li><i>Build the FastCGI Developer's Kit.</i>
+    Tcl needs to link against <tt>libfcgi.a</tt>, so
+    <a href="http://www.fastcgi.com/kit/doc/fcgi-devel-kit.htm#S2">build
+    the FastCGI Developer's Kit</a>
+    in order to create this library for your platform.<p>
+
+    <li><i>Pull the Tcl 7.4p3 kit.</i>
+    You'll need the files
+    <a href="ftp://ftp.smli.com/pub/tcl/tcl7.4.tar.Z">tcl7.4.tar.Z</a>,
+    <a href="ftp://ftp.smli.com/pub/tcl/tcl7.4p1.patch.gz">tcl7.4p1.patch.gz</a>,
+    <a href="ftp://ftp.smli.com/pub/tcl/tcl7.4p2.patch.gz">tcl7.4p2.patch.gz</a>,
+    and
+    <a href="ftp://ftp.smli.com/pub/tcl/tcl7.4p3.patch.gz">tcl7.4p3.patch.gz</a>.
+    (Some older Netscape browsers can't perform these
+    retrievals because of a protocol conflict between Netscape
+    and Sun's firewall.)<p>
+
+    Unpack the tar file in the parent directory of the
+    FastCGI kit directory you used in the previous step,
+    so that the directories <tt>tcl7.4</tt> and <tt>fcgi-devel-kit</tt>
+    are siblings.  After unpacking the tar file, follow the directions
+    in the <tt>README</tt> to apply the patches.<p>
+
+    The <a href="http://www.sunlabs.com:80/research/tcl/">Sun Labs Tcl/Tk
+    Project Page</a> contains a wealth of information on Tcl, including
+    up to date information on the latest kits.<p>
+
+    <li><i>Copy the files <tt>tclFCGI.c</tt>, <tt>tclAppInit.c</tt>,
+        <tt>Makefile.in</tt>, and <tt>configure.in</tt> from the FastCGI kit.</i>
+    <pre>
+    > cd tcl7.4
+    > mv tclAppInit.c tclAppInit.c.orig
+    > mv Makefile.in.orig Makefile.in.orig.orig
+    > mv Makefile.in Makefile.in.orig
+    > mv configure.in configure.in.orig
+    > cp ../fcgi-devel-kit/tcl/tcl7.4/* .
+    > cp ../fcgi-devel-kit/tcl/common/* .</pre>
+
+    <li><i>Create a new <tt>configure</tt> script.</i>
+    <pre>
+    > autoconf</pre>
+
+    <li><i>Configure and build.</i>
+    <pre>
+    > ./configure
+    > make</pre>
+    The <tt>make</tt> creates the Tcl interpreter <tt>tclsh</tt>
+    and library archive <tt>libtcl.a</tt> (for embedding Tcl in
+    your own C applications).  The Tcl <tt>README</tt> file
+    explains how you can experiment with <tt>tclsh</tt>
+    without installing it in a standard place.<p>
+</ol>
+
+<h3><a NAME = "S3">3. Recipe Explained</a></h3>
+
+The recipe alone is fine if you are using Tcl 7.4p3, you have gcc
+version 2.7, and you have GNU autoconf.  In case one or more of these
+assumptions doesn't hold for you, and to illuminate how little work was
+involved in integrating FastCGI, here's an explanation of how
+and why you would modify the files <tt>tclAppInit.c</tt>,
+<tt>Makefile.in</tt>, and <tt>configure.in</tt> from the Tcl kit.
+
+<ul>
+    <li><tt>tclAppInit.c</tt>:<p>
+    <ul>
+        <li>Add the following three lines of code
+        to the function <tt>Tcl_AppInit</tt> after the call
+        to <tt>Tcl_Init</tt> and after the comment about calling init
+        procedures:
+        <pre>
+    if (FCGI_Init(interp) == TCL_ERROR) {
+        return TCL_ERROR;
+    }</pre>
+        This registers four Tcl commands (<tt>FCGI_Accept</tt>,
+        <tt>FCGI_Finish</tt>, <tt>FCGI_SetExitStatus</tt>, and
+        <tt>FCGI_StartFilterData</tt>), implemented in
+        <tt>tclFCGI.c</tt>, with the Tcl interpreter.<p>
+    </ul>
+
+    <li><tt>Makefile.in</tt>:<p>
+    <ul>
+        <li>Add <tt>tclFCGI.o</tt> to the <tt>GENERIC_OBJS</tt> variable, and
+            add <tt>tclFCGI.c</tt> to the <tt>SRCS</tt> variable.<p>
+
+            This builds the FastCGI Tcl commands and
+            links them into the Tcl interpreter.<p>
+
+        <li>Add <tt>-I../fcgi-devel-kit/include
+                    -include ../fcgi-devel-kit/include/fcgi_stdio.h</tt>
+            to the <tt>CFLAGS</tt> variable.<p>
+
+            This includes <tt>fcgi_stdio.h</tt>
+            when compiling C code for the Tcl interpreter, overriding
+            the normal <tt>stdio</tt> types, variables, and functions.<p>
+
+        <li>Add <tt>../fcgi-devel-kit/libfcgi/libfcgi.a</tt> before the
+            <tt>@LIBS@</tt> part of the <tt>LIBS</tt> variable.<p>
+
+            This links the implementation of <tt>fcgi_stdio.h</tt>
+            into the Tcl interpreter, for use by the <tt>FCGI_accept</tt>
+            command and any code that uses <tt>stdio</tt> variables
+            or calls <tt>stdio</tt> functions.<p>
+    </ul><p>
+
+    The last two edits will vary if you use a compiler other than gcc or
+    install the <tt>tcl7.4</tt> directory
+    somewhere else in relation to the <tt>fcgi-devel-kit</tt> directory.<p>
+
+    <li><tt>configure.in</tt>:<p>
+    <ul>
+        <li>
+        Replace the lines
+        <pre>
+AC_C_CROSS
+CC=${CC-cc}</pre>
+        with the lines
+        <pre>
+AC_PROG_CC
+AC_C_CROSS</pre>
+        This selects gcc in preference to other C compilers.<p>
+         
+        <li>
+        Add the following lines just after the
+        <tt>AC_SUBST(CC)</tt> line:
+        <pre>
+AC_CHECK_LIB(socket, main, [LIBS="$LIBS -lsocket"])
+AC_CHECK_LIB(nsl, main, [LIBS="$LIBS -lnsl"])
+AC_SUBST(LIBS)</pre>
+        This ensures that the socket libraries used by FastCGI
+        are linked into the Tcl interpreter.<p>
+    </ul>
+    If GNU autoconf is not available to you, you'll leave
+    <tt>configure.in</tt> alone and perform the following steps:<p>
+    <ul>
+        <li>
+        Execute
+        <pre>
+    > SETENV CC gcc</pre>
+        before running <tt>configure</tt>.<p>
+
+        <li>
+        If you are running on a SVR4-derived Unix platform,
+        edit <tt>Makefile</tt> to add
+        <tt>-lsocket -lnsl</tt> to the <tt>LIBS</tt> value
+        after running <tt>configure</tt>.<p>
+    </ul>
+    If you ever re-run <tt>configure</tt>, you'll need to repeat
+    these steps.<p>
+
+</ul>
+
+
+<h3><a NAME = "S4">4. Writing FastCGI applications in Tcl</a></h3>
+
+The Tcl program <tt>tcl/tiny-tcl-fcgi</tt> performs the same
+function as the C program <tt>examples/tiny-fcgi.c</tt>
+that's used as an example in the
+<a href="fcgi-devel-kit.html#S3.1.1">FastCGI Developer's Kit
+document</a>.  Here's what the Tcl version looks like:<p>
+
+<pre>
+#!./tclsh
+set count 0 
+while {[FCGI_Accept] &gt;= 0 } {
+    incr count
+    puts -nonewline "Content-type: text/html\r\n\r\n"
+    puts "&lt;title&gt;FastCGI Hello! (Tcl)&lt;/title&gt;"
+    puts "&lt;h1&gt;FastCGI Hello! (Tcl)&lt;/h1&gt;"
+    puts "Request number $count running on host &lt;i&gt;$env(SERVER_NAME)&lt;/i&gt;"
+}
+</pre>
+
+If you've built Tcl according to the recipe and you have a Web server
+set up to run FastCGI applications, load the FastCGI Developer's Kit
+Index Page in that server and run this Tcl application now.<p>
+
+The script invokes Tcl indirectly via the symbolic link
+<tt>examples/tclsh</tt>.  It does this because HP-UX has a limit of 32
+characters for the first line of a command-interpreter file such as
+<tt>examples/tiny-tcl-fcgi</tt>.  If you run on HP-UX you won't want
+to sprinkle symbolic links to <tt>tclsh</tt> everywhere, so you should install
+<tt>tclsh</tt> with a shorter pathname than
+<tt>/usr/local/tcl7.4-fcgi/bin/tclsh7.4</tt>.<p>
+
+The Tcl command <tt>FCGI_Accept</tt> treats the initial
+environment differently than the C function <tt>FCGI_Accept</tt>.  The
+first call to the
+C function <tt>FCGI_Accept</tt> replaces the initial environment with
+the environment of the first request.  The first call to the Tcl command
+<tt>FCGI_Accept</tt> adds the variable bindings of the first request
+to the bindings present in the initial environment.  So when the first
+call to <tt>FCGI_Accept</tt> returns, bindings from the initial
+environment are still there (unless, due to naming conflicts, some of
+them have been overwritten by the first request).  The next call to
+<tt>FCGI_Accept</tt> removes the bindings made on the previous call
+before adding a new set for the request just accepted, again preserving
+the initial environment.<p>
+
+The FastCGI-integrated <tt>tclsh</tt> also includes
+commands <tt>FCGI_Finish</tt>, <tt>FCGI_SetExitStatus</tt>,
+and <tt>FCGI_StartFilterData</tt> that correspond to
+C functions in <tt>fcgi_stdio.h</tt>; see the manpages for
+full information.<p>
+
+Converting a Tcl CGI application to FastCGI is not fundamentally
+different from converting a C CGI application.  You separate
+the portion of the application that performs one-time
+initialization from the portion that performs per-request
+processing.  You put the per-request processing into a loop
+controlled by <tt>FCGI_Accept</tt>.<p>
+
+<HR>
+<ADDRESS><A HREF="mailto:shanzer@openmarket.com">Mike Shanzer // shanzer@openmarket.com</A></ADDRESS>
diff --git a/doc/fcgi-tcl.htm b/doc/fcgi-tcl.htm
new file mode 100644 (file)
index 0000000..122013b
--- /dev/null
@@ -0,0 +1,317 @@
+<html>
+<head><title>Integrating FastCGI with Tcl</title>
+</head>
+
+<body bgcolor="#FFFFFF" text="#000000" link="#cc0000" alink="#000011" 
+vlink="#555555">
+
+<center>
+<a href="/fastcgi/words">
+    <img border=0 src="../images/fcgi-hd.gif" alt="[[FastCGI]]"></a>
+</center>
+<br clear=all>
+<h3><center>Integrating FastCGI with Tcl</center></h3>
+
+<!--Copyright (c) 1996 Open Market, Inc.                                    -->
+<!--See the file "LICENSE.TERMS" for information on usage and redistribution-->
+<!--of this file, and for a DISCLAIMER OF ALL WARRANTIES.                   -->
+
+<P ALIGN=CENTER>
+Michael S. Shanzer
+<BR>
+Open Market, Inc.
+<BR>
+<EM>19 January 1995</EM>
+</P>
+
+<h5 align=center>
+Copyright &copy; 1996 Open Market, Inc.  245 First Street, Cambridge,
+  MA 02142 U.S.A.<br>
+Tel: 617-621-9500 Fax: 617-621-1703 URL:
+  <a href="http://www.openmarket.com/">http://www.openmarket.com/</a><br>
+$Id: fcgi-tcl.htm,v 1.1 1997/09/16 15:36:26 stanleyg Exp $ <br>
+</h5>
+<hr>
+
+
+<h3><a NAME = "S1">1. Introduction</a></h3>
+
+
+Tcl (tool command language) is an embeddable scripting language
+that's often used for CGI programming.  Tcl is freely available
+as a source kit.<p>
+
+We've built a Tcl interpreter that runs as a FastCGI application.  Our
+purpose in doing so was twofold:
+
+<ul>
+    <li><i>Create a useful artifact.</i>
+        Open Market has written many CGI applications using Tcl.
+        Now we'd like to turn them into FastCGI applications.<p>
+    <li><i>Demonstrate how easy it is to integrate FastCGI with an
+        existing program.</i>
+        The Tcl interpreter is a substantial program, so integrating
+        FastCGI with the Tcl interpreter is a good test of the
+        <tt>fcgi_stdio</tt> compatability library.
+</ul>
+
+We've succeeded on both counts.  We now have a platform for
+migrating our Tcl-based CGI applications to FastCGI.  And
+the integration required a very small effort.  The only source
+code change to the Tcl interpreter was the routine addition of a
+handful of new commands: <tt>FCGI_Accept</tt>, <tt>FCGI_Finish</tt>,
+<tt>FCGI_SetExitStatus</tt>, and <tt>FCGI_StartFilterData</tt>.<p>
+
+The FastCGI-integrated Tcl interpreter works as usual when run
+from a shell or as a CGI program.  You don't need two Tcls,
+one for FastCGI and one for other uses.<p>
+
+The remainder of this document gives a recipe you can follow to
+build FastCGI into Tcl, explains what's happening in the recipe,
+and illustrates the use of FastCGI Tcl with
+an example program.<p>
+
+<h3><a NAME = "S2">2. Recipe</a></h3>
+
+Here are the assumptions embedded in the following recipe:
+<ul>
+    <li>You are building Tcl 7.4p3, the current stable Tcl release
+    as this is written.
+    You unpack the Tcl kit into a directory <tt>tcl7.4</tt>
+    that's a sibling of the FastCGI kit directory
+    <tt>fcgi-devel-kit</tt>.<p>
+
+    <li>You have gcc version 2.7
+    installed on your system, and use it in the build.
+    gcc is convenient because it supports the <tt>-include</tt>
+    command-line option
+    that instructs the C preprocessor to include a specific file before
+    processing any other include files.  This allows you to include
+    <tt>fcgi_stdio.h</tt> without modifying Tcl source files.  (The
+    reason for specifying gcc version 2.7 is that I have
+    experienced bad behavior with an earlier version and the <tt>-include</tt>
+    flag -- the C preprocessor died with SIGABRT.)<p>
+
+    <li>You have GNU autoconf
+    installed on your system.  If you don't have GNU autoconf,
+    you will have to make certain edits by hand and
+    repeat these edits for each build platform.<p>
+</ul>
+
+If those are valid assumptions, follow these steps:
+<ol>
+    <li><i>Build the FastCGI Developer's Kit.</i>
+    Tcl needs to link against <tt>libfcgi.a</tt>, so
+    <a href="http://www.fastcgi.com/kit/doc/fcgi-devel-kit.htm#S2">build
+    the FastCGI Developer's Kit</a>
+    in order to create this library for your platform.<p>
+
+    <li><i>Pull the Tcl 7.4p3 kit.</i>
+    You'll need the files
+    <a href="ftp://ftp.smli.com/pub/tcl/tcl7.4.tar.Z">tcl7.4.tar.Z</a>,
+    <a href="ftp://ftp.smli.com/pub/tcl/tcl7.4p1.patch.gz">tcl7.4p1.patch.gz</a>,
+    <a href="ftp://ftp.smli.com/pub/tcl/tcl7.4p2.patch.gz">tcl7.4p2.patch.gz</a>,
+    and
+    <a href="ftp://ftp.smli.com/pub/tcl/tcl7.4p3.patch.gz">tcl7.4p3.patch.gz</a>.
+    (Some older Netscape browsers can't perform these
+    retrievals because of a protocol conflict between Netscape
+    and Sun's firewall.)<p>
+
+    Unpack the tar file in the parent directory of the
+    FastCGI kit directory you used in the previous step,
+    so that the directories <tt>tcl7.4</tt> and <tt>fcgi-devel-kit</tt>
+    are siblings.  After unpacking the tar file, follow the directions
+    in the <tt>README</tt> to apply the patches.<p>
+
+    The <a href="http://www.sunlabs.com:80/research/tcl/">Sun Labs Tcl/Tk
+    Project Page</a> contains a wealth of information on Tcl, including
+    up to date information on the latest kits.<p>
+
+    <li><i>Copy the files <tt>tclFCGI.c</tt>, <tt>tclAppInit.c</tt>,
+        <tt>Makefile.in</tt>, and <tt>configure.in</tt> from the FastCGI kit.</i>
+    <pre>
+    > cd tcl7.4
+    > mv tclAppInit.c tclAppInit.c.orig
+    > mv Makefile.in.orig Makefile.in.orig.orig
+    > mv Makefile.in Makefile.in.orig
+    > mv configure.in configure.in.orig
+    > cp ../fcgi-devel-kit/tcl/tcl7.4/* .
+    > cp ../fcgi-devel-kit/tcl/common/* .</pre>
+
+    <li><i>Create a new <tt>configure</tt> script.</i>
+    <pre>
+    > autoconf</pre>
+
+    <li><i>Configure and build.</i>
+    <pre>
+    > ./configure
+    > make</pre>
+    The <tt>make</tt> creates the Tcl interpreter <tt>tclsh</tt>
+    and library archive <tt>libtcl.a</tt> (for embedding Tcl in
+    your own C applications).  The Tcl <tt>README</tt> file
+    explains how you can experiment with <tt>tclsh</tt>
+    without installing it in a standard place.<p>
+</ol>
+
+<h3><a NAME = "S3">3. Recipe Explained</a></h3>
+
+The recipe alone is fine if you are using Tcl 7.4p3, you have gcc
+version 2.7, and you have GNU autoconf.  In case one or more of these
+assumptions doesn't hold for you, and to illuminate how little work was
+involved in integrating FastCGI, here's an explanation of how
+and why you would modify the files <tt>tclAppInit.c</tt>,
+<tt>Makefile.in</tt>, and <tt>configure.in</tt> from the Tcl kit.
+
+<ul>
+    <li><tt>tclAppInit.c</tt>:<p>
+    <ul>
+        <li>Add the following three lines of code
+        to the function <tt>Tcl_AppInit</tt> after the call
+        to <tt>Tcl_Init</tt> and after the comment about calling init
+        procedures:
+        <pre>
+    if (FCGI_Init(interp) == TCL_ERROR) {
+        return TCL_ERROR;
+    }</pre>
+        This registers four Tcl commands (<tt>FCGI_Accept</tt>,
+        <tt>FCGI_Finish</tt>, <tt>FCGI_SetExitStatus</tt>, and
+        <tt>FCGI_StartFilterData</tt>), implemented in
+        <tt>tclFCGI.c</tt>, with the Tcl interpreter.<p>
+    </ul>
+
+    <li><tt>Makefile.in</tt>:<p>
+    <ul>
+        <li>Add <tt>tclFCGI.o</tt> to the <tt>GENERIC_OBJS</tt> variable, and
+            add <tt>tclFCGI.c</tt> to the <tt>SRCS</tt> variable.<p>
+
+            This builds the FastCGI Tcl commands and
+            links them into the Tcl interpreter.<p>
+
+        <li>Add <tt>-I../fcgi-devel-kit/include
+                    -include ../fcgi-devel-kit/include/fcgi_stdio.h</tt>
+            to the <tt>CFLAGS</tt> variable.<p>
+
+            This includes <tt>fcgi_stdio.h</tt>
+            when compiling C code for the Tcl interpreter, overriding
+            the normal <tt>stdio</tt> types, variables, and functions.<p>
+
+        <li>Add <tt>../fcgi-devel-kit/libfcgi/libfcgi.a</tt> before the
+            <tt>@LIBS@</tt> part of the <tt>LIBS</tt> variable.<p>
+
+            This links the implementation of <tt>fcgi_stdio.h</tt>
+            into the Tcl interpreter, for use by the <tt>FCGI_accept</tt>
+            command and any code that uses <tt>stdio</tt> variables
+            or calls <tt>stdio</tt> functions.<p>
+    </ul><p>
+
+    The last two edits will vary if you use a compiler other than gcc or
+    install the <tt>tcl7.4</tt> directory
+    somewhere else in relation to the <tt>fcgi-devel-kit</tt> directory.<p>
+
+    <li><tt>configure.in</tt>:<p>
+    <ul>
+        <li>
+        Replace the lines
+        <pre>
+AC_C_CROSS
+CC=${CC-cc}</pre>
+        with the lines
+        <pre>
+AC_PROG_CC
+AC_C_CROSS</pre>
+        This selects gcc in preference to other C compilers.<p>
+         
+        <li>
+        Add the following lines just after the
+        <tt>AC_SUBST(CC)</tt> line:
+        <pre>
+AC_CHECK_LIB(socket, main, [LIBS="$LIBS -lsocket"])
+AC_CHECK_LIB(nsl, main, [LIBS="$LIBS -lnsl"])
+AC_SUBST(LIBS)</pre>
+        This ensures that the socket libraries used by FastCGI
+        are linked into the Tcl interpreter.<p>
+    </ul>
+    If GNU autoconf is not available to you, you'll leave
+    <tt>configure.in</tt> alone and perform the following steps:<p>
+    <ul>
+        <li>
+        Execute
+        <pre>
+    > SETENV CC gcc</pre>
+        before running <tt>configure</tt>.<p>
+
+        <li>
+        If you are running on a SVR4-derived Unix platform,
+        edit <tt>Makefile</tt> to add
+        <tt>-lsocket -lnsl</tt> to the <tt>LIBS</tt> value
+        after running <tt>configure</tt>.<p>
+    </ul>
+    If you ever re-run <tt>configure</tt>, you'll need to repeat
+    these steps.<p>
+
+</ul>
+
+
+<h3><a NAME = "S4">4. Writing FastCGI applications in Tcl</a></h3>
+
+The Tcl program <tt>tcl/tiny-tcl-fcgi</tt> performs the same
+function as the C program <tt>examples/tiny-fcgi.c</tt>
+that's used as an example in the
+<a href="fcgi-devel-kit.html#S3.1.1">FastCGI Developer's Kit
+document</a>.  Here's what the Tcl version looks like:<p>
+
+<pre>
+#!./tclsh
+set count 0 
+while {[FCGI_Accept] &gt;= 0 } {
+    incr count
+    puts -nonewline "Content-type: text/html\r\n\r\n"
+    puts "&lt;title&gt;FastCGI Hello! (Tcl)&lt;/title&gt;"
+    puts "&lt;h1&gt;FastCGI Hello! (Tcl)&lt;/h1&gt;"
+    puts "Request number $count running on host &lt;i&gt;$env(SERVER_NAME)&lt;/i&gt;"
+}
+</pre>
+
+If you've built Tcl according to the recipe and you have a Web server
+set up to run FastCGI applications, load the FastCGI Developer's Kit
+Index Page in that server and run this Tcl application now.<p>
+
+The script invokes Tcl indirectly via the symbolic link
+<tt>examples/tclsh</tt>.  It does this because HP-UX has a limit of 32
+characters for the first line of a command-interpreter file such as
+<tt>examples/tiny-tcl-fcgi</tt>.  If you run on HP-UX you won't want
+to sprinkle symbolic links to <tt>tclsh</tt> everywhere, so you should install
+<tt>tclsh</tt> with a shorter pathname than
+<tt>/usr/local/tcl7.4-fcgi/bin/tclsh7.4</tt>.<p>
+
+The Tcl command <tt>FCGI_Accept</tt> treats the initial
+environment differently than the C function <tt>FCGI_Accept</tt>.  The
+first call to the
+C function <tt>FCGI_Accept</tt> replaces the initial environment with
+the environment of the first request.  The first call to the Tcl command
+<tt>FCGI_Accept</tt> adds the variable bindings of the first request
+to the bindings present in the initial environment.  So when the first
+call to <tt>FCGI_Accept</tt> returns, bindings from the initial
+environment are still there (unless, due to naming conflicts, some of
+them have been overwritten by the first request).  The next call to
+<tt>FCGI_Accept</tt> removes the bindings made on the previous call
+before adding a new set for the request just accepted, again preserving
+the initial environment.<p>
+
+The FastCGI-integrated <tt>tclsh</tt> also includes
+commands <tt>FCGI_Finish</tt>, <tt>FCGI_SetExitStatus</tt>,
+and <tt>FCGI_StartFilterData</tt> that correspond to
+C functions in <tt>fcgi_stdio.h</tt>; see the manpages for
+full information.<p>
+
+Converting a Tcl CGI application to FastCGI is not fundamentally
+different from converting a C CGI application.  You separate
+the portion of the application that performs one-time
+initialization from the portion that performs per-request
+processing.  You put the per-request processing into a loop
+controlled by <tt>FCGI_Accept</tt>.<p>
+
+<HR>
+<ADDRESS><A HREF="mailto:shanzer@openmarket.com">Mike Shanzer // shanzer@openmarket.com</A></ADDRESS>
+</body>
+</html>
diff --git a/doc/omi-logo.gif b/doc/omi-logo.gif
new file mode 100644 (file)
index 0000000..00bda56
Binary files /dev/null and b/doc/omi-logo.gif differ
diff --git a/doc/www5-api-workshop.html b/doc/www5-api-workshop.html
new file mode 100644 (file)
index 0000000..13d005f
--- /dev/null
@@ -0,0 +1,286 @@
+<html>
+<head>
+<title>FastCGI: A High-Performance Gateway Interface</title>
+</head>
+
+<body>
+<center>
+<h2>FastCGI: A High-Performance Gateway Interface</h2>
+Position paper for the workshop
+"Programming the Web - a search for APIs",<br>
+Fifth International World Wide Web Conference,
+6 May 1996, Paris, France.<br>
+<p>
+Mark R. Brown<br>
+Open Market, Inc.<br>
+<p>
+
+2 May 1996<br>
+</center>
+<p>
+
+<h5 align=center>
+Copyright &copy; 1996 Open Market, Inc.  245 First Street, Cambridge,
+  MA 02142 U.S.A.<br>
+Tel: 617-621-9500 Fax: 617-621-1703 URL:
+  <a href="http://www.openmarket.com/">http://www.openmarket.com/</a><br>
+</h5>
+<hr>
+
+
+<H3>Abstract</H3>
+
+FastCGI is a fast, open, and secure Web server interface that
+solves the performance problems inherent in CGI without
+introducing any of the new problems associated with writing applications
+to lower-level Web server APIs.  Modules to support FastCGI can
+be plugged into Web server APIs such as Apache API, NSAPI, and ISAPI.
+Key considerations in designing FastCGI included minimizing the cost of
+migrating CGI applications (including applications written in popular
+scripting languages such as Perl), supporting both single-threaded and
+multi-threaded application programming, supporting distributed configurations
+for scaling and high availability, and generalizing the roles that
+gateway applications can play beyond CGI's "responder" role.<p>
+
+For more information on FastCGI, including an interface specification
+and a module for the Apache server, visit the
+<a href = "http://www.fastcgi.com/">www.fastcgi.com Web site</a>.
+
+
+<H3>1. Introduction</H3>
+
+The surge in the use of the Web by business has created great demand
+for applications that create dynamic content.  These applications
+allow businesses to deliver products, services, and messages whose
+shape and content are influenced by interaction with and knowledge
+of users.
+
+<P> This move towards dynamic Web content has highlighted the performance
+limits of CGI (Common Gateway Interface).  In response there has been
+a proliferation of Web server APIs.  These APIs address some (though
+not all) of the performance problems with CGI, but are not designed to
+meet the need of business applications.  When applied to business
+applications, Web server APIs suffer from these problems:
+
+<UL>
+<LI><B>Complexity.</B>
+  Server APIs introduce a steep learning curve, with increased
+  implementation and maintenance costs.
+<LI><B>Language dependence.</B>
+  Applications have to be written in a language supported by the
+  server API (usually C/C++).  Perl, the most popular language for
+  CGI programs, can't be used with any existing server API.
+<LI><B>No process isolation.</B>
+  Since the applications run in the server's address space, buggy
+  applications can corrupt the core server (or each other).  A
+  malicious or buggy application can compromise server security, and
+  bugs in the core server can corrupt applications.
+<LI><B>Proprietary.</B>
+  Coding your application to a particular API locks you into a
+  particular server.
+<LI><B>Tie-in to server architecture.</B>
+  API applications have to share the same architecture as the server:
+  If the Web server is multi-threaded, the application has to be
+  thread-safe.  If the Web server has single-threaded processes,
+  multi-threaded applications don't gain any performance advantage.
+  Also, when the server's architecture changes, the API
+  will usually have to change, and applications will have to be
+  adapted or rewritten.
+</UL>
+
+Web server APIs are suitable for applications that require an intimate
+connection to the core Web server, such as security protocols.  But
+using a Web server API for a Web business application would be much
+like using an old-fashioned TP monitor, which required linking
+applications right into the monitor, for a modern business transaction
+processing application.  The old-fashioned solution suffers a huge
+development and maintenance cost penalty because it ignores 30 years
+of progress in computing technology, and may end up
+providing inferior performance to boot.  Nobody uses the old technology
+unless they are already locked into it.
+
+<p> FastCGI is best viewed as a new implementation of CGI,
+designed to overcome CGI's performance problems.  The major
+implementation differences are:
+
+<UL>
+  <LI>FastCGI processes are persistent: after finishing a request,
+  they wait for a new request instead of exiting.
+
+  <LI>Instead of using operating system environment variables and
+  pipes, the FastCGI protocol multiplexes the environment information,
+  standard input, output, and error over a single full-duplex
+  connection.  This allows FastCGI programs to run on remote machines,
+  using TCP connections between the Web server and the FastCGI
+  application.
+</UL>
+
+<p> FastCGI communicates the exact same information as CGI in a
+different way.  Because FastCGI <i>is</i> CGI, and like CGI runs
+applications in separate processes, it suffers none of the server API
+problems listed above.
+
+
+<H3>2. Migration from CGI</H3>
+
+Open Market has developed a FastCGI application library
+that implements the FastCGI protocol, hiding the protocol details
+from the developer. This library, which is freely available,
+makes writing FastCGI programs
+as easy as writing CGI applications.
+
+<P> The application library provides replacements for the C language
+standard I/O (stdio) routines such as <TT>printf()</TT> and
+<TT>gets()</TT>.  The library converts references to environment
+variables, standard input,
+standard output, and standard error to the FastCGI protocol.
+References to other files &quot;fall through&quot; to the underlying
+operating system standard I/O routines.  This approach has several benefits:
+
+<UL>
+  <LI>Developers don't have to learn a new API to develop FastCGI
+  applications.
+
+  <LI>Existing CGI programs can be migrated with minimal
+  source changes.
+
+  <LI>FastCGI interpreters for Perl, Tcl, and other interpreted
+  languages can be built without modifying the interpreter source
+  code.
+</UL>
+
+<P>
+Here's a simple FastCGI application:<P>
+<PRE>
+    #include &lt;fcgi_stdio.h&gt;
+
+    void main(void)
+    {
+        int count = 0;
+        while(FCGI_Accept() &gt;= 0) {
+            printf("Content-type: text/html\r\n");
+            printf("\r\n");
+            printf("Hello world!&lt;br&gt;\r\n");
+            printf("Request number %d.", count++);
+        }
+        exit(0);
+    }
+</PRE>
+This application returns a &quot;Hello world&quot;
+HTML response to the client.  It also keeps a counter of the number
+of times it has been accessed, displaying the value of the counter
+at each request.  The <TT>fcgi_stdio.h</TT>
+header file provides the FastCGI replacement routines for the
+C standard I/O library.  The <TT>FCGI_Accept()</TT>
+routine accepts a new request from the Web server.</FONT>
+
+<p> The application library was designed to make migration
+of existing CGI programs as simple as possible.  Many applications
+can be converted by adding a loop around the main request processing
+code and recompiling with the FastCGI application library.
+To ease migration to FastCGI, executables built with
+the application library can run as either CGI or FastCGI programs,
+depending on how they are invoked.  The library detects the execution
+environment and automatically selects FastCGI or regular I/O routines,
+as appropriate.
+
+<P> Applications written in Perl, Tcl, and other scripting
+languages can be migrated by using a language interpreter built
+with the application library.  FastCGI-integrated Tcl and Perl
+interpreters for popular Unix platforms are available from
+the <i>www.fastcgi.com</i> Web site.  The interpreters are
+backward-compatible:  They can run standard Tcl and Perl applications.
+
+
+<H3>3. Single-threaded and multi-threaded applications</H3>
+
+FastCGI gives developers a free choice of whether to develop
+applications in a single-threaded or multi-threaded style.
+The FastCGI interface supports multi-threading in two ways:
+
+<ul>
+  <li> Applications can accept concurrent Web server connections
+  to provide concurrent requests to multiple application threads.
+
+  <li> Applications can accept multiplexed Web server connections,
+  in which concurrent requests are communicated over a single
+  connection to multiple application threads.
+</ul>
+
+<p> Multi-threaded programming is complex -- concurrency makes
+programs difficult to test and debug -- so many developers will prefer
+to program in the familiar single-threaded style.  By having several
+concurrent processes running the same application it is often possible
+to achieve high performance with single-threaded programming.
+
+<p> The FastCGI interface allows Web servers to implement <i>session
+affinity</i>, a feature that allows applications to maintain
+caches of user-related data.  With session affinity, when several
+concurrent processes are running the same application, the Web
+server routes all requests from a particular user to the same
+application process.  Web server APIs don't provide this functionality
+to single-threaded applications, so the performance of an API-based
+application is often inferior to the performance of the
+corresponding FastCGI application.
+
+
+<H3>4. Distributed FastCGI</H3>
+
+Because FastCGI can communicate over TCP/IP connections, it supports
+configurations in which applications run remotely from
+the Web server.  This can provide scaling, load balancing,
+high availability, and connections to systems that don't have
+Web servers.
+
+<p> Distributed FastCGI can also provide security advantages.
+A Web server outside a corporate firewall can communicate
+through the firewall to internal databases.  For instance,
+an application might need to authenticate incoming users
+as customers in order to give access to certain documents
+on the external Web site.  With FastCGI this authentication
+can be done without replicating data and without compromising
+security.
+
+
+<H3>5. Roles</H3>
+
+A problem with CGI is its limited functionality:
+CGI programs can only provide responses to requests. 
+FastCGI provides expanded functionality with support for three
+different application &quot;roles&quot;:
+<UL>
+
+  <LI><B>Responder.</B> This is the basic FastCGI role, and
+  corresponds to the simple functionality offered by CGI today.
+
+  <LI><B>Filter.</B> The FastCGI application filters the requested Web
+  server file before sending it to the client.
+
+  <LI><B>Authorizer.</B> The FastCGI program performs an access
+  control decision for the request (such as performing a
+  username/password database lookup).
+
+</UL>
+
+<P>
+Other roles will be defined in the future.  For instance,
+a &quot;logger&quot; role would be useful, where the FastCGI program
+would receive the server's log entries for real-time processing
+and analysis.
+
+
+<H3>6. Conclusions</H3>
+
+<P>Today's Web business applications need a platform that's fast,
+open, maintainable, straightforward, stable, and secure.  FastCGI's
+design meets these requirements, and provides a logical migration
+path from the proven and widely deployed CGI technology.  This allows
+developers to take advantage of FastCGI's benefits without losing
+their existing investment in CGI applications.
+
+<P>For more information about FastCGI, visit the
+<a href = "http://www.fastcgi.com/">www.fastcgi.com Web site</a>.
+
+
+</BODY>
+</HTML>
diff --git a/examples/Makefile.in b/examples/Makefile.in
new file mode 100644 (file)
index 0000000..153288f
--- /dev/null
@@ -0,0 +1,100 @@
+#
+#  Makefile for FastCGI examples directory
+#
+#  Open Market, Inc.
+#
+#  $Id: Makefile.in,v 1.1 1997/09/16 15:36:28 stanleyg Exp $
+#
+
+SHELL = @SHELL@
+O =     @O@
+L =     @L@
+CC     = @CC@
+
+INCLUDEDIR  = ../include
+CFLAGS = @CCDEFS@ @PROFILE@ -I$(INCLUDEDIR)
+LIBS   = @LIBS@
+RANLIB = @RANLIB@
+
+PERL_INSTALL = /usr/local/bin/perl5-fcgi
+TCL_INSTALL  = /usr/local/bin/tcl7.4-fcgi
+
+INCLUDES    = $(INCLUDEDIR)/fastcgi.h $(INCLUDEDIR)/fcgiapp.h \
+             $(INCLUDEDIR)/fcgimisc.h $(INCLUDEDIR)/fcgiappmisc.h \
+              $(INCLUDEDIR)/fcgi_stdio.h
+LIBDIR      = ../libfcgi
+LIBFCGI            = $(LIBDIR)/libfcgi.${L}
+TARGETS            = tiny-cgi.cgi tiny-fcgi tiny-fcgi2 tiny-authorizer \
+             echo echo2 sample-store sockets echo.fcg perl tclsh \
+              SampleStore.state.0 SampleStore.state.1 log-dump
+
+all: $(TARGETS)
+
+tiny-cgi.cgi: tiny-cgi.${O}
+       $(CC) $(CFLAGS) tiny-cgi.${O} -o tiny-cgi.cgi
+
+tiny-fcgi: tiny-fcgi.${O} $(LIBFCGI)
+       $(CC) $(CFLAGS) tiny-fcgi.${O} -o tiny-fcgi $(LIBFCGI) $(LIBS)
+
+tiny-fcgi2: tiny-fcgi2.${O} $(LIBFCGI)
+       $(CC) $(CFLAGS) tiny-fcgi2.${O} -o tiny-fcgi2 $(LIBFCGI) $(LIBS)
+
+tiny-authorizer: tiny-authorizer.${O} $(LIBFCGI)
+       $(CC) $(CFLAGS) tiny-authorizer.${O} -o tiny-authorizer $(LIBFCGI) $(LIBS)
+
+echo: echo.${O} $(LIBFCGI)
+       $(CC) $(CFLAGS) echo.${O} -o echo $(LIBFCGI) $(LIBS)
+
+echo2: echo2.${O} $(LIBFCGI)
+       $(CC) $(CFLAGS) echo2.${O} -o echo2 $(LIBFCGI) $(LIBS)
+
+sample-store: sample-store.${O} tclHash.${O} $(LIBFCGI)
+       $(CC) $(CFLAGS) sample-store.${O} tclHash.${O} -o sample-store $(LIBFCGI) $(LIBS)
+
+log-dump: log-dump.${O} $(LIBFCGI)
+       $(CC) $(CFLAGS) log-dump.${O} -o log-dump $(LIBFCGI) $(LIBS)
+
+sockets:
+       mkdir sockets
+
+echo.fcg:
+       rm -f echo.fcg
+       ln -s echo echo.fcg
+
+perl:
+       rm -f perl
+       ln -s $(PERL_INSTALL)/bin/perl perl
+
+tclsh:
+       rm -f tclsh
+       ln -s $(TCL_INSTALL)/bin/tclsh7.4 tclsh
+
+SampleStore.state.0:
+       mkdir SampleStore.state.0
+
+SampleStore.state.1:
+       mkdir SampleStore.state.1
+
+clean:
+       rm -rf sockets SampleStore.state.0 SampleStore.state.1
+       rm -f *.${L} *.${O} core.* errs *~ \#* TAGS *.E a.out $(TARGETS)
+
+# ----------------------------------------------------------------------------
+
+tiny-cgi.${O}: tiny-cgi.c $(INCLUDES)
+
+tiny-fcgi.${O}: tiny-fcgi.c $(INCLUDES)
+
+tiny-fcgi2.${O}: tiny-fcgi2.c $(INCLUDES)
+
+tiny-authorizer.${O}: tiny-authorizer.c $(INCLUDES)
+
+echo.${O}: echo.c $(INCLUDES)
+
+echo2.${O}: echo2.c $(INCLUDES)
+
+sample-store.${O}: sample-store.c $(INCLUDES)
+
+tclHash.${O}: tclHash.c
+
+log-dump.${O}: log-dump.c $(INCLUDES)
diff --git a/examples/SampleStore/Images/cart-hd.gif b/examples/SampleStore/Images/cart-hd.gif
new file mode 100644 (file)
index 0000000..330856a
Binary files /dev/null and b/examples/SampleStore/Images/cart-hd.gif differ
diff --git a/examples/SampleStore/Images/main-hd.gif b/examples/SampleStore/Images/main-hd.gif
new file mode 100644 (file)
index 0000000..34e6170
Binary files /dev/null and b/examples/SampleStore/Images/main-hd.gif differ
diff --git a/examples/SampleStore/Images/offer-hd.gif b/examples/SampleStore/Images/offer-hd.gif
new file mode 100644 (file)
index 0000000..bc66f00
Binary files /dev/null and b/examples/SampleStore/Images/offer-hd.gif differ
diff --git a/examples/SampleStore/Images/purch-hd.gif b/examples/SampleStore/Images/purch-hd.gif
new file mode 100644 (file)
index 0000000..7ce89a3
Binary files /dev/null and b/examples/SampleStore/Images/purch-hd.gif differ
diff --git a/examples/SampleStore/Images/thank-hd.gif b/examples/SampleStore/Images/thank-hd.gif
new file mode 100644 (file)
index 0000000..bd644ac
Binary files /dev/null and b/examples/SampleStore/Images/thank-hd.gif differ
diff --git a/examples/SampleStore/Protected/RMSTitanic.html b/examples/SampleStore/Protected/RMSTitanic.html
new file mode 100644 (file)
index 0000000..3d2d0fc
--- /dev/null
@@ -0,0 +1,24 @@
+<html>
+<head>
+<title>Special Offer</title>
+</head>
+
+<body bgcolor="ffffff" text="000000" link="39848c" vlink="808080" 
+alink="000000">
+<center>
+<img src="../Images/offer-hd.gif" alt="[Special Offer]">
+</center>
+<h3>Here's just the thing to accompany your <i>RMS Titanic</i>:</h3>
+<ul>
+  <li>Add the<a href="../App?op=AddItemToCart&item=YellowSubmarine">
+      <i>Yellow Submarine</i></a> to your shopping cart.
+</ul><p>
+
+<a href="../App?op=DisplayStore">Return to shop.</a><p>
+
+<a href="../App?op=DisplayCart">View the contents of your
+    shopping cart.</a><p>
+
+<hr>
+</body>
+</html>
diff --git a/examples/SampleStore/Unprotected/Purchase.html b/examples/SampleStore/Unprotected/Purchase.html
new file mode 100644 (file)
index 0000000..c28e226
--- /dev/null
@@ -0,0 +1,24 @@
+<html>
+<head>
+<title>Purchase</title>
+</head>
+
+<body bgcolor="ffffff" text="000000" link="39848c" vlink="808080" 
+alink="000000">
+
+<center>
+<img src="../Images/purch-hd.gif" alt="[Purchase]">
+</center>
+
+This is only a sample program; you can't make real purchases.<p>
+
+<a href="../App?op=Purchase">Complete the purchase (i.e. empty your cart).</a><p>
+
+<a href="../App?op=DisplayStore">Return to shop.</a><p>
+
+<a href="../App?op=DisplayCart">View the contents of your
+    shopping cart.</a><p>
+
+<hr>
+</body>
+</html>
diff --git a/examples/SampleStore/Unprotected/ThankYou.html b/examples/SampleStore/Unprotected/ThankYou.html
new file mode 100644 (file)
index 0000000..9a8c747
--- /dev/null
@@ -0,0 +1,18 @@
+<html>
+<head>
+<title>Thank You</title>
+</head>
+
+<body bgcolor="ffffff" text="000000" link="39848c" vlink="808080" 
+alink="000000">
+<center>
+<img src="../Images/thank-hd.gif" alt="[Thank You]">
+</center>
+
+We look forward to your next visit.<p>
+
+<a href="../App?op=DisplayStore">Return to shop.</a><p>
+
+<hr>
+</body>
+</html>
diff --git a/examples/conf/om-httpd.config b/examples/conf/om-httpd.config
new file mode 100644 (file)
index 0000000..4320d9e
--- /dev/null
@@ -0,0 +1,86 @@
+#
+# om-httpd.config
+#
+#       Config file for demonstrating the FastCGI Developer's Kit
+#       using the Open Market Secure WebServer
+#
+#
+# Copyright (c) 1996 Open Market, Inc.
+#
+# See the file "LICENSE.TERMS" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+#
+# 1. Copy this file to 'conf/httpd.config' in the home directory
+#    of the WebServer.
+#
+# 2. Create a symbolic link to the fcgi-devel-kit directory from
+#    'root' in the home directory of the WebServer.
+#
+# 3. If the port assignment conflicts with another server, change it.
+#
+# $Id: om-httpd.config,v 1.1 1997/09/16 15:36:30 stanleyg Exp $
+#
+Port 5555
+Procs 1
+set home [pwd]/..
+ServerRoot $home
+Filemap / $home/root
+IndexFile index.html
+ExtendedLog $home/logs/httpd.log
+PidFile     $home/logs/httpd.pid
+Region /* {
+    DirectoryIndex
+}
+source mime-types.config
+#
+# Ticketing stuff
+#
+SI_Enable On
+SI_Department SampleStoreDept \
+    -RewriteHtmlLinks Off -RewriteImageLinks Off \
+    -EnableAnonymousTicketing 1 \
+    -UseCookies No
+Region /apps/* {
+    SI_RequireSI SampleStoreDept 1
+}
+Region /SampleStore/* {
+    SI_RequireSI SampleStoreDept 1
+}
+#
+# tiny-fcgi: /apps/tiny-fcgi
+#
+AppClass  tiny-fcgi $home/root/examples/tiny-fcgi
+Responder tiny-fcgi /apps/tiny-fcgi
+#
+# echo: /apps/echo
+#
+AppClass echo $home/root/examples/echo \
+  -processes 2 -affinity \
+  -initial-env STATE=Texas
+Responder echo /apps/echo
+#
+# echo with tiny-authorizer: /apps/echo-protected
+#
+AppClass tiny-authorizer $home/root/examples/tiny-authorizer \
+  -initial-env USER=fastcgi -initial-env PASSWORD=sano
+Responder echo /apps/echo-protected
+Region /apps/echo-protected {
+    CGIPassword
+}
+AuthorizeRegion /apps/echo-protected tiny-authorizer
+Region /apps/echo-protected {
+     AffinityMap -id $PROCESS_ID
+}
+#
+# sample-store: /SampleStore/App
+#
+Filemap /SampleStore $home/root/examples/SampleStore
+AppClass  SampleStoreAppClass \
+  $home/root/examples/sample-store \
+  -processes 2 -affinity \
+  -initial-env STATE_DIR=$home/root/examples/SampleStore.state \
+  -initial-env CKP_THRESHOLD=100 \
+  -initial-env CART_HOLD_MINUTES=240
+Responder SampleStoreAppClass /SampleStore/App
+AuthorizeRegion /SampleStore/Protected/* SampleStoreAppClass
diff --git a/examples/echo-perl b/examples/echo-perl
new file mode 100755 (executable)
index 0000000..f8762d6
--- /dev/null
@@ -0,0 +1,54 @@
+#!./perl
+#
+#  echo-perl --
+# 
+#      Produce a page containing all FastCGI inputs
+# 
+# Copyright (c) 1996 Open Market, Inc.
+#
+# See the file "LICENSE.TERMS" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+# 
+#  $Id: echo-perl,v 1.1 1997/09/16 15:36:28 stanleyg Exp $
+#
+
+use FCGI;
+
+sub print_env {
+    my($label, $envp) = @_;
+    print("$label:<br>\n<pre>\n");
+    my @keys = sort keys(%$envp);
+    foreach $key (@keys) {
+        print("$key=$$envp{$key}\n");
+    }
+    print("</pre><p>\n");
+}
+
+while (($key, $val) = each %ENV) {
+    $initialEnv{$key} = $val;
+}
+$count = 0;
+while(FCGI::accept() >= 0) {
+    print("Content-type: text/html\r\n\r\n",
+          "<title>FastCGI echo (Perl)</title>\n",
+          "<h1>FastCGI echo (Perl)</h1>\n",
+          "Request number ", ++$count, "<p>\n");
+    $len = 0 + $ENV{'CONTENT_LENGTH'};
+    if($len == 0) {
+        print("No data from standard input.<p>\n");
+    } else {
+        print("Standard input:<br>\n<pre>\n");
+        for($i = 0; $i < $len; $i++) {
+            $ch = getc(STDIN);
+            if($ch eq "") {
+                print("Error: Not enough bytes received ",
+                      "on standard input<p>\n");
+                last;
+           }
+            print($ch);
+        }
+        print("\n</pre><p>\n");
+    }
+    print_env("Request environment", \%ENV);
+    print_env("Initial environment", \%initialEnv);
+}
diff --git a/examples/echo-tcl b/examples/echo-tcl
new file mode 100755 (executable)
index 0000000..d7065c2
--- /dev/null
@@ -0,0 +1,56 @@
+#!./tclsh
+#
+#  echo-tcl --
+# 
+#      Produce a page containing all FastCGI inputs
+# 
+# Copyright (c) 1996 Open Market, Inc.
+#
+# See the file "LICENSE.TERMS" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+# 
+#  $Id: echo-tcl,v 1.1 1997/09/16 15:36:28 stanleyg Exp $
+#
+
+proc printEnv {label envArrayName} {
+    upvar $envArrayName envArray
+    puts "$label:<br>\n<pre>"
+    foreach name [lsort [array names envArray]] {
+        puts "$name=$envArray($name)"
+    }
+    puts "</pre><p>"
+}
+
+foreach name [array names env] {
+    set initialEnv($name) $env($name)
+}
+set count 0 
+while {[FCGI_Accept] >= 0 } {
+    incr count
+    puts -nonewline "Content-type: text/html\r\n\r\n"
+    puts "<title>FastCGI echo (Tcl)</title>"
+    puts "<h1>FastCGI echo (Tcl)</h1>"
+    puts "Request number $count <p>"
+    if [info exists env(CONTENT_LENGTH)] {
+        set len $env(CONTENT_LENGTH)
+    } else {
+        set len 0
+    }
+    if {$len == 0} {
+        puts "No data from standard input.<p>"
+    } else {
+        puts "Standard input:<br>\n<pre>"
+        for {set i 0} {$i < $len} {incr i} {
+            set ch [read stdin 1]
+            if {$ch == ""} {
+                puts -nonewline "Error: Not enough bytes received "
+                puts "on standard input<p>"
+                break
+           }
+            puts -nonewline $ch
+       }
+        puts "\n</pre><p>"
+    }
+    printEnv "Request environment" env
+    printEnv "Initial environment" initialEnv
+}
diff --git a/examples/echo.c b/examples/echo.c
new file mode 100644 (file)
index 0000000..e1aa10d
--- /dev/null
@@ -0,0 +1,71 @@
+/* 
+ * echo.c --
+ *
+ *     Produce a page containing all FastCGI inputs
+ *
+ *
+ * Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] = "$Id: echo.c,v 1.1 1997/09/16 15:36:28 stanleyg Exp $";
+#endif /* not lint */
+
+#include "fcgi_stdio.h"
+#include <stdlib.h>
+
+#ifdef _WIN32
+#include <process.h>
+#else
+extern char **environ;
+#endif
+
+void PrintEnv(char *label, char **envp)
+{
+    printf("%s:<br>\n<pre>\n", label);
+    for(; *envp != NULL; envp++) {
+        printf("%s\n", *envp);
+    }
+    printf("</pre><p>\n");
+}
+
+void main ()
+{
+    char **initialEnv = environ;
+    int count = 0;
+    while(FCGI_Accept() >= 0) {
+        char *contentLength = getenv("CONTENT_LENGTH");
+        int len;
+       printf("Content-type: text/html\r\n"
+              "\r\n"
+              "<title>FastCGI echo</title>"
+              "<h1>FastCGI echo</h1>\n"
+               "Request number %d,  Process ID: %d<p>\n", ++count, getpid());
+        if(contentLength != NULL) {
+            len = strtol(contentLength, NULL, 10);
+        } else {
+            len = 0;
+        }
+        if(len <= 0) {
+           printf("No data from standard input.<p>\n");
+        } else {
+            int i, ch;
+           printf("Standard input:<br>\n<pre>\n");
+            for(i = 0; i < len; i++) {
+                if((ch = getchar()) < 0) {
+                    printf("Error: Not enough bytes received "
+                           "on standard input<p>\n");
+                    break;
+               }
+                putchar(ch);
+            }
+            printf("\n</pre><p>\n");
+        }
+        PrintEnv("Request environment", environ);
+        PrintEnv("Initial environment", initialEnv);
+    } /* while */
+}
diff --git a/examples/echo.cgi b/examples/echo.cgi
new file mode 100755 (executable)
index 0000000..bfdebde
--- /dev/null
@@ -0,0 +1,14 @@
+#!../cgi-fcgi/cgi-fcgi -f
+-connect sockets/echo ./echo
+#
+# echo.cgi --
+#
+#        Invoke FastCGI echo program using cgi-fcgi
+#
+# Copyright (c) 1996 Open Market, Inc.
+#
+# See the file "LICENSE.TERMS" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+#  $Id: echo.cgi,v 1.1 1997/09/16 15:36:28 stanleyg Exp $
+#
diff --git a/examples/echo.html b/examples/echo.html
new file mode 100644 (file)
index 0000000..07a51e1
--- /dev/null
@@ -0,0 +1,14 @@
+<html>
+<head><TITLE>EchoFCGI Test</TITLE></head>
+<BODY>
+<FORM ACTION="http://fcgi-devel-kit/examples/EchoFCGI" method="POST">
+Enter your name:<br>
+<INPUT TYPE="text" name="name" size=35><b></b><br>
+<p>
+Enter your password:<br>
+<INPUT TYPE="text" name="password" size=35><b></b><br>
+<p>
+<br><INPUT TYPE="submit" Value="Go Find"> <INPUT TYPE="reset" value="Reset">
+<br>
+</BODY>
+</HTML>
diff --git a/examples/echo.mak b/examples/echo.mak
new file mode 100644 (file)
index 0000000..4604311
--- /dev/null
@@ -0,0 +1,200 @@
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+!IF "$(CFG)" == ""
+CFG=tinyfcgi - Win32 Debug
+!MESSAGE No configuration specified.  Defaulting to tinyfcgi - Win32 Debug.
+!ENDIF 
+
+!IF "$(CFG)" != "tinyfcgi - Win32 Release" && "$(CFG)" !=\
+ "tinyfcgi - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line.  For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "echo.mak" CFG="tinyfcgi - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "tinyfcgi - Win32 Release" (based on\
+ "Win32 (x86) Console Application")
+!MESSAGE "tinyfcgi - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+!ERROR An invalid configuration is specified.
+!ENDIF 
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE 
+NULL=nul
+!ENDIF 
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "tinyfcgi - Win32 Debug"
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "tinyfcgi - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : "$(OUTDIR)\echo.exe"
+
+CLEAN : 
+       -@erase "$(INTDIR)\echo.obj"
+       -@erase "$(OUTDIR)\echo.exe"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MD /W3 /GX /O2 /I "..\include" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/echo.pch" /YX /Fo"$(INTDIR)/" /c 
+CPP_OBJS=.\Release/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/echo.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ..\libfcgi\Release\libfcgi.lib /nologo /subsystem:console /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib ..\libfcgi\Release\libfcgi.lib /nologo /subsystem:console\
+ /incremental:no /pdb:"$(OUTDIR)/echo.pdb" /machine:I386\
+ /out:"$(OUTDIR)/echo.exe" 
+LINK32_OBJS= \
+       "$(INTDIR)\echo.obj"
+
+"$(OUTDIR)\echo.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF  "$(CFG)" == "tinyfcgi - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : "$(OUTDIR)\echo.exe"
+
+CLEAN : 
+       -@erase "$(INTDIR)\echo.obj"
+       -@erase "$(INTDIR)\vc40.idb"
+       -@erase "$(INTDIR)\vc40.pdb"
+       -@erase "$(OUTDIR)\echo.exe"
+       -@erase "$(OUTDIR)\echo.ilk"
+       -@erase "$(OUTDIR)\echo.pdb"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MD /W3 /Gm /GX /Zi /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MD /W3 /Gm /GX /Zi /Od /I "..\include" /D "WIN32" /D "_DEBUG"\
+ /D "_CONSOLE" /Fp"$(INTDIR)/echo.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c 
+CPP_OBJS=.\Debug/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/echo.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ..\libfcgi\Debug\libfcgi.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib ..\libfcgi\Debug\libfcgi.lib /nologo /subsystem:console\
+ /incremental:yes /pdb:"$(OUTDIR)/echo.pdb" /debug /machine:I386\
+ /out:"$(OUTDIR)/echo.exe" 
+LINK32_OBJS= \
+       "$(INTDIR)\echo.obj"
+
+"$(OUTDIR)\echo.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF 
+
+.c{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.c{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+################################################################################
+# Begin Target
+
+# Name "tinyfcgi - Win32 Release"
+# Name "tinyfcgi - Win32 Debug"
+
+!IF  "$(CFG)" == "tinyfcgi - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "tinyfcgi - Win32 Debug"
+
+!ENDIF 
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\echo.c
+DEP_CPP_ECHO_=\
+       "..\include\fcgi_config.h"\
+       "..\include\fcgi_stdio.h"\
+       "..\include\fcgiapp.h"\
+       {$(INCLUDE)}"\sys\TYPES.H"\
+       
+
+"$(INTDIR)\echo.obj" : $(SOURCE) $(DEP_CPP_ECHO_) "$(INTDIR)"
+
+
+# End Source File
+# End Target
+# End Project
+################################################################################
diff --git a/examples/echo2.c b/examples/echo2.c
new file mode 100644 (file)
index 0000000..950cccc
--- /dev/null
@@ -0,0 +1,78 @@
+/* 
+ * echo2.c --
+ *
+ *     Produce a page containing all the inputs (fcgiapp version)
+ *
+ *
+ * Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] = "$Id: echo2.c,v 1.1 1997/09/16 15:36:28 stanleyg Exp $";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "fcgiapp.h"
+
+#ifdef _WIN32
+#include <process.h>
+#endif
+
+void PrintEnv(FCGX_Stream *out, char *label, char **envp)
+{
+    printf("%s:<br>\n<pre>\n", label);
+    FCGX_FPrintF(out, "%s:<br>\n<pre>\n", label);
+    for(; *envp != NULL; envp++) {
+        FCGX_FPrintF(out, "%s\n", *envp);
+    }
+    FCGX_FPrintF(out, "</pre><p>\n");
+}
+
+#ifndef _WIN32
+extern char **environ;
+#endif
+
+void main ()
+{
+    FCGX_Stream *in, *out, *err;
+    FCGX_ParamArray envp;
+    int count = 0;
+    while (FCGX_Accept(&in, &out, &err, &envp) >= 0) {
+        char *contentLength = FCGX_GetParam("CONTENT_LENGTH", envp);
+        int len;
+        FCGX_FPrintF(out,
+               "Content-type: text/html\r\n"
+              "\r\n"
+              "<title>FastCGI echo (fcgiapp version)</title>"
+              "<h1>FastCGI echo (fcgiapp version)</h1>\n"
+               "Request number %d,  Process ID: %d<p>\n", ++count, 
+                     getpid());
+        if(contentLength != NULL) {
+            len = strtol(contentLength, NULL, 10);
+        } else {
+            len = 0;
+        }
+        if(len <= 0) {
+           FCGX_FPrintF(out, "No data from standard input.<p>\n");
+        } else {
+            int i, ch;
+           FCGX_FPrintF(out, "Standard input:<br>\n<pre>\n");
+            for(i = 0; i < len; i++) {
+                if((ch = FCGX_GetChar(in)) < 0) {
+                    FCGX_FPrintF(out, "Error: Not enough bytes received "
+                                      "on standard input<p>\n");
+                    break;
+               }
+                FCGX_PutChar(ch, out);
+            }
+            FCGX_FPrintF(out, "\n</pre><p>\n");
+        }
+        PrintEnv(out, "Request environment", envp);
+        PrintEnv(out, "Initial environment", environ);
+    } /* while */
+}
diff --git a/examples/echo2.html b/examples/echo2.html
new file mode 100644 (file)
index 0000000..a6f56fd
--- /dev/null
@@ -0,0 +1,14 @@
+<html>
+<head><TITLE>EchoFCGI Test</TITLE></head>
+<BODY>
+<FORM ACTION="http://fcgidev/Echo2FCGI" method="POST">
+Enter your name:<br>
+<INPUT TYPE="text" name="name" size=35><br>
+<p>
+Enter your password:<br>
+<INPUT TYPE="text" name="password" size=35><br>
+<p>
+<br><INPUT TYPE="submit" Value="Go Find"> <INPUT TYPE="reset" value="Reset">
+<br>
+</BODY>
+</HTML>
diff --git a/examples/echo2.mak b/examples/echo2.mak
new file mode 100644 (file)
index 0000000..3590c80
--- /dev/null
@@ -0,0 +1,198 @@
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+!IF "$(CFG)" == ""
+CFG=tinyfcgi - Win32 Debug
+!MESSAGE No configuration specified.  Defaulting to tinyfcgi - Win32 Debug.
+!ENDIF 
+
+!IF "$(CFG)" != "tinyfcgi - Win32 Release" && "$(CFG)" !=\
+ "tinyfcgi - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line.  For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "echo2.mak" CFG="tinyfcgi - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "tinyfcgi - Win32 Release" (based on\
+ "Win32 (x86) Console Application")
+!MESSAGE "tinyfcgi - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+!ERROR An invalid configuration is specified.
+!ENDIF 
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE 
+NULL=nul
+!ENDIF 
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "tinyfcgi - Win32 Debug"
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "tinyfcgi - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : "$(OUTDIR)\echo2.exe"
+
+CLEAN : 
+       -@erase "$(INTDIR)\echo2.obj"
+       -@erase "$(OUTDIR)\echo2.exe"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MD /W3 /GX /O2 /I "..\include" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/echo2.pch" /YX /Fo"$(INTDIR)/" /c 
+CPP_OBJS=.\Release/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/echo2.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ..\libfcgi\Release\libfcgi.lib /nologo /subsystem:console /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib ..\libfcgi\Release\libfcgi.lib /nologo /subsystem:console\
+ /incremental:no /pdb:"$(OUTDIR)/echo2.pdb" /machine:I386\
+ /out:"$(OUTDIR)/echo2.exe" 
+LINK32_OBJS= \
+       "$(INTDIR)\echo2.obj"
+
+"$(OUTDIR)\echo2.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF  "$(CFG)" == "tinyfcgi - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : "$(OUTDIR)\echo2.exe"
+
+CLEAN : 
+       -@erase "$(INTDIR)\echo2.obj"
+       -@erase "$(INTDIR)\vc40.idb"
+       -@erase "$(INTDIR)\vc40.pdb"
+       -@erase "$(OUTDIR)\echo2.exe"
+       -@erase "$(OUTDIR)\echo2.ilk"
+       -@erase "$(OUTDIR)\echo2.pdb"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MD /W3 /Gm /GX /Zi /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MD /W3 /Gm /GX /Zi /Od /I "..\include" /D "WIN32" /D "_DEBUG"\
+ /D "_CONSOLE" /Fp"$(INTDIR)/echo2.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c 
+CPP_OBJS=.\Debug/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/echo2.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ..\libfcgi\Debug\libfcgi.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib ..\libfcgi\Debug\libfcgi.lib /nologo /subsystem:console\
+ /incremental:yes /pdb:"$(OUTDIR)/echo2.pdb" /debug /machine:I386\
+ /out:"$(OUTDIR)/echo2.exe" 
+LINK32_OBJS= \
+       "$(INTDIR)\echo2.obj"
+
+"$(OUTDIR)\echo2.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF 
+
+.c{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.c{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+################################################################################
+# Begin Target
+
+# Name "tinyfcgi - Win32 Release"
+# Name "tinyfcgi - Win32 Debug"
+
+!IF  "$(CFG)" == "tinyfcgi - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "tinyfcgi - Win32 Debug"
+
+!ENDIF 
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\echo2.c
+DEP_CPP_ECHO2=\
+       "..\include\fcgi_config.h"\
+       "..\include\fcgiapp.h"\
+       
+
+"$(INTDIR)\echo2.obj" : $(SOURCE) $(DEP_CPP_ECHO2) "$(INTDIR)"
+
+
+# End Source File
+# End Target
+# End Project
+################################################################################
diff --git a/examples/echo2_nt.fcgi b/examples/echo2_nt.fcgi
new file mode 100644 (file)
index 0000000..b3d8cb1
--- /dev/null
@@ -0,0 +1,18 @@
+#!../cgi-fcgi/cgi-fcgi -f
+-connect sockets_echo2 ./echo2.exe
+#
+# echo_nt.fcgi --
+#
+#        Invoke FastCGI echo program using cgi-fcgi
+#
+# Copyright (c) 1996 Open Market, Inc.
+#
+# See the file "LICENSE.TERMS" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+#  $Id: echo2_nt.fcgi,v 1.1 1997/09/16 15:36:28 stanleyg Exp $
+#
+#
+# This file is intended to be used with the cgi-fcgi.exe application on
+# windows NT.
+#
diff --git a/examples/echo_nt.fcgi b/examples/echo_nt.fcgi
new file mode 100644 (file)
index 0000000..1dc1c00
--- /dev/null
@@ -0,0 +1,17 @@
+#!../cgi-fcgi/cgi-fcgi -f
+-connect sockets_echo ./echo.exe
+#
+# echo_nt.fcgi --
+#
+#        Invoke FastCGI echo program using cgi-fcgi
+#
+# Copyright (c) 1996 Open Market, Inc.
+#
+# See the file "LICENSE.TERMS" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+#  $Id: echo_nt.fcgi,v 1.1 1997/09/16 15:36:28 stanleyg Exp $
+#
+# This file is intended to be used with the cgi-fcgi.exe application on
+# windows NT.
+#
diff --git a/examples/log-dump.c b/examples/log-dump.c
new file mode 100644 (file)
index 0000000..d2023ad
--- /dev/null
@@ -0,0 +1,132 @@
+/* 
+ * log-dump.c --
+ *
+ *     FastCGI example program to illustrate both an Authorizer and a
+ *      Responder in a single application that are used to provide access
+ *      to an ascii text file.  The intent of this application is to
+ *      show the basic mechanics needed to display a log file for example
+ *      though any ascii text file should work.
+ *
+ *
+ * Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] = "$Id: log-dump.c,v 1.1 1997/09/16 15:36:28 stanleyg Exp $";
+#endif /* not lint */
+
+#include "fcgi_stdio.h"
+#include <stdlib.h>
+#include <signal.h>
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+static int successCount = 0;
+static int failureCount = 0;
+
+void main(void)
+{
+    char *queryString = NULL;
+    char *rolePtr;
+    char *authPtr;
+    char *fileNamePtr = NULL;
+    int fd, n, i, j;
+    char temp[4096];
+    char temp2[5000];
+    
+    while(FCGI_Accept() >= 0) {
+        rolePtr = getenv("FCGI_ROLE");
+       if(rolePtr == NULL) {
+           kill(getpid(), SIGQUIT);
+           exit(-1);
+       }
+       if(strstr(rolePtr, "AUTHORIZER")) {
+           queryString = getenv("QUERY_STRING");
+           if((queryString == NULL) ||
+              (strstr(queryString, "showme_the_log") == NULL)) {
+               failureCount++;
+               printf("Status: 403 Forbidden\r\n"
+                      "Content-type: text/html\r\n"
+                      "\r\n"
+                      "<title>FastCGI Forbidden!</title>"
+                      "<h2>Access to URL: \"%s\" forbidden!</h2><p>"
+                      "<h2>This is password protected and you "
+                      "have not specified a valid password.</h2>"
+                      "<p><h3>Total Failed Accesses: %d</h3>",
+                      getenv("URL_PATH"), failureCount);
+           } else {
+               successCount++;
+               printf("Status: 200 OK\r\n"
+                    "Variable-LOG_ACCESS: ACCESS_OK.%d\r\n"
+                    "\r\n", successCount);
+           }
+           continue;
+       }
+
+       /*
+        * If we're being invoked as a RESPONDER, make sure that we've
+        * been granted access to return the file or that the file being
+        * requested is beyond access control (ie. per request file data).
+        */
+       if(strstr(rolePtr, "RESPONDER")) {
+           authPtr = getenv("LOG_ACCESS");
+           if((authPtr == NULL) || (strstr(authPtr, "ACCESS_OK") == NULL)) {
+               failureCount++;
+               printf("Content-type: text/html\r\n\r\n"
+                      "<h2>Access to log file \"%s\" denied</h2>"
+                      "<p>Total Invalid Access Attempts: %d\r\n\r\n",
+                      fileNamePtr, failureCount);
+               continue;
+           }
+
+           fileNamePtr = getenv("LOG_FILE");
+           if(fileNamePtr == NULL || *fileNamePtr == '\0') {
+               failureCount++;
+               printf("Content-type: text/html\r\n\r\n"
+                      "<h2>No file specified.</h2>>>"
+                      "<p>Total Invalid Access Attempts: %d\r\n\r\n",
+                      failureCount);
+               continue;
+           }
+
+           fd = open(fileNamePtr, O_RDONLY, (S_IRGRP | S_IROTH | S_IRUSR));
+           if(fd < 0) {
+               printf("Content-type: text/html\r\n\r\n"
+                      "<h2>File Error trying to access file \"%s\".</h2>"
+                      "Error = %s\r\n\r\n", fileNamePtr, strerror(errno));
+               continue;
+           }
+           printf("Content-type: text/html\r\n\r\n"
+                  "<h2>Sending contents of file: %s</h2><p>"
+                  "<h2>Successful Accesses: %d</h2>", fileNamePtr,
+                  successCount);
+           while((n = read(fd, temp, 4096)) > 0) {
+               j = 0;
+               for(i = 0; i < n; i++) {
+                   temp2[j] = temp[i];
+                   if(temp[i] == '\n') {
+                       strcpy(&temp2[j], "<p>");
+                       printf(temp2);
+                       j = 0;
+                   } else {
+                       j++;
+                   }
+               }
+           }
+           close(fd);
+           continue;
+       }
+    }
+}
diff --git a/examples/sample-store.c b/examples/sample-store.c
new file mode 100644 (file)
index 0000000..64ea60f
--- /dev/null
@@ -0,0 +1,1101 @@
+/* 
+ * sample-store.c --
+ *
+ *     FastCGI example program using fcgi_stdio library
+ *
+ *
+ * Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ *
+ * sample-store is a program designed to illustrate one technique
+ * for writing a high-performance FastCGI application that maintains
+ * permanent state.  It is real enough to demonstrate a range of issues
+ * that can arise in FastCGI application programming.
+ *
+ * sample-store implements per-user shopping carts.  These carts are kept
+ * in memory for speed but are backed up on disk for reliability; the
+ * program can restart at any time, affecting at most one request.  Unlike
+ * CGI applications, the performance impact of sample-store's disk
+ * use is negligible: no I/O for query requests, no reads and one write
+ * for a typical update request.
+ *
+ * sample-store's on-disk representation is extremely simple.  The
+ * current state of all shopping carts managed by a process is kept
+ * in two files, a snapshot and a log.  Both files have the same format,
+ * a sequence of ASCII records.  On restart the current state is restored
+ * by replaying the snapshot and the log.  When the log grows to a certain
+ * length, sample-store writes a new snapshot and empties the log.
+ * This prevents the time needed for restart from growing without
+ * bound.
+ * 
+ * Since users "visit" Web sites, but never "leave", sample-store
+ * deletes a shopping cart after the cart has been inactive
+ * for a certain period of time.  This policy prevents sample-store's
+ * memory requirements from growing without bound.
+ *
+ * sample-store operates both as a FastCGI Responder and as an
+ * Authorizer, showing how one program can play two roles.
+ *
+ * The techniques used in sample-store are not specific to shopping
+ * carts; they apply equally well to maintaining all sorts of
+ * information.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] = "$Id: sample-store.c,v 1.1 1997/09/16 15:36:28 stanleyg Exp $";
+#endif /* not lint */
+
+#include "fcgi_stdio.h"  /* FCGI_Accept, FCGI_Finish, stdio */
+#include <stdlib.h>      /* malloc/free, getenv, strtol */
+#include <string.h>      /* strcmp, strncmp, strlen, strstr, strchr */
+#include <tcl.h>         /* Tcl_*Hash* functions */
+#include <time.h>        /* time, time_t */
+#include <assert.h>      /* assert */
+#include <errno.h>       /* errno, ENOENT */
+#include <dirent.h>      /* readdir, closedir, DIR, dirent */
+#include <unistd.h>      /* fsync */
+
+/*
+ * sample-store is designed to be configured as follows:
+ *
+ * SI_Department SampleStoreDept -EnableAnonymousTicketing 1
+ * Region /SampleStore/* { SI_RequireSI SampleStoreDept 1 }
+ *
+ * Filemap /SampleStore $fcgi-devel-kit/examples/SampleStore
+ * AppClass  SampleStoreAppClass \
+ *     $fcgi-devel-kit/examples/sample-store \
+ *     -initial-env STATE_DIR=$fcgi-devel-kit/examples/SampleStore.state \
+ *     -initial-env CKP_THRESHOLD=100 \
+ *     -initial-env CART_HOLD_MINUTES=240 \
+ *     -processes 2 -affinity
+ * Responder SampleStoreAppClass /SampleStore/App
+ * AuthorizeRegion /SampleStore/Protected/* SampleStoreAppClass
+ *
+ * sample-store looks for three initial environment variables:
+ *
+ *  STATE_DIR
+ *    When sample-store is run as a single process without affinity
+ *    this is the directory containing the permanent state of the
+ *    process.  When sample-store is run as multiple processes
+ *    using session affinity, the state directory is
+ *    $STATE_DIR.$FCGI_PROCESS_ID, e.g. SampleStore.state.0
+ *    and SampleStore.state.1 in the config above.  The process
+ *    state directory must exist, but may be empty.
+ *
+ *  CKP_THRESHOLD
+ *    When the log grows to contain this many records the process
+ *    writes a new snapshot and truncates the log.  Defaults
+ *    to CKP_THRESHOLD_DEFAULT.
+ *
+ *  CART_HOLD_MINUTES
+ *    When a cart has not been accessed for this many minutes it
+ *    may be deleted.  Defaults to CART_HOLD_MINUTES_DEFAULT.
+ *
+ * The program is prepared to run as multiple processes using
+ * session affinity (illustrated in config above) or as a single process.
+ *
+ * The program does not depend upon the specific URL prefix /SampleStore.
+ *
+ */
+\f
+/*
+ * This code is organized top-down, trying to put the most interesting
+ * parts first.  Unfortunately, organizing the program in this way requires
+ * lots of extra declarations to take care of forward references.
+ *
+ * Utility functions for string/list processing and such
+ * are left to the very end.  The program uses the Tcl hash table
+ * package because it is both adequate and readily available.
+ */
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#ifndef TRUE
+#define TRUE  (1)
+#endif
+
+#ifndef max
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+#define Strlen(str) (((str) == NULL) ? 0 : strlen(str))
+
+static void *Malloc(size_t size);
+static void Free(void *ptr);
+static char *StringNCopy(char *str, int strLen);
+static char *StringCopy(char *str);
+static char *StringCat(char *str1, char *str2);
+static char *StringCat4(char *str1, char *str2, char *str3, char *str4);
+static char *QueryLookup(char *query, char *name);
+static char *PathTail(char *path);
+
+typedef struct ListOfString {
+    char *head;
+    struct ListOfString *tail;
+} ListOfString;
+static char *ListOfString_Head(ListOfString *list);
+static ListOfString *ListOfString_Tail(ListOfString *list);
+static int ListOfString_Length(ListOfString *list);
+static int ListOfString_IsElement(ListOfString *list, char *element);
+static ListOfString *ListOfString_AppendElement(
+        ListOfString *list, char *element);
+static ListOfString *ListOfString_RemoveElement(
+        ListOfString *list, char *element);
+
+static int IntGetEnv(char *varName, int defaultValue);
+\f
+static void Initialize(void);
+static void PerformRequest(void);
+static void GarbageCollectStep(void);
+static void ConditionalCheckpoint(void);
+
+/*
+ * A typical FastCGI main program: Initialize, then loop
+ * calling FCGI_Accept and performing the accepted request.
+ * Do cleanup operations incrementally between requests.
+ */
+void main(void)
+{
+    Initialize();
+    while(FCGI_Accept() >= 0) {
+        PerformRequest();
+        FCGI_Finish();
+        GarbageCollectStep();
+        ConditionalCheckpoint();
+    }
+}
+\f
+/*
+ * All the global variables
+ */
+typedef struct CartObj {
+    int inactive;                   /* This cart not accessed since mark      */
+    ListOfString *items;            /* Items in cart                          */
+} CartObj;
+static Tcl_HashTable *cartTablePtr; /* Table of CartObj, indexed by userId    */
+static Tcl_HashTable cartTable;
+static char *fcgiProcessId;         /* Id of this process in affinity group   */
+static char *stateDir;              /* Path to dir with snapshot and log      */
+char *snapshotPath, *logPath;       /* Paths to current snapshot and log      */
+static int generation;              /* Number embedded in paths, inc on ckp   */
+static FILE *logFile = NULL;        /* Open for append to current log file    */
+static int numLogRecords;           /* Number of records in current log file  */
+static int checkpointThreshold;     /* Do ckp when numLogRecords exceeds this */
+static int purge = TRUE;            /* Cart collector is removing inactives   */
+static time_t timeCartsMarked;      /* Time all carts marked inactive         */
+static int cartHoldSeconds;         /* Begin purge when this interval elapsed */
+\f
+#define STATE_DIR_VAR             "STATE_DIR"
+#define PID_VAR                   "FCGI_PROCESS_ID"
+#define CKP_THRESHOLD_VAR         "CKP_THRESHOLD"
+#define CKP_THRESHOLD_DEFAULT     200
+#define CART_HOLD_MINUTES_VAR     "CART_HOLD_MINUTES"
+#define CART_HOLD_MINUTES_DEFAULT 300
+
+#define SNP_PREFIX    "snapshot"
+#define LOG_PREFIX    "log"
+#define TMP_SNP_NAME  "tmp-snapshot"
+
+#define LR_ADD_ITEM    "Add"
+#define LR_REMOVE_ITEM "Rem"
+#define LR_EMPTY_CART  "Emp"
+
+
+static char *MakePath(char *dir, char *prefix, int gen);
+static void AnalyzeStateDir(
+    char *dir, char *prefix, int *largestP, ListOfString **fileListP);
+static int RecoverFile(char *pathname);
+static void Checkpoint(void);
+
+/*
+ * Initialize the process by reading environment variables and files
+ */
+static void Initialize(void)
+{
+    char *temp;
+    ListOfString *fileList;
+    int stateDirLen;
+    /*
+     * Process miscellaneous parameters from the initial environment.
+     */
+    checkpointThreshold =
+            IntGetEnv(CKP_THRESHOLD_VAR, CKP_THRESHOLD_DEFAULT);
+    cartHoldSeconds =
+            IntGetEnv(CART_HOLD_MINUTES_VAR, CART_HOLD_MINUTES_DEFAULT)*60;
+    /*
+     * Create an empty in-memory shopping cart data structure.
+     */
+    cartTablePtr = &cartTable;
+    Tcl_InitHashTable(cartTablePtr, TCL_STRING_KEYS);
+    /*
+     * Compute the state directory name from the initial environment
+     * variables.
+     */
+    stateDir = getenv(STATE_DIR_VAR);
+    stateDirLen = Strlen(stateDir);
+    assert(stateDirLen > 0);
+    if(stateDir[stateDirLen - 1] == '/') {
+        stateDir[stateDirLen - 1] = '\000';
+    }
+    fcgiProcessId = getenv(PID_VAR);
+    if(fcgiProcessId != NULL) {
+        stateDir = StringCat4(stateDir, ".", fcgiProcessId, "/");
+    } else {
+        stateDir = StringCat(stateDir, "/");
+    }
+    /*
+     * Read the state directory to determine the current
+     * generation number and a list of files that may
+     * need to be deleted (perhaps left over from an earlier
+     * system crash).  Recover the current generation
+     * snapshot and log (either or both may be missing),
+     * populating the in-memory shopping cart data structure.
+     * Take a checkpoint, making the current log empty.
+     */
+    AnalyzeStateDir(stateDir, SNP_PREFIX, &generation, &fileList);
+    snapshotPath = MakePath(stateDir, SNP_PREFIX, generation);
+    RecoverFile(snapshotPath);
+    logPath = MakePath(stateDir, LOG_PREFIX, generation);
+    numLogRecords = RecoverFile(logPath);
+    Checkpoint();
+    /*
+     * Clean up stateDir without removing the current snapshot and log.
+     */
+    while(fileList != NULL) {
+        char *cur = ListOfString_Head(fileList);
+        if(strcmp(snapshotPath, cur) && strcmp(logPath, cur)) {
+            remove(cur);
+        }
+        fileList = ListOfString_RemoveElement(fileList, cur);
+    }
+}
+
+static char *MakePath(char *dir, char *prefix, int gen)
+{
+    char nameBuffer[24];
+    sprintf(nameBuffer, "%s.%d", prefix, gen);
+    return  StringCat(dir, nameBuffer);
+}
+\f
+static void ConditionalCheckpoint(void)
+{
+    if(numLogRecords >= checkpointThreshold) {
+        Checkpoint();
+    }
+}
+static void WriteSnapshot(char *snpPath);
+
+static void Checkpoint(void)
+{
+    char *tempSnapshotPath, *newLogPath, *newSnapshotPath;
+    /*
+     * Close the current log file.
+     */
+    if(logFile != NULL) {
+        fclose(logFile);
+    }
+    /*
+     * Create a new snapshot with a temporary name.
+     */
+    tempSnapshotPath = StringCat(stateDir, TMP_SNP_NAME);
+    WriteSnapshot(tempSnapshotPath);
+    ++generation;
+    /*
+     * Ensure that the new log file doesn't already exist by removing it.
+     */
+    newLogPath = MakePath(stateDir, LOG_PREFIX, generation);
+    remove(newLogPath);
+    /*
+     * Commit by renaming the snapshot.  The rename atomically
+     * makes the old snapshot and log obsolete.
+     */
+    newSnapshotPath = MakePath(stateDir, SNP_PREFIX, generation);
+    rename(tempSnapshotPath, newSnapshotPath);
+    /*
+     * Clean up the old snapshot and log.
+     */
+    Free(tempSnapshotPath);
+    remove(snapshotPath);
+    Free(snapshotPath);
+    snapshotPath = newSnapshotPath;
+    remove(logPath);
+    Free(logPath);
+    logPath = newLogPath;
+    /*
+     * Open the new, empty log.
+     */
+    logFile = fopen(logPath, "a");
+    numLogRecords = 0;
+}
+\f
+/*
+ * Return *largestP     = the largest int N such that the name prefix.N
+ *                        is in the directory dir.  0 if no such name
+ *        *fileListP    = list of all files in the directory dir,
+ *                        excluding '.' and '..'
+ */
+static void AnalyzeStateDir(
+    char *dir, char *prefix, int *largestP, ListOfString **fileListP)
+{
+    DIR *dp;
+    struct dirent *dirp;
+    int prefixLen = strlen(prefix);
+    int largest = 0;
+    int cur;
+    char *curName;
+    ListOfString *fileList = NULL;
+    dp = opendir(dir);
+    assert(dp != NULL);
+    while((dirp = readdir(dp)) != NULL) {
+        if(!strcmp(dirp->d_name, ".") || !strcmp(dirp->d_name, "..")) {
+            continue;
+       }
+        curName = StringCat(dir, dirp->d_name);
+        fileList = ListOfString_AppendElement(fileList, curName);
+        if(!strncmp(dirp->d_name, prefix, prefixLen)
+                && (dirp->d_name)[prefixLen] == '.') {
+            cur = strtol(dirp->d_name + prefixLen + 1, NULL, 10);
+            if(cur > largest) {
+                largest = cur;
+           }
+       }
+    }
+    assert(closedir(dp) >= 0);
+    *largestP = largest;
+    *fileListP = fileList;
+}
+\f
+static int DoAddItemToCart(char *userId, char *item, int writeLog);
+static int DoRemoveItemFromCart(char *userId, char *item, int writeLog);
+static int DoEmptyCart(char *userId, int writeLog);
+
+/*
+ * Read either a snapshot or a log and perform the specified
+ * actions on the in-memory representation.
+ */
+static int RecoverFile(char *pathname)
+{
+    int numRecords;
+    FILE *recoveryFile = fopen(pathname, "r");
+    if(recoveryFile == NULL) {
+        assert(errno == ENOENT);
+        return 0;
+    }
+    for(numRecords = 0; ; numRecords++) {
+        char buff[128];
+        char op[32], userId[32], item[64];
+        int count;
+        char *status = fgets(buff, sizeof(buff), recoveryFile);
+        if(status == NULL) {
+            assert(feof(recoveryFile));
+            fclose(recoveryFile);
+            return numRecords;
+       }
+        count = sscanf(buff, "%31s %31s %63s", op, userId, item);
+        assert(count == 3);
+        if(!strcmp(op, LR_ADD_ITEM)) {
+            assert(DoAddItemToCart(userId, item, FALSE) >= 0);
+        } else if(!strcmp(op, LR_REMOVE_ITEM)) {
+            assert(DoRemoveItemFromCart(userId, item, FALSE) >= 0);
+        } else if(!strcmp(op, LR_EMPTY_CART)) {
+            assert(DoEmptyCart(userId, FALSE) >= 0);
+        } else {
+            assert(FALSE);
+        }
+    }
+}
+\f
+static void WriteLog(char *command, char *userId, char *item, int force);
+
+/*
+ * Read the in-memory representation and write a snapshot file
+ * that captures it.
+ */
+static void WriteSnapshot(char *snpPath)
+{
+    Tcl_HashSearch search;
+    Tcl_HashEntry *cartEntry;
+    ListOfString *items;
+    char *userId;
+    logFile = fopen(snpPath, "w");
+    assert(logFile != NULL);
+    cartEntry = Tcl_FirstHashEntry(cartTablePtr, &search);
+    for(cartEntry = Tcl_FirstHashEntry(cartTablePtr, &search);
+            cartEntry != NULL; cartEntry = Tcl_NextHashEntry(&search)) {
+        userId = Tcl_GetHashKey(cartTablePtr, cartEntry);
+        for(items = ((CartObj *) Tcl_GetHashValue(cartEntry))->items;
+                items != NULL; items = ListOfString_Tail(items)) {
+            WriteLog(LR_ADD_ITEM, userId, ListOfString_Head(items), FALSE);
+       }
+    }
+    fflush(logFile);
+    fsync(fileno(logFile));
+    fclose(logFile);
+}
+\f
+static void WriteLog(char *command, char *userId, char *item, int force)
+{
+    fprintf(logFile, "%s %s %s\n", command, userId, item);
+    ++numLogRecords;
+    if(force) {
+        fflush(logFile);
+        fsync(fileno(logFile));
+    }
+}
+\f
+static int RemoveOneInactiveCart(void);
+static void MarkAllCartsInactive(void);
+
+/*
+ * Incremental garbage collection of inactive shopping carts:
+ *
+ * Each user access to a shopping cart clears its "inactive" bit via a
+ * call to MarkThisCartActive.  When restart creates a cart it
+ * also marks the cart active.
+ *
+ * If purge == TRUE, each call to GarbageCollectStep scans for and removes
+ * the first inactive cart found.  If there are no inactive carts,
+ * GarbageCollectStep marks *all* carts inactive, records the time in
+ * timeCartsMarked, and sets purge = FALSE.
+ *
+ * If purge == FALSE, each call to GarbageCollectStep checks the
+ * elapsed time since timeCartsMarked.  If the elapsed time
+ * exceeds a threshold, GarbageCollectStep sets purge = TRUE.
+ */
+
+static void GarbageCollectStep(void)
+{
+    if(purge) {
+        if(!RemoveOneInactiveCart()) {
+            MarkAllCartsInactive();
+            timeCartsMarked = time(NULL);
+            purge = FALSE;
+       }
+    } else {
+        int diff = time(NULL)-timeCartsMarked;
+        if(diff > cartHoldSeconds) {
+            purge = TRUE;
+       }
+    }
+}
+\f
+static int RemoveOneInactiveCart(void)
+{
+    Tcl_HashSearch search;
+    Tcl_HashEntry *cartEntry;
+    CartObj *cart;
+    char *userId;
+    cartEntry = Tcl_FirstHashEntry(cartTablePtr, &search);
+    for(cartEntry = Tcl_FirstHashEntry(cartTablePtr, &search);
+            cartEntry != NULL; cartEntry = Tcl_NextHashEntry(&search)) {
+        cart = Tcl_GetHashValue(cartEntry);
+        if(cart->inactive) {
+            userId = Tcl_GetHashKey(cartTablePtr, cartEntry);
+            DoEmptyCart(userId, TRUE);
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+static Tcl_HashEntry *GetCartEntry(char *userId);
+
+static void MarkAllCartsInactive(void)
+{
+    Tcl_HashSearch search;
+    Tcl_HashEntry *cartEntry;
+    CartObj *cart;
+    cartEntry = Tcl_FirstHashEntry(cartTablePtr, &search);
+    for(cartEntry = Tcl_FirstHashEntry(cartTablePtr, &search);
+            cartEntry != NULL; cartEntry = Tcl_NextHashEntry(&search)) {
+        cart = Tcl_GetHashValue(cartEntry);
+        cart->inactive = TRUE;
+    }
+}
+
+static void MarkThisCartActive(char *userId)
+{
+    Tcl_HashEntry *cartEntry = GetCartEntry(userId);
+    CartObj *cart = Tcl_GetHashValue(cartEntry);
+    cart->inactive = FALSE;
+}
+\f
+#define OP_DISPLAY_STORE "DisplayStore"
+#define OP_ADD_ITEM      "AddItemToCart"
+#define OP_DISPLAY_CART  "DisplayCart"
+#define OP_REMOVE_ITEM   "RemoveItemFromCart"
+#define OP_PURCHASE      "Purchase"
+
+static void DisplayStore(
+        char *scriptName, char *parent, char *userId, char *processId);
+static void AddItemToCart(
+        char *scriptName, char *parent, char *userId, char *processId,
+        char *item);
+static void DisplayCart(
+        char *scriptName, char *parent, char *userId, char *processId);
+static void RemoveItemFromCart(
+        char *scriptName, char *parent, char *userId, char *processId,
+        char *item);
+static void Purchase(
+        char *scriptName, char *parent, char *userId, char *processId);
+static void InvalidRequest(char *code, char *message);
+static void Authorize(char *userId);
+
+/*
+ * As a Responder, this application expects to be called with the
+ * GET method and a URL of the form
+ *
+ *     http://<host-port>/<script-name>?op=<op>&item=<item>
+ *
+ * The application expects the SI_UID variable to provide
+ * a user ID, either authenticated or anonymous.
+ *
+ * The application expects the directory *containing* <script-name>
+ * to contain various static HTML files related to the application.
+ *
+ * As an Authorizer, the application expects to be called with
+ * SID_UID and URL_PATH set.
+ */
+
+static void PerformRequest(void)
+{
+    char *method = getenv("REQUEST_METHOD");
+    char *role = getenv("FCGI_ROLE");
+    char *scriptName = PathTail(getenv("SCRIPT_NAME"));
+    char *parent = "";
+    char *op = QueryLookup(getenv("QUERY_STRING"), "op");
+    char *item = QueryLookup(getenv("QUERY_STRING"), "item");
+    char *userId =  getenv("SI_UID");
+    if(userId == NULL) {
+        InvalidRequest("405", "Incorrect configuration, no user id");
+        goto done;
+    } else {
+        MarkThisCartActive(userId);
+    }
+    if(!strcmp(role, "RESPONDER")) {
+        if(strcmp(method, "GET")) {
+            InvalidRequest("405", "Only GET Method Allowed");
+        } else if(op == NULL || !strcmp(op, OP_DISPLAY_STORE)) {
+            DisplayStore(scriptName, parent, userId, fcgiProcessId);
+       } else if(!strcmp(op, OP_ADD_ITEM)) {
+            AddItemToCart(scriptName, parent, userId, fcgiProcessId, item);
+       } else if(!strcmp(op, OP_DISPLAY_CART)) {
+            DisplayCart(scriptName, parent, userId, fcgiProcessId);
+       } else if(!strcmp(op, OP_REMOVE_ITEM)) {
+            RemoveItemFromCart(scriptName, parent, userId, fcgiProcessId, item);
+       } else if(!strcmp(op, OP_PURCHASE)) {
+            Purchase(scriptName, parent, userId, fcgiProcessId);
+       } else {
+            InvalidRequest("404", "Invalid 'op' argument");
+       }
+    } else if(!strcmp(role, "AUTHORIZER")) {
+        Authorize(userId);
+    } else {
+        InvalidRequest("404", "Invalid FastCGI Role");
+    }
+  done:
+    Free(scriptName);
+    Free(op);
+    Free(item);
+}
+\f
+/*
+ * Tiny database of shop inventory.  The first form is the
+ * item identifier used in a request, the second form is used
+ * for HTML display.  REQUIRED_ITEM is the item required
+ * the the Authorizer.  SPECIAL_ITEM is the item on the protected
+ * page (must follow unprotected items in table).
+ */
+
+char *ItemNames[] = {
+        "BrooklynBridge",
+        "RMSTitanic",
+        "CometKohoutec",
+        "YellowSubmarine",
+        NULL
+        };
+char *ItemDisplayNames[] = {
+        "<i>Brooklyn Bridge</i>",
+        "<i>RMS Titanic</i>",
+        "<i>Comet Kohoutec</i>",
+        "<i>Yellow Submarine</i>",
+        NULL
+        };
+#define REQUIRED_ITEM 1
+#define SPECIAL_ITEM 3
+
+
+static char *ItemDisplayName(char *item)
+{
+    int i;
+    if(item == NULL) {
+        return NULL;
+    }
+    for(i = 0; ItemNames[i] != NULL; i++) {
+        if(!strcmp(item, ItemNames[i])) {
+            return ItemDisplayNames[i];
+       }
+    }
+    return NULL;
+}
+\f
+static void DisplayNumberOfItems(int numberOfItems, char *processId);
+
+static void DisplayHead(char *title, char *parent, char *gif)
+{
+    printf("Content-type: text/html\r\n"
+           "\r\n"
+           "<html>\n<head>\n<title>%s</title>\n</head>\n\n"
+           "<body bgcolor=\"ffffff\" text=\"000000\" link=\"39848c\"\n"
+           "      vlink=\"808080\" alink=\"000000\">\n", title);
+    if(parent != NULL && gif != NULL) {
+        printf("<center>\n<img src=\"%s%s\" alt=\"[%s]\">\n</center>\n\n",
+               parent, gif, title);
+    } else {
+        printf("<h2>%s</h2>\n<hr>\n\n", title);
+    }
+}
+
+static void DisplayFoot(void)
+{
+    printf("<hr>\n</body>\n</html>\n");
+}
+
+static void DisplayStore(
+        char *scriptName, char *parent, char *userId, char *processId)
+{
+    Tcl_HashEntry *cartEntry = GetCartEntry(userId);
+    ListOfString *items = ((CartObj *) Tcl_GetHashValue(cartEntry))->items;
+    int numberOfItems = ListOfString_Length(items);
+    int i;
+
+    DisplayHead("FastCGI Shop!", parent, "Images/main-hd.gif");
+    DisplayNumberOfItems(numberOfItems, processId);
+    printf("<h3>Goods for sale:</h3>\n<ul>\n");
+    for(i = 0; i < SPECIAL_ITEM; i++) {
+        printf("  <li>Add the <a href=\"%s?op=AddItemToCart&item=%s\">%s</a>\n"
+               "      to your shopping cart.\n",
+               scriptName, ItemNames[i], ItemDisplayNames[i]);
+    }
+    printf("</ul><p>\n\n");
+    printf("If the %s is in your shopping cart,\n"
+           "<a href=\"%sProtected/%s.html\">go see a special offer</a>\n"
+           "available only to %s purchasers.<p>\n\n",
+           ItemDisplayNames[REQUIRED_ITEM], parent,
+           ItemNames[REQUIRED_ITEM], ItemDisplayNames[REQUIRED_ITEM]);
+    printf("<a href=\"%sUnprotected/Purchase.html\">Purchase\n"
+           "the contents of your shopping cart.</a><p>\n\n", parent);
+    printf("<a href=\"%s?op=DisplayCart\">View the contents\n"
+           "of your shopping cart.</a><p>\n\n", scriptName);
+    DisplayFoot();
+}
+\f
+static Tcl_HashEntry *GetCartEntry(char *userId)
+{
+    Tcl_HashEntry *cartEntry = Tcl_FindHashEntry(cartTablePtr, userId);
+    int new;
+    if(cartEntry == NULL) {
+        CartObj *cart = Malloc(sizeof(CartObj));
+        cart->inactive = FALSE;
+        cart->items = NULL;
+        cartEntry = Tcl_CreateHashEntry(cartTablePtr, userId, &new);
+        assert(new);
+        Tcl_SetHashValue(cartEntry, cart);
+    }
+    return cartEntry;
+}
+\f
+static void AddItemToCart(
+        char *scriptName, char *parent, char *userId, char *processId,
+        char *item)
+{
+    if(DoAddItemToCart(userId, item, TRUE) < 0) {
+        InvalidRequest("404", "Invalid 'item' argument");
+    } else {
+        /*
+         * Would call
+         *   DisplayStore(scriptName, parent, userId, processId);
+         * except for browser reload issue.  Redirect instead.
+         */
+        printf("Location: %s?op=%s\r\n"
+               "\r\n", scriptName, OP_DISPLAY_STORE);
+    }
+}  
+
+static int DoAddItemToCart(char *userId, char *item, int writeLog)
+{
+    if(ItemDisplayName(item) == NULL) {
+        return -1;
+    } else {
+        Tcl_HashEntry *cartEntry = GetCartEntry(userId);
+        CartObj *cart = Tcl_GetHashValue(cartEntry);
+        cart->items = ListOfString_AppendElement(
+                              cart->items, StringCopy(item));
+        if(writeLog) {
+            WriteLog(LR_ADD_ITEM, userId, item, TRUE);
+       }
+    }
+}
+\f
+static void DisplayCart(
+        char *scriptName, char *parent, char *userId, char *processId)
+{
+    Tcl_HashEntry *cartEntry = GetCartEntry(userId);
+    CartObj *cart = Tcl_GetHashValue(cartEntry);
+    ListOfString *items = cart->items;
+    int numberOfItems = ListOfString_Length(items);
+    int i;
+
+    DisplayHead("Your shopping cart", parent, "Images/cart-hd.gif");
+    DisplayNumberOfItems(numberOfItems, processId);
+    printf("<ul>\n");
+    for(; items != NULL; items = ListOfString_Tail(items)) {
+        char *item = ListOfString_Head(items);
+        printf("  <li>%s . . . . . \n"
+               "    <a href=\"%s?op=RemoveItemFromCart&item=%s\">Click\n"
+               "    to remove</a> from your shopping cart.\n",
+               ItemDisplayName(item), scriptName, item);
+    }
+    printf("</ul><p>\n\n");
+    printf("<a href=\"%sUnprotected/Purchase.html\">Purchase\n"
+           "the contents of your shopping cart.</a><p>\n\n", parent);
+    printf("<a href=\"%s?op=DisplayStore\">Return to shop.</a><p>\n\n",
+           scriptName);
+    DisplayFoot();
+}
+\f
+static void RemoveItemFromCart(
+        char *scriptName, char *parent, char *userId, char *processId,
+        char *item)
+{
+    if(DoRemoveItemFromCart(userId, item, TRUE) < 0) {
+        InvalidRequest("404", "Invalid 'item' argument");
+    } else {
+        /*
+         * Would call
+         *   DisplayCart(scriptName, parent, userId, processId);
+         * except for browser reload issue.  Redirect instead.
+         */
+        printf("Location: %s?op=%s\r\n"
+               "\r\n", scriptName, OP_DISPLAY_CART);
+    }
+}
+
+static int DoRemoveItemFromCart(char *userId, char *item, int writeLog)
+{
+    if(ItemDisplayName(item) == NULL) {
+        return -1;
+    } else {
+        Tcl_HashEntry *cartEntry = GetCartEntry(userId);
+        CartObj *cart = Tcl_GetHashValue(cartEntry);
+        if(ListOfString_IsElement(cart->items, item)) {
+            cart->items = ListOfString_RemoveElement(cart->items, item);
+            if (writeLog) {
+                WriteLog(LR_REMOVE_ITEM, userId, item, TRUE);
+           }
+        }
+    }
+}
+\f
+static void Purchase(
+        char *scriptName, char *parent, char *userId, char *processId)
+{
+    DoEmptyCart(userId, TRUE);
+    printf("Location: %sUnprotected/ThankYou.html\r\n"
+           "\r\n", parent);
+}
+
+static int DoEmptyCart(char *userId, int writeLog)
+{
+    Tcl_HashEntry *cartEntry = GetCartEntry(userId);
+    CartObj *cart = Tcl_GetHashValue(cartEntry);
+    ListOfString *items = cart->items;
+    /*
+     * Write log *before* tearing down cart structure because userId
+     * is part of the structure.  (Thanks, Purify.)
+     */
+    if (writeLog) {
+        WriteLog(LR_EMPTY_CART, userId, "NullItem", TRUE);
+    }
+    while(items != NULL) {
+        items = ListOfString_RemoveElement(
+                items, ListOfString_Head(items));
+    }
+    Free(cart);
+    Tcl_DeleteHashEntry(cartEntry);
+    return 0;
+}
+\f
+static void NotAuthorized(void);
+
+static void Authorize(char *userId)
+{
+    Tcl_HashEntry *cartEntry = GetCartEntry(userId);
+    ListOfString *items = ((CartObj *) Tcl_GetHashValue(cartEntry))->items;
+    for( ; items != NULL; items = ListOfString_Tail(items)) {
+        if(!strcmp(ListOfString_Head(items), ItemNames[REQUIRED_ITEM])) {
+            printf("Status: 200 OK\r\n"
+                   "Variable-Foo: Bar\r\n"
+                   "\r\n");
+           return;
+       }
+    }
+    NotAuthorized();
+}
+\f
+static void DisplayNumberOfItems(int numberOfItems, char *processId)
+{
+    if(processId != NULL) {
+        printf("FastCGI process %s is serving you today.<br>\n", processId);
+    }
+    if(numberOfItems == 0) {
+        printf("Your shopping cart is empty.<p>\n\n");
+    } else if(numberOfItems == 1) {
+        printf("Your shopping cart contains 1 item.<p>\n\n");
+    } else {
+        printf("Your shopping cart contains %d items.<p>\n\n", numberOfItems);
+    };
+}
+\f
+static void InvalidRequest(char *code, char *message)
+{
+    printf("Status: %s %s\r\n", code, message);
+    DisplayHead("Invalid request", NULL, NULL);
+    printf("%s.\n\n", message);
+    DisplayFoot();
+}
+
+static void NotAuthorized(void)
+{
+    printf("Status: 403 Forbidden\r\n");
+    DisplayHead("Access Denied", NULL, NULL);
+    printf("Put the %s in your cart to access this page.\n\n",
+           ItemDisplayNames[REQUIRED_ITEM]);
+    DisplayFoot();
+}
+\f
+/*
+ * Mundane utility functions, not specific to this application:
+ */
+
+
+/*
+ * Fail-fast version of 'malloc'
+ */
+static void *Malloc(size_t size)
+{
+    void *result = malloc(size);
+    assert(size == 0 || result != NULL);
+    return result;
+}
+
+/*
+ * Protect against old, broken implementations of 'free'
+ */
+static void Free(void *ptr)
+{
+    if(ptr != NULL) {
+        free(ptr);
+      }
+}
+
+/*
+ * Return a new string created by calling Malloc, copying strLen
+ * characters from str to the new string, then appending a null.
+ */
+static char *StringNCopy(char *str, int strLen)
+{
+    char *newString = Malloc(strLen + 1);
+    memcpy(newString, str, strLen);
+    newString[strLen] = '\000';
+    return newString;
+}
+
+/*
+ * Return a new string that's a copy of str, including the null
+ */
+static char *StringCopy(char *str)
+{
+    return StringNCopy(str, strlen(str));
+}
+
+/*
+ * Return a new string that's a copy of str1 followed by str2,
+ * including the null
+ */
+static char *StringCat(char *str1, char *str2)
+{
+    return StringCat4(str1, str2, NULL, NULL);
+}
+
+static char *StringCat4(char *str1, char *str2, char *str3, char *str4)
+{
+    int str1Len = Strlen(str1);
+    int str2Len = Strlen(str2);
+    int str3Len = Strlen(str3);
+    int str4Len = Strlen(str4);
+    char *newString = Malloc(str1Len + str2Len + str3Len + str4Len + 1);
+    memcpy(newString, str1, str1Len);
+    memcpy(newString + str1Len, str2, str2Len);
+    memcpy(newString + str1Len + str2Len, str3, str3Len);
+    memcpy(newString + str1Len + str2Len + str3Len, str4, str4Len);
+    newString[str1Len + str2Len + str3Len + str4Len] = '\000';
+    return newString;
+}
+
+/*
+ * Return a copy of the value associated with 'name' in 'query'.
+ * XXX: does not perform URL-decoding of query.
+ */
+static char *QueryLookup(char *query, char *name)
+{
+    int nameLen = strlen(name);
+    char *queryTail, *nameFirst, *valueFirst, *valueLast, *value;
+    if(query == NULL) {
+        return NULL;
+    }
+    queryTail = query;
+    for(;;) {
+        nameFirst = strstr(queryTail, name);
+        if(nameFirst == NULL) {
+            return NULL;
+        }
+        if(((nameFirst == query) || (nameFirst[-1] == '&')) &&
+                (nameFirst[nameLen] == '=')) {
+            valueFirst = nameFirst + nameLen + 1;
+            valueLast = strchr(valueFirst, '&');
+            if(valueLast == NULL) {
+                valueLast = strchr(valueFirst, '\000');
+           };
+            return StringNCopy(valueFirst, valueLast - valueFirst);
+        }
+        queryTail = nameFirst + 1;
+    }
+}
+
+/*
+ * Return a copy of the characters following the final '/' character
+ * of path.
+ */
+static char *PathTail(char *path)
+{
+    char *afterSlash, *slash;
+    if(path == NULL) {
+        return NULL;
+    }
+    afterSlash = path;
+    while((slash = strchr(afterSlash, '/')) != NULL) {
+        afterSlash = slash + 1;
+    }
+    return StringCopy(afterSlash);
+}
+
+/*
+ * Return the integer value of the specified environment variable,
+ * or a specified default value if the variable is unbound.
+ */
+static int IntGetEnv(char *varName, int defaultValue)
+{
+    char *strValue = getenv(varName);
+    int value = 0;
+    if(strValue != NULL) {
+        value = strtol(strValue, NULL, 10);
+    }
+    if(value <= 0) {
+        value = defaultValue;
+    }
+    return value;
+}
+
+/*
+ * Should the Tcl hash package detect an unrecoverable error(!), halt.
+ */
+void panic(char *format,
+        char *arg1, char *arg2, char *arg3, char *arg4,
+        char *arg5, char *arg6, char *arg7, char *arg8)
+{
+    assert(FALSE);
+}
+   
+
+/*
+ * ListOfString abstraction
+ */
+
+static char *ListOfString_Head(ListOfString *list)
+{
+    return list->head;
+}
+
+static ListOfString *ListOfString_Tail(ListOfString *list)
+{
+    return list->tail;
+}
+
+static int ListOfString_Length(ListOfString *list)
+{
+    int length = 0;
+    for(; list != NULL; list = list->tail) {
+        length++;
+    }
+    return length;
+}
+
+static int ListOfString_IsElement(ListOfString *list, char *element)
+{
+    for(; list != NULL; list = list->tail) {
+        if(!strcmp(list->head, element)) {
+            return TRUE;
+       }
+    }
+    return FALSE;
+}
+
+static ListOfString *ListOfString_AppendElement(
+        ListOfString *list, char *element)
+{
+    ListOfString *cur;
+    ListOfString *newCell = Malloc(sizeof(ListOfString));
+    newCell->head = element;
+    newCell->tail = NULL;
+    if(list == NULL) {
+        return newCell;
+    } else {
+        for(cur = list; cur->tail != NULL; cur = cur->tail) {
+       }
+        cur->tail = newCell;
+        return list;
+    }
+}
+
+static ListOfString *ListOfString_RemoveElement(
+        ListOfString *list, char *element)
+{
+    ListOfString *cur;
+    ListOfString *prevCell = NULL;
+    for(cur = list; cur != NULL; cur = cur->tail) {
+        if(!strcmp(cur->head, element)) {
+            if(prevCell == NULL) {
+                list = cur->tail;
+           } else {
+                prevCell->tail = cur->tail;
+           }
+            free(cur->head);
+            free(cur);
+            return list;
+       }
+        prevCell = cur;
+    }
+    return list;
+}
+
+
+/*
+ * End
+ */
diff --git a/examples/tclHash.c b/examples/tclHash.c
new file mode 100644 (file)
index 0000000..f13690a
--- /dev/null
@@ -0,0 +1,956 @@
+/* 
+ * tclHash.c --
+ *
+ *     Implementation of in-memory hash tables for Tcl and Tcl-based
+ *     applications.
+ *
+ * Copyright (c) 1991-1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+ *
+ * This software is copyrighted by the Regents of the University of
+ * California, Sun Microsystems, Inc., and other parties.  The following
+ * terms apply to all files associated with the software unless explicitly
+ * disclaimed in individual files.
+ *
+ * The authors hereby grant permission to use, copy, modify, distribute,
+ * and license this software and its documentation for any purpose, provided
+ * that existing copyright notices are retained in all copies and that this
+ * notice is included verbatim in any distributions. No written agreement,
+ * license, or royalty fee is required for any of the authorized uses.
+ * Modifications to this software may be copyrighted by their authors
+ * and need not follow the licensing terms described here, provided that
+ * the new terms are clearly indicated on the first page of each file where
+ * they apply.
+ * 
+ * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
+ * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
+ * DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
+ * IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
+ * NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+ * MODIFICATIONS.
+ * 
+ * RESTRICTED RIGHTS: Use, duplication or disclosure by the government
+ * is subject to the restrictions as set forth in subparagraph (c) (1) (ii)
+ * of the Rights in Technical Data and Computer Software Clause as DFARS
+ * 252.227-7013 and FAR 52.227-19.
+ *
+ * $Id: tclHash.c,v 1.1 1997/09/16 15:36:28 stanleyg Exp $
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#) tclHash.c 1.14 94/12/17 16:14:17";
+#endif /* not lint */
+
+#include "tclInt.h"
+
+/*
+ * When there are this many entries per bucket, on average, rebuild
+ * the hash table to make it larger.
+ */
+
+#define REBUILD_MULTIPLIER     3
+
+
+/*
+ * The following macro takes a preliminary integer hash value and
+ * produces an index into a hash tables bucket list.  The idea is
+ * to make it so that preliminary values that are arbitrarily similar
+ * will end up in different buckets.  The hash function was taken
+ * from a random-number generator.
+ */
+
+#define RANDOM_INDEX(tablePtr, i) \
+    (((((long) (i))*1103515245) >> (tablePtr)->downShift) & (tablePtr)->mask)
+
+/*
+ * Procedure prototypes for static procedures in this file:
+ */
+
+static Tcl_HashEntry * ArrayFind _ANSI_ARGS_((Tcl_HashTable *tablePtr,
+                           char *key));
+static Tcl_HashEntry * ArrayCreate _ANSI_ARGS_((Tcl_HashTable *tablePtr,
+                           char *key, int *newPtr));
+static Tcl_HashEntry * BogusFind _ANSI_ARGS_((Tcl_HashTable *tablePtr,
+                           char *key));
+static Tcl_HashEntry * BogusCreate _ANSI_ARGS_((Tcl_HashTable *tablePtr,
+                           char *key, int *newPtr));
+static unsigned int    HashString _ANSI_ARGS_((char *string));
+static void            RebuildTable _ANSI_ARGS_((Tcl_HashTable *tablePtr));
+static Tcl_HashEntry * StringFind _ANSI_ARGS_((Tcl_HashTable *tablePtr,
+                           char *key));
+static Tcl_HashEntry * StringCreate _ANSI_ARGS_((Tcl_HashTable *tablePtr,
+                           char *key, int *newPtr));
+static Tcl_HashEntry * OneWordFind _ANSI_ARGS_((Tcl_HashTable *tablePtr,
+                           char *key));
+static Tcl_HashEntry * OneWordCreate _ANSI_ARGS_((Tcl_HashTable *tablePtr,
+                           char *key, int *newPtr));
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_InitHashTable --
+ *
+ *     Given storage for a hash table, set up the fields to prepare
+ *     the hash table for use.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     TablePtr is now ready to be passed to Tcl_FindHashEntry and
+ *     Tcl_CreateHashEntry.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tcl_InitHashTable(tablePtr, keyType)
+    register Tcl_HashTable *tablePtr;  /* Pointer to table record, which
+                                        * is supplied by the caller. */
+    int keyType;                       /* Type of keys to use in table:
+                                        * TCL_STRING_KEYS, TCL_ONE_WORD_KEYS,
+                                        * or an integer >= 2. */
+{
+    tablePtr->buckets = tablePtr->staticBuckets;
+    tablePtr->staticBuckets[0] = tablePtr->staticBuckets[1] = 0;
+    tablePtr->staticBuckets[2] = tablePtr->staticBuckets[3] = 0;
+    tablePtr->numBuckets = TCL_SMALL_HASH_TABLE;
+    tablePtr->numEntries = 0;
+    tablePtr->rebuildSize = TCL_SMALL_HASH_TABLE*REBUILD_MULTIPLIER;
+    tablePtr->downShift = 28;
+    tablePtr->mask = 3;
+    tablePtr->keyType = keyType;
+    if (keyType == TCL_STRING_KEYS) {
+       tablePtr->findProc = StringFind;
+       tablePtr->createProc = StringCreate;
+    } else if (keyType == TCL_ONE_WORD_KEYS) {
+       tablePtr->findProc = OneWordFind;
+       tablePtr->createProc = OneWordCreate;
+    } else {
+       tablePtr->findProc = ArrayFind;
+       tablePtr->createProc = ArrayCreate;
+    };
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_DeleteHashEntry --
+ *
+ *     Remove a single entry from a hash table.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     The entry given by entryPtr is deleted from its table and
+ *     should never again be used by the caller.  It is up to the
+ *     caller to free the clientData field of the entry, if that
+ *     is relevant.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tcl_DeleteHashEntry(entryPtr)
+    Tcl_HashEntry *entryPtr;
+{
+    register Tcl_HashEntry *prevPtr;
+
+    if (*entryPtr->bucketPtr == entryPtr) {
+       *entryPtr->bucketPtr = entryPtr->nextPtr;
+    } else {
+       for (prevPtr = *entryPtr->bucketPtr; ; prevPtr = prevPtr->nextPtr) {
+           if (prevPtr == NULL) {
+               panic("malformed bucket chain in Tcl_DeleteHashEntry");
+           }
+           if (prevPtr->nextPtr == entryPtr) {
+               prevPtr->nextPtr = entryPtr->nextPtr;
+               break;
+           }
+       }
+    }
+    entryPtr->tablePtr->numEntries--;
+    ckfree((char *) entryPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_DeleteHashTable --
+ *
+ *     Free up everything associated with a hash table except for
+ *     the record for the table itself.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     The hash table is no longer useable.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tcl_DeleteHashTable(tablePtr)
+    register Tcl_HashTable *tablePtr;          /* Table to delete. */
+{
+    register Tcl_HashEntry *hPtr, *nextPtr;
+    int i;
+
+    /*
+     * Free up all the entries in the table.
+     */
+
+    for (i = 0; i < tablePtr->numBuckets; i++) {
+       hPtr = tablePtr->buckets[i];
+       while (hPtr != NULL) {
+           nextPtr = hPtr->nextPtr;
+           ckfree((char *) hPtr);
+           hPtr = nextPtr;
+       }
+    }
+
+    /*
+     * Free up the bucket array, if it was dynamically allocated.
+     */
+
+    if (tablePtr->buckets != tablePtr->staticBuckets) {
+       ckfree((char *) tablePtr->buckets);
+    }
+
+    /*
+     * Arrange for panics if the table is used again without
+     * re-initialization.
+     */
+
+    tablePtr->findProc = BogusFind;
+    tablePtr->createProc = BogusCreate;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_FirstHashEntry --
+ *
+ *     Locate the first entry in a hash table and set up a record
+ *     that can be used to step through all the remaining entries
+ *     of the table.
+ *
+ * Results:
+ *     The return value is a pointer to the first entry in tablePtr,
+ *     or NULL if tablePtr has no entries in it.  The memory at
+ *     *searchPtr is initialized so that subsequent calls to
+ *     Tcl_NextHashEntry will return all of the entries in the table,
+ *     one at a time.
+ *
+ * Side effects:
+ *     None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_HashEntry *
+Tcl_FirstHashEntry(tablePtr, searchPtr)
+    Tcl_HashTable *tablePtr;           /* Table to search. */
+    Tcl_HashSearch *searchPtr;         /* Place to store information about
+                                        * progress through the table. */
+{
+    searchPtr->tablePtr = tablePtr;
+    searchPtr->nextIndex = 0;
+    searchPtr->nextEntryPtr = NULL;
+    return Tcl_NextHashEntry(searchPtr);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_NextHashEntry --
+ *
+ *     Once a hash table enumeration has been initiated by calling
+ *     Tcl_FirstHashEntry, this procedure may be called to return
+ *     successive elements of the table.
+ *
+ * Results:
+ *     The return value is the next entry in the hash table being
+ *     enumerated, or NULL if the end of the table is reached.
+ *
+ * Side effects:
+ *     None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tcl_HashEntry *
+Tcl_NextHashEntry(searchPtr)
+    register Tcl_HashSearch *searchPtr;        /* Place to store information about
+                                        * progress through the table.  Must
+                                        * have been initialized by calling
+                                        * Tcl_FirstHashEntry. */
+{
+    Tcl_HashEntry *hPtr;
+
+    while (searchPtr->nextEntryPtr == NULL) {
+       if (searchPtr->nextIndex >= searchPtr->tablePtr->numBuckets) {
+           return NULL;
+       }
+       searchPtr->nextEntryPtr =
+               searchPtr->tablePtr->buckets[searchPtr->nextIndex];
+       searchPtr->nextIndex++;
+    }
+    hPtr = searchPtr->nextEntryPtr;
+    searchPtr->nextEntryPtr = hPtr->nextPtr;
+    return hPtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_HashStats --
+ *
+ *     Return statistics describing the layout of the hash table
+ *     in its hash buckets.
+ *
+ * Results:
+ *     The return value is a malloc-ed string containing information
+ *     about tablePtr.  It is the caller's responsibility to free
+ *     this string.
+ *
+ * Side effects:
+ *     None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+char *
+Tcl_HashStats(tablePtr)
+    Tcl_HashTable *tablePtr;           /* Table for which to produce stats. */
+{
+#define NUM_COUNTERS 10
+    int count[NUM_COUNTERS], overflow, i, j;
+    double average, tmp;
+    register Tcl_HashEntry *hPtr;
+    char *result, *p;
+
+    /*
+     * Compute a histogram of bucket usage.
+     */
+
+    for (i = 0; i < NUM_COUNTERS; i++) {
+       count[i] = 0;
+    }
+    overflow = 0;
+    average = 0.0;
+    for (i = 0; i < tablePtr->numBuckets; i++) {
+       j = 0;
+       for (hPtr = tablePtr->buckets[i]; hPtr != NULL; hPtr = hPtr->nextPtr) {
+           j++;
+       }
+       if (j < NUM_COUNTERS) {
+           count[j]++;
+       } else {
+           overflow++;
+       }
+       tmp = j;
+       average += (tmp+1.0)*(tmp/tablePtr->numEntries)/2.0;
+    }
+
+    /*
+     * Print out the histogram and a few other pieces of information.
+     */
+
+    result = (char *) ckalloc((unsigned) ((NUM_COUNTERS*60) + 300));
+    sprintf(result, "%d entries in table, %d buckets\n",
+           tablePtr->numEntries, tablePtr->numBuckets);
+    p = result + strlen(result);
+    for (i = 0; i < NUM_COUNTERS; i++) {
+       sprintf(p, "number of buckets with %d entries: %d\n",
+               i, count[i]);
+       p += strlen(p);
+    }
+    sprintf(p, "number of buckets with %d or more entries: %d\n",
+           NUM_COUNTERS, overflow);
+    p += strlen(p);
+    sprintf(p, "average search distance for entry: %.1f", average);
+    return result;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * HashString --
+ *
+ *     Compute a one-word summary of a text string, which can be
+ *     used to generate a hash index.
+ *
+ * Results:
+ *     The return value is a one-word summary of the information in
+ *     string.
+ *
+ * Side effects:
+ *     None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static unsigned int
+HashString(string)
+    register char *string;     /* String from which to compute hash value. */
+{
+    register unsigned int result;
+    register int c;
+
+    /*
+     * I tried a zillion different hash functions and asked many other
+     * people for advice.  Many people had their own favorite functions,
+     * all different, but no-one had much idea why they were good ones.
+     * I chose the one below (multiply by 9 and add new character)
+     * because of the following reasons:
+     *
+     * 1. Multiplying by 10 is perfect for keys that are decimal strings,
+     *    and multiplying by 9 is just about as good.
+     * 2. Times-9 is (shift-left-3) plus (old).  This means that each
+     *    character's bits hang around in the low-order bits of the
+     *    hash value for ever, plus they spread fairly rapidly up to
+     *    the high-order bits to fill out the hash value.  This seems
+     *    works well both for decimal and non-decimal strings.
+     */
+
+    result = 0;
+    while (1) {
+       c = *string;
+       string++;
+       if (c == 0) {
+           break;
+       }
+       result += (result<<3) + c;
+    }
+    return result;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringFind --
+ *
+ *     Given a hash table with string keys, and a string key, find
+ *     the entry with a matching key.
+ *
+ * Results:
+ *     The return value is a token for the matching entry in the
+ *     hash table, or NULL if there was no matching entry.
+ *
+ * Side effects:
+ *     None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_HashEntry *
+StringFind(tablePtr, key)
+    Tcl_HashTable *tablePtr;   /* Table in which to lookup entry. */
+    char *key;                 /* Key to use to find matching entry. */
+{
+    register Tcl_HashEntry *hPtr;
+    register char *p1, *p2;
+    int index;
+
+    index = HashString(key) & tablePtr->mask;
+
+    /*
+     * Search all of the entries in the appropriate bucket.
+     */
+
+    for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
+           hPtr = hPtr->nextPtr) {
+       for (p1 = key, p2 = hPtr->key.string; ; p1++, p2++) {
+           if (*p1 != *p2) {
+               break;
+           }
+           if (*p1 == '\0') {
+               return hPtr;
+           }
+       }
+    }
+    return NULL;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * StringCreate --
+ *
+ *     Given a hash table with string keys, and a string key, find
+ *     the entry with a matching key.  If there is no matching entry,
+ *     then create a new entry that does match.
+ *
+ * Results:
+ *     The return value is a pointer to the matching entry.  If this
+ *     is a newly-created entry, then *newPtr will be set to a non-zero
+ *     value;  otherwise *newPtr will be set to 0.  If this is a new
+ *     entry the value stored in the entry will initially be 0.
+ *
+ * Side effects:
+ *     A new entry may be added to the hash table.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_HashEntry *
+StringCreate(tablePtr, key, newPtr)
+    Tcl_HashTable *tablePtr;   /* Table in which to lookup entry. */
+    char *key;                 /* Key to use to find or create matching
+                                * entry. */
+    int *newPtr;               /* Store info here telling whether a new
+                                * entry was created. */
+{
+    register Tcl_HashEntry *hPtr;
+    register char *p1, *p2;
+    int index;
+
+    index = HashString(key) & tablePtr->mask;
+
+    /*
+     * Search all of the entries in this bucket.
+     */
+
+    for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
+           hPtr = hPtr->nextPtr) {
+       for (p1 = key, p2 = hPtr->key.string; ; p1++, p2++) {
+           if (*p1 != *p2) {
+               break;
+           }
+           if (*p1 == '\0') {
+               *newPtr = 0;
+               return hPtr;
+           }
+       }
+    }
+
+    /*
+     * Entry not found.  Add a new one to the bucket.
+     */
+
+    *newPtr = 1;
+    hPtr = (Tcl_HashEntry *) ckalloc((unsigned)
+           (sizeof(Tcl_HashEntry) + strlen(key) - (sizeof(hPtr->key) -1)));
+    hPtr->tablePtr = tablePtr;
+    hPtr->bucketPtr = &(tablePtr->buckets[index]);
+    hPtr->nextPtr = *hPtr->bucketPtr;
+    hPtr->clientData = 0;
+    strcpy(hPtr->key.string, key);
+    *hPtr->bucketPtr = hPtr;
+    tablePtr->numEntries++;
+
+    /*
+     * If the table has exceeded a decent size, rebuild it with many
+     * more buckets.
+     */
+
+    if (tablePtr->numEntries >= tablePtr->rebuildSize) {
+       RebuildTable(tablePtr);
+    }
+    return hPtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * OneWordFind --
+ *
+ *     Given a hash table with one-word keys, and a one-word key, find
+ *     the entry with a matching key.
+ *
+ * Results:
+ *     The return value is a token for the matching entry in the
+ *     hash table, or NULL if there was no matching entry.
+ *
+ * Side effects:
+ *     None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_HashEntry *
+OneWordFind(tablePtr, key)
+    Tcl_HashTable *tablePtr;   /* Table in which to lookup entry. */
+    register char *key;                /* Key to use to find matching entry. */
+{
+    register Tcl_HashEntry *hPtr;
+    int index;
+
+    index = RANDOM_INDEX(tablePtr, key);
+
+    /*
+     * Search all of the entries in the appropriate bucket.
+     */
+
+    for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
+           hPtr = hPtr->nextPtr) {
+       if (hPtr->key.oneWordValue == key) {
+           return hPtr;
+       }
+    }
+    return NULL;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * OneWordCreate --
+ *
+ *     Given a hash table with one-word keys, and a one-word key, find
+ *     the entry with a matching key.  If there is no matching entry,
+ *     then create a new entry that does match.
+ *
+ * Results:
+ *     The return value is a pointer to the matching entry.  If this
+ *     is a newly-created entry, then *newPtr will be set to a non-zero
+ *     value;  otherwise *newPtr will be set to 0.  If this is a new
+ *     entry the value stored in the entry will initially be 0.
+ *
+ * Side effects:
+ *     A new entry may be added to the hash table.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_HashEntry *
+OneWordCreate(tablePtr, key, newPtr)
+    Tcl_HashTable *tablePtr;   /* Table in which to lookup entry. */
+    register char *key;                /* Key to use to find or create matching
+                                * entry. */
+    int *newPtr;               /* Store info here telling whether a new
+                                * entry was created. */
+{
+    register Tcl_HashEntry *hPtr;
+    int index;
+
+    index = RANDOM_INDEX(tablePtr, key);
+
+    /*
+     * Search all of the entries in this bucket.
+     */
+
+    for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
+           hPtr = hPtr->nextPtr) {
+       if (hPtr->key.oneWordValue == key) {
+           *newPtr = 0;
+           return hPtr;
+       }
+    }
+
+    /*
+     * Entry not found.  Add a new one to the bucket.
+     */
+
+    *newPtr = 1;
+    hPtr = (Tcl_HashEntry *) ckalloc(sizeof(Tcl_HashEntry));
+    hPtr->tablePtr = tablePtr;
+    hPtr->bucketPtr = &(tablePtr->buckets[index]);
+    hPtr->nextPtr = *hPtr->bucketPtr;
+    hPtr->clientData = 0;
+    hPtr->key.oneWordValue = key;
+    *hPtr->bucketPtr = hPtr;
+    tablePtr->numEntries++;
+
+    /*
+     * If the table has exceeded a decent size, rebuild it with many
+     * more buckets.
+     */
+
+    if (tablePtr->numEntries >= tablePtr->rebuildSize) {
+       RebuildTable(tablePtr);
+    }
+    return hPtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ArrayFind --
+ *
+ *     Given a hash table with array-of-int keys, and a key, find
+ *     the entry with a matching key.
+ *
+ * Results:
+ *     The return value is a token for the matching entry in the
+ *     hash table, or NULL if there was no matching entry.
+ *
+ * Side effects:
+ *     None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_HashEntry *
+ArrayFind(tablePtr, key)
+    Tcl_HashTable *tablePtr;   /* Table in which to lookup entry. */
+    char *key;                 /* Key to use to find matching entry. */
+{
+    register Tcl_HashEntry *hPtr;
+    int *arrayPtr = (int *) key;
+    register int *iPtr1, *iPtr2;
+    int index, count;
+
+    for (index = 0, count = tablePtr->keyType, iPtr1 = arrayPtr;
+           count > 0; count--, iPtr1++) {
+       index += *iPtr1;
+    }
+    index = RANDOM_INDEX(tablePtr, index);
+
+    /*
+     * Search all of the entries in the appropriate bucket.
+     */
+
+    for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
+           hPtr = hPtr->nextPtr) {
+       for (iPtr1 = arrayPtr, iPtr2 = hPtr->key.words,
+               count = tablePtr->keyType; ; count--, iPtr1++, iPtr2++) {
+           if (count == 0) {
+               return hPtr;
+           }
+           if (*iPtr1 != *iPtr2) {
+               break;
+           }
+       }
+    }
+    return NULL;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ArrayCreate --
+ *
+ *     Given a hash table with one-word keys, and a one-word key, find
+ *     the entry with a matching key.  If there is no matching entry,
+ *     then create a new entry that does match.
+ *
+ * Results:
+ *     The return value is a pointer to the matching entry.  If this
+ *     is a newly-created entry, then *newPtr will be set to a non-zero
+ *     value;  otherwise *newPtr will be set to 0.  If this is a new
+ *     entry the value stored in the entry will initially be 0.
+ *
+ * Side effects:
+ *     A new entry may be added to the hash table.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_HashEntry *
+ArrayCreate(tablePtr, key, newPtr)
+    Tcl_HashTable *tablePtr;   /* Table in which to lookup entry. */
+    register char *key;                /* Key to use to find or create matching
+                                * entry. */
+    int *newPtr;               /* Store info here telling whether a new
+                                * entry was created. */
+{
+    register Tcl_HashEntry *hPtr;
+    int *arrayPtr = (int *) key;
+    register int *iPtr1, *iPtr2;
+    int index, count;
+
+    for (index = 0, count = tablePtr->keyType, iPtr1 = arrayPtr;
+           count > 0; count--, iPtr1++) {
+       index += *iPtr1;
+    }
+    index = RANDOM_INDEX(tablePtr, index);
+
+    /*
+     * Search all of the entries in the appropriate bucket.
+     */
+
+    for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
+           hPtr = hPtr->nextPtr) {
+       for (iPtr1 = arrayPtr, iPtr2 = hPtr->key.words,
+               count = tablePtr->keyType; ; count--, iPtr1++, iPtr2++) {
+           if (count == 0) {
+               *newPtr = 0;
+               return hPtr;
+           }
+           if (*iPtr1 != *iPtr2) {
+               break;
+           }
+       }
+    }
+
+    /*
+     * Entry not found.  Add a new one to the bucket.
+     */
+
+    *newPtr = 1;
+    hPtr = (Tcl_HashEntry *) ckalloc((unsigned) (sizeof(Tcl_HashEntry)
+           + (tablePtr->keyType*sizeof(int)) - 4));
+    hPtr->tablePtr = tablePtr;
+    hPtr->bucketPtr = &(tablePtr->buckets[index]);
+    hPtr->nextPtr = *hPtr->bucketPtr;
+    hPtr->clientData = 0;
+    for (iPtr1 = arrayPtr, iPtr2 = hPtr->key.words, count = tablePtr->keyType;
+           count > 0; count--, iPtr1++, iPtr2++) {
+       *iPtr2 = *iPtr1;
+    }
+    *hPtr->bucketPtr = hPtr;
+    tablePtr->numEntries++;
+
+    /*
+     * If the table has exceeded a decent size, rebuild it with many
+     * more buckets.
+     */
+
+    if (tablePtr->numEntries >= tablePtr->rebuildSize) {
+       RebuildTable(tablePtr);
+    }
+    return hPtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * BogusFind --
+ *
+ *     This procedure is invoked when an Tcl_FindHashEntry is called
+ *     on a table that has been deleted.
+ *
+ * Results:
+ *     If panic returns (which it shouldn't) this procedure returns
+ *     NULL.
+ *
+ * Side effects:
+ *     Generates a panic.
+ *
+ *----------------------------------------------------------------------
+ */
+
+       /* ARGSUSED */
+static Tcl_HashEntry *
+BogusFind(tablePtr, key)
+    Tcl_HashTable *tablePtr;   /* Table in which to lookup entry. */
+    char *key;                 /* Key to use to find matching entry. */
+{
+    panic("called Tcl_FindHashEntry on deleted table");
+    return NULL;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * BogusCreate --
+ *
+ *     This procedure is invoked when an Tcl_CreateHashEntry is called
+ *     on a table that has been deleted.
+ *
+ * Results:
+ *     If panic returns (which it shouldn't) this procedure returns
+ *     NULL.
+ *
+ * Side effects:
+ *     Generates a panic.
+ *
+ *----------------------------------------------------------------------
+ */
+
+       /* ARGSUSED */
+static Tcl_HashEntry *
+BogusCreate(tablePtr, key, newPtr)
+    Tcl_HashTable *tablePtr;   /* Table in which to lookup entry. */
+    char *key;                 /* Key to use to find or create matching
+                                * entry. */
+    int *newPtr;               /* Store info here telling whether a new
+                                * entry was created. */
+{
+    panic("called Tcl_CreateHashEntry on deleted table");
+    return NULL;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * RebuildTable --
+ *
+ *     This procedure is invoked when the ratio of entries to hash
+ *     buckets becomes too large.  It creates a new table with a
+ *     larger bucket array and moves all of the entries into the
+ *     new table.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Memory gets reallocated and entries get re-hashed to new
+ *     buckets.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+RebuildTable(tablePtr)
+    register Tcl_HashTable *tablePtr;  /* Table to enlarge. */
+{
+    int oldSize, count, index;
+    Tcl_HashEntry **oldBuckets;
+    register Tcl_HashEntry **oldChainPtr, **newChainPtr;
+    register Tcl_HashEntry *hPtr;
+
+    oldSize = tablePtr->numBuckets;
+    oldBuckets = tablePtr->buckets;
+
+    /*
+     * Allocate and initialize the new bucket array, and set up
+     * hashing constants for new array size.
+     */
+
+    tablePtr->numBuckets *= 4;
+    tablePtr->buckets = (Tcl_HashEntry **) ckalloc((unsigned)
+           (tablePtr->numBuckets * sizeof(Tcl_HashEntry *)));
+    for (count = tablePtr->numBuckets, newChainPtr = tablePtr->buckets;
+           count > 0; count--, newChainPtr++) {
+       *newChainPtr = NULL;
+    }
+    tablePtr->rebuildSize *= 4;
+    tablePtr->downShift -= 2;
+    tablePtr->mask = (tablePtr->mask << 2) + 3;
+
+    /*
+     * Rehash all of the existing entries into the new bucket array.
+     */
+
+    for (oldChainPtr = oldBuckets; oldSize > 0; oldSize--, oldChainPtr++) {
+       for (hPtr = *oldChainPtr; hPtr != NULL; hPtr = *oldChainPtr) {
+           *oldChainPtr = hPtr->nextPtr;
+           if (tablePtr->keyType == TCL_STRING_KEYS) {
+               index = HashString(hPtr->key.string) & tablePtr->mask;
+           } else if (tablePtr->keyType == TCL_ONE_WORD_KEYS) {
+               index = RANDOM_INDEX(tablePtr, hPtr->key.oneWordValue);
+           } else {
+               register int *iPtr;
+               int count;
+
+               for (index = 0, count = tablePtr->keyType,
+                       iPtr = hPtr->key.words; count > 0; count--, iPtr++) {
+                   index += *iPtr;
+               }
+               index = RANDOM_INDEX(tablePtr, index);
+           }
+           hPtr->bucketPtr = &(tablePtr->buckets[index]);
+           hPtr->nextPtr = *hPtr->bucketPtr;
+           *hPtr->bucketPtr = hPtr;
+       }
+    }
+
+    /*
+     * Free up the old bucket array, if it was dynamically allocated.
+     */
+
+    if (oldBuckets != tablePtr->staticBuckets) {
+       ckfree((char *) oldBuckets);
+    }
+}
diff --git a/examples/tiny-authorizer.c b/examples/tiny-authorizer.c
new file mode 100644 (file)
index 0000000..799a794
--- /dev/null
@@ -0,0 +1,60 @@
+/* 
+ * tiny-authorizer.c --
+ *
+ *     FastCGI example Authorizer program using fcgi_stdio library
+ *
+ *
+ * Be sure to run this program in a region with CGIPassword
+ * in order to get REMOTE_USER and REMOTE_PASSWD in place
+ * of HTTP_AUTHORIZATION.
+ *
+ * Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] = "$Id: tiny-authorizer.c,v 1.1 1997/09/16 15:36:28 stanleyg Exp $";
+#endif /* not lint */
+
+#include "fcgi_stdio.h"
+#include <stdlib.h>
+#include <string.h>
+
+void main(void)
+{
+    char *user, *password;
+    user = getenv("USER");
+    if(user == NULL) {
+        user = "doe";
+    }
+    password = getenv("PASSWORD");
+    if(password == NULL) {
+        password = "xxxx";
+    }
+    
+    while(FCGI_Accept() >= 0) {
+        char *remoteUser, *remotePassword;
+        remoteUser = getenv("REMOTE_USER");
+        remotePassword = getenv("REMOTE_PASSWD");
+        if((remoteUser == NULL) || (remotePassword == NULL) ||
+           strcmp(remoteUser, user) || strcmp(remotePassword, password)) {
+           printf("Status: 401 Unauthorized\r\n"
+                  "WWW-Authenticate: Basic realm=\"Test\"\r\n"
+                  "\r\n");
+        
+       } else {
+            char *processId = getenv("QUERY_STRING");
+            if(processId == NULL || strlen(processId) == 0) {
+                processId = "0";
+           }
+            printf("Status: 200 OK\r\n"
+                   "Variable-AUTH_TYPE: Basic\r\n"
+                   "Variable-REMOTE_PASSWD:\r\n"
+                   "Variable-PROCESS_ID: %s\r\n"
+                   "\r\n", processId);
+        }
+    }
+}
diff --git a/examples/tiny-cgi.c b/examples/tiny-cgi.c
new file mode 100644 (file)
index 0000000..a2d871e
--- /dev/null
@@ -0,0 +1,30 @@
+/* 
+ * tiny-cgi.c --
+ *
+ *     CGI example program
+ *
+ *
+ * Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] = "$Id: tiny-cgi.c,v 1.1 1997/09/16 15:36:28 stanleyg Exp $";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+void main(void)
+{
+    int count = 0;
+    printf("Content-type: text/html\r\n"
+           "\r\n"
+           "<title>CGI Hello!</title>"
+           "<h1>CGI Hello!</h1>"
+           "Request number %d running on host <i>%s</i>\n",
+           ++count, getenv("SERVER_NAME"));
+}
diff --git a/examples/tiny-fcgi.c b/examples/tiny-fcgi.c
new file mode 100644 (file)
index 0000000..325c349
--- /dev/null
@@ -0,0 +1,37 @@
+/* 
+ * tiny-fcgi.c --
+ *
+ *     FastCGI example program using fcgi_stdio library
+ *
+ *
+ * Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] = "$Id: tiny-fcgi.c,v 1.1 1997/09/16 15:36:28 stanleyg Exp $";
+#endif /* not lint */
+
+#include "fcgi_stdio.h"
+#include <stdlib.h>
+
+#ifdef _WIN32
+#include <process.h>
+#endif
+
+void main(void)
+{
+    int count = 0;
+    while(FCGI_Accept() >= 0) {
+        printf("Content-type: text/html\r\n"
+               "\r\n"
+               "<title>FastCGI Hello! (C, fcgi_stdio library)</title>"
+               "<h1>FastCGI Hello! (C, fcgi_stdio library)</h1>"
+               "Request number %d running on host <i>%s</i>  "
+               "Process ID: %d\n",
+               ++count, getenv("SERVER_NAME"), getpid());
+    }
+}
diff --git a/examples/tiny-fcgi.cgi b/examples/tiny-fcgi.cgi
new file mode 100755 (executable)
index 0000000..55e2007
--- /dev/null
@@ -0,0 +1,14 @@
+#!../cgi-fcgi/cgi-fcgi -f
+-connect sockets/tiny-fcgi ./tiny-fcgi 1
+#
+# tiny-fcgi.cgi --
+#
+#        Invoke FastCGI example program using cgi-fcgi
+#
+# Copyright (c) 1996 Open Market, Inc.
+#
+# See the file "LICENSE.TERMS" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+#  $Id: tiny-fcgi.cgi,v 1.1 1997/09/16 15:36:29 stanleyg Exp $
+#
diff --git a/examples/tiny-fcgi.mak b/examples/tiny-fcgi.mak
new file mode 100644 (file)
index 0000000..f02c4c1
--- /dev/null
@@ -0,0 +1,221 @@
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+!IF "$(CFG)" == ""
+CFG=tinyfcgi - Win32 Debug
+!MESSAGE No configuration specified.  Defaulting to tinyfcgi - Win32 Debug.
+!ENDIF 
+
+!IF "$(CFG)" != "tinyfcgi - Win32 Release" && "$(CFG)" !=\
+ "tinyfcgi - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line.  For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "tiny-fcgi.mak" CFG="tinyfcgi - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "tinyfcgi - Win32 Release" (based on\
+ "Win32 (x86) Console Application")
+!MESSAGE "tinyfcgi - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+!ERROR An invalid configuration is specified.
+!ENDIF 
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE 
+NULL=nul
+!ENDIF 
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "tinyfcgi - Win32 Debug"
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "tinyfcgi - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : "$(OUTDIR)\tiny-fcgi.exe"
+
+CLEAN : 
+       -@erase "$(INTDIR)\tiny-fcgi.obj"
+       -@erase "$(OUTDIR)\tiny-fcgi.exe"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MD /W3 /GX /O2 /I "..\include" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/tiny-fcgi.pch" /YX /Fo"$(INTDIR)/" /c 
+CPP_OBJS=.\Release/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/tiny-fcgi.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib  ..\libfcgi\Release\libfcgi.lib /nologo /subsystem:console /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib  ..\libfcgi\Release\libfcgi.lib /nologo /subsystem:console\
+ /incremental:no /pdb:"$(OUTDIR)/tiny-fcgi.pdb" /machine:I386\
+ /out:"$(OUTDIR)/tiny-fcgi.exe" 
+LINK32_OBJS= \
+       "$(INTDIR)\tiny-fcgi.obj"
+
+"$(OUTDIR)\tiny-fcgi.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF  "$(CFG)" == "tinyfcgi - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : "$(OUTDIR)\tiny-fcgi.exe"
+
+CLEAN : 
+       -@erase "$(INTDIR)\tiny-fcgi.obj"
+       -@erase "$(INTDIR)\vc40.idb"
+       -@erase "$(INTDIR)\vc40.pdb"
+       -@erase "$(OUTDIR)\tiny-fcgi.exe"
+       -@erase "$(OUTDIR)\tiny-fcgi.ilk"
+       -@erase "$(OUTDIR)\tiny-fcgi.pdb"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MD /W3 /Gm /GX /Zi /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MD /W3 /Gm /GX /Zi /Od /I "..\include" /D "WIN32" /D "_DEBUG"\
+ /D "_CONSOLE" /Fp"$(INTDIR)/tiny-fcgi.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/"\
+ /c 
+CPP_OBJS=.\Debug/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/tiny-fcgi.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib  ..\libfcgi\Debug\libfcgi.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib  ..\libfcgi\Debug\libfcgi.lib /nologo /subsystem:console\
+ /incremental:yes /pdb:"$(OUTDIR)/tiny-fcgi.pdb" /debug /machine:I386\
+ /out:"$(OUTDIR)/tiny-fcgi.exe" 
+LINK32_OBJS= \
+       "$(INTDIR)\tiny-fcgi.obj"
+
+"$(OUTDIR)\tiny-fcgi.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF 
+
+.c{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.c{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+################################################################################
+# Begin Target
+
+# Name "tinyfcgi - Win32 Release"
+# Name "tinyfcgi - Win32 Debug"
+
+!IF  "$(CFG)" == "tinyfcgi - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "tinyfcgi - Win32 Debug"
+
+!ENDIF 
+
+################################################################################
+# Begin Source File
+
+SOURCE=".\tiny-fcgi.c"
+
+!IF  "$(CFG)" == "tinyfcgi - Win32 Release"
+
+DEP_CPP_TINY_=\
+       "..\include\fcgi_stdio.h"\
+       "..\include\fcgiapp.h"\
+       ".\*"\
+       {$(INCLUDE)}"\sys\types.h"\
+       
+NODEP_CPP_TINY_=\
+       "..\include\fcgi_config.h"\
+       
+
+"$(INTDIR)\tiny-fcgi.obj" : $(SOURCE) $(DEP_CPP_TINY_) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "tinyfcgi - Win32 Debug"
+
+DEP_CPP_TINY_=\
+       "..\include\fcgi_config.h"\
+       "..\include\fcgi_stdio.h"\
+       "..\include\fcgiapp.h"\
+       {$(INCLUDE)}"\sys\types.h"\
+       
+
+"$(INTDIR)\tiny-fcgi.obj" : $(SOURCE) $(DEP_CPP_TINY_) "$(INTDIR)"
+
+
+!ENDIF 
+
+# End Source File
+# End Target
+# End Project
+################################################################################
diff --git a/examples/tiny-fcgi2.c b/examples/tiny-fcgi2.c
new file mode 100644 (file)
index 0000000..4b27a9a
--- /dev/null
@@ -0,0 +1,38 @@
+/* 
+ * tiny-fcgi2.c --
+ *
+ *     FastCGI example program using fcgiapp library
+ *
+ *
+ * Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] = "$Id: tiny-fcgi2.c,v 1.1 1997/09/16 15:36:29 stanleyg Exp $";
+#endif /* not lint */
+
+#include "fcgiapp.h"
+
+#ifdef _WIN32
+#include <process.h>
+#endif
+
+void main(void)
+{
+    FCGX_Stream *in, *out, *err;
+    FCGX_ParamArray envp;
+    int count = 0;
+    while(FCGX_Accept(&in, &out, &err, &envp) >= 0)
+        FCGX_FPrintF(out,
+               "Content-type: text/html\r\n"
+               "\r\n"
+               "<title>FastCGI Hello! (C, fcgiapp library)</title>"
+               "<h1>FastCGI Hello! (C, fcgiapp library)</h1>"
+               "Request number %d running on host <i>%s</i>  "
+               "Process ID: %d\n",
+               ++count, FCGX_GetParam("SERVER_NAME", envp), getpid());
+}
diff --git a/examples/tiny-fcgi2.cgi b/examples/tiny-fcgi2.cgi
new file mode 100755 (executable)
index 0000000..fb57ffb
--- /dev/null
@@ -0,0 +1,14 @@
+#!../cgi-fcgi/cgi-fcgi -f
+-connect sockets/tiny-fcgi2 ./tiny-fcgi2 1
+#
+# tiny-fcgi2.cgi --
+#
+#        Invoke FastCGI example program using cgi-fcgi
+#
+# Copyright (c) 1996 Open Market, Inc.
+#
+# See the file "LICENSE.TERMS" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+#  $Id: tiny-fcgi2.cgi,v 1.1 1997/09/16 15:36:29 stanleyg Exp $
+#
diff --git a/examples/tiny-fcgi2.mak b/examples/tiny-fcgi2.mak
new file mode 100644 (file)
index 0000000..561ed02
--- /dev/null
@@ -0,0 +1,199 @@
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+!IF "$(CFG)" == ""
+CFG=tinyfcgi - Win32 Debug
+!MESSAGE No configuration specified.  Defaulting to tinyfcgi - Win32 Debug.
+!ENDIF 
+
+!IF "$(CFG)" != "tinyfcgi - Win32 Release" && "$(CFG)" !=\
+ "tinyfcgi - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line.  For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "tiny-fcgi2.mak" CFG="tinyfcgi - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "tinyfcgi - Win32 Release" (based on\
+ "Win32 (x86) Console Application")
+!MESSAGE "tinyfcgi - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+!ERROR An invalid configuration is specified.
+!ENDIF 
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE 
+NULL=nul
+!ENDIF 
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "tinyfcgi - Win32 Debug"
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "tinyfcgi - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : "$(OUTDIR)\tiny-fcgi2.exe"
+
+CLEAN : 
+       -@erase "$(INTDIR)\tiny-fcgi2.obj"
+       -@erase "$(OUTDIR)\tiny-fcgi2.exe"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MD /W3 /GX /O2 /I "..\include" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/tiny-fcgi2.pch" /YX /Fo"$(INTDIR)/" /c 
+CPP_OBJS=.\Release/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/tiny-fcgi2.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ..\libfcgi\Release\libfcgi.lib /nologo /subsystem:console /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib ..\libfcgi\Release\libfcgi.lib /nologo /subsystem:console\
+ /incremental:no /pdb:"$(OUTDIR)/tiny-fcgi2.pdb" /machine:I386\
+ /out:"$(OUTDIR)/tiny-fcgi2.exe" 
+LINK32_OBJS= \
+       "$(INTDIR)\tiny-fcgi2.obj"
+
+"$(OUTDIR)\tiny-fcgi2.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF  "$(CFG)" == "tinyfcgi - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : "$(OUTDIR)\tiny-fcgi2.exe"
+
+CLEAN : 
+       -@erase "$(INTDIR)\tiny-fcgi2.obj"
+       -@erase "$(INTDIR)\vc40.idb"
+       -@erase "$(INTDIR)\vc40.pdb"
+       -@erase "$(OUTDIR)\tiny-fcgi2.exe"
+       -@erase "$(OUTDIR)\tiny-fcgi2.ilk"
+       -@erase "$(OUTDIR)\tiny-fcgi2.pdb"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MD /W3 /Gm /GX /Zi /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MD /W3 /Gm /GX /Zi /Od /I "..\include" /D "WIN32" /D "_DEBUG"\
+ /D "_CONSOLE" /Fp"$(INTDIR)/tiny-fcgi2.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/"\
+ /c 
+CPP_OBJS=.\Debug/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/tiny-fcgi2.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ..\libfcgi\Debug\libfcgi.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib ..\libfcgi\Debug\libfcgi.lib /nologo /subsystem:console\
+ /incremental:yes /pdb:"$(OUTDIR)/tiny-fcgi2.pdb" /debug /machine:I386\
+ /out:"$(OUTDIR)/tiny-fcgi2.exe" 
+LINK32_OBJS= \
+       "$(INTDIR)\tiny-fcgi2.obj"
+
+"$(OUTDIR)\tiny-fcgi2.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF 
+
+.c{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.c{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+################################################################################
+# Begin Target
+
+# Name "tinyfcgi - Win32 Release"
+# Name "tinyfcgi - Win32 Debug"
+
+!IF  "$(CFG)" == "tinyfcgi - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "tinyfcgi - Win32 Debug"
+
+!ENDIF 
+
+################################################################################
+# Begin Source File
+
+SOURCE=".\tiny-fcgi2.c"
+DEP_CPP_TINY_=\
+       "..\include\fcgi_config.h"\
+       "..\include\fcgiapp.h"\
+       
+
+"$(INTDIR)\tiny-fcgi2.obj" : $(SOURCE) $(DEP_CPP_TINY_) "$(INTDIR)"
+
+
+# End Source File
+# End Target
+# End Project
+################################################################################
diff --git a/examples/tiny-fcgi2_nt.fcgi b/examples/tiny-fcgi2_nt.fcgi
new file mode 100644 (file)
index 0000000..0747e9a
--- /dev/null
@@ -0,0 +1,18 @@
+#!../cgi-fcgi/cgi-fcgi -f
+-connect sockets_tiny-fcgi2 ./tiny-fcgi2.exe 1
+#
+# tiny-fcgi2_nt.fcgi --
+#
+#        Invoke FastCGI example program using cgi-fcgi
+#
+# Copyright (c) 1996 Open Market, Inc.
+#
+# See the file "LICENSE.TERMS" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+#  $Id: tiny-fcgi2_nt.fcgi,v 1.1 1997/09/16 15:36:29 stanleyg Exp $
+#
+#
+# This file is intended to be used with the cgi-fcgi.exe application on
+# windows NT.
+#
diff --git a/examples/tiny-fcgi_nt.fcgi b/examples/tiny-fcgi_nt.fcgi
new file mode 100644 (file)
index 0000000..2c49a56
--- /dev/null
@@ -0,0 +1,18 @@
+#!../cgi-fcgi/cgi-fcgi -f
+-connect sockets_tiny-fcgi ./tiny-fcgi.exe 1
+#
+# tiny-fcgi_nt.fcgi --
+#
+#        Invoke FastCGI example program using cgi-fcgi
+#
+# Copyright (c) 1996 Open Market, Inc.
+#
+# See the file "LICENSE.TERMS" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+#  $Id: tiny-fcgi_nt.fcgi,v 1.1 1997/09/16 15:36:29 stanleyg Exp $
+#
+#
+# This file is intended to be used with the cgi-fcgi.exe application on
+# windows NT.
+#
diff --git a/examples/tiny-perl-fcgi b/examples/tiny-perl-fcgi
new file mode 100755 (executable)
index 0000000..2faf33d
--- /dev/null
@@ -0,0 +1,30 @@
+#!./perl
+#
+#  tiny-perl-fcgi --
+# 
+#      Perl-5 FastCGI example program
+# 
+# Copyright (c) 1996 Open Market, Inc.
+#
+# See the file "LICENSE.TERMS" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+# 
+#  $Id: tiny-perl-fcgi,v 1.1 1997/09/16 15:36:29 stanleyg Exp $
+#
+
+
+use FCGI;
+#
+# Work around the "empty initial environment" bug
+#
+while (($ignore) = each %ENV) {
+}
+
+$count = 0;
+while(FCGI::accept() >= 0) {
+    print("Content-type: text/html\r\n\r\n",
+          "<title>FastCGI Hello! (Perl)</title>\n",
+          "<h1>FastCGI Hello! (Perl)</h1>\n",
+          "Request number ", ++$count,
+          " running on host <i>$ENV{'SERVER_NAME'}</i>\n");
+}
diff --git a/examples/tiny-tcl-fcgi b/examples/tiny-tcl-fcgi
new file mode 100755 (executable)
index 0000000..650fe48
--- /dev/null
@@ -0,0 +1,22 @@
+#!./tclsh
+#
+#  tiny-tcl-fcgi --
+# 
+#      Tcl FastCGI example program
+# 
+# Copyright (c) 1996 Open Market, Inc.
+#
+# See the file "LICENSE.TERMS" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+# 
+#  $Id: tiny-tcl-fcgi,v 1.1 1997/09/16 15:36:29 stanleyg Exp $
+#
+
+set count 0 
+while {[FCGI_Accept] >= 0 } {
+    incr count
+    puts -nonewline "Content-type: text/html\r\n\r\n"
+    puts "<title>FastCGI Hello! (Tcl)</title>"
+    puts "<h1>FastCGI Hello! (Tcl)</h1>"
+    puts "Request number $count running on host <i>$env(SERVER_NAME)</i>"
+}
diff --git a/images/aplib-hd.gif b/images/aplib-hd.gif
new file mode 100644 (file)
index 0000000..8198890
Binary files /dev/null and b/images/aplib-hd.gif differ
diff --git a/images/divider.gif b/images/divider.gif
new file mode 100644 (file)
index 0000000..0a8e0a4
Binary files /dev/null and b/images/divider.gif differ
diff --git a/images/fcgi-hd.gif b/images/fcgi-hd.gif
new file mode 100644 (file)
index 0000000..5eab1c4
Binary files /dev/null and b/images/fcgi-hd.gif differ
diff --git a/images/mail-hd.gif b/images/mail-hd.gif
new file mode 100644 (file)
index 0000000..4ea84d2
Binary files /dev/null and b/images/mail-hd.gif differ
diff --git a/images/navbar.gif b/images/navbar.gif
new file mode 100644 (file)
index 0000000..c2ada2e
Binary files /dev/null and b/images/navbar.gif differ
diff --git a/images/serv-hd.gif b/images/serv-hd.gif
new file mode 100644 (file)
index 0000000..791c723
Binary files /dev/null and b/images/serv-hd.gif differ
diff --git a/images/words-hd.gif b/images/words-hd.gif
new file mode 100644 (file)
index 0000000..bd7a0ff
Binary files /dev/null and b/images/words-hd.gif differ
diff --git a/include/fastcgi.h b/include/fastcgi.h
new file mode 100644 (file)
index 0000000..66d8ec7
--- /dev/null
@@ -0,0 +1,136 @@
+/* 
+ * fastcgi.h --
+ *
+ *     Defines for the FastCGI protocol.
+ *
+ *
+ * Copyright (c) 1995-1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * $Id: fastcgi.h,v 1.1 1997/09/16 15:36:32 stanleyg Exp $
+ */
+
+#ifndef _FASTCGI_H
+#define _FASTCGI_H
+
+/*
+ * Listening socket file number
+ */
+#define FCGI_LISTENSOCK_FILENO 0
+
+typedef struct {
+    unsigned char version;
+    unsigned char type;
+    unsigned char requestIdB1;
+    unsigned char requestIdB0;
+    unsigned char contentLengthB1;
+    unsigned char contentLengthB0;
+    unsigned char paddingLength;
+    unsigned char reserved;
+} FCGI_Header;
+
+#define FCGI_MAX_LENGTH 0xffff
+
+/*
+ * Number of bytes in a FCGI_Header.  Future versions of the protocol
+ * will not reduce this number.
+ */
+#define FCGI_HEADER_LEN  8
+
+/*
+ * Value for version component of FCGI_Header
+ */
+#define FCGI_VERSION_1           1
+
+/*
+ * Values for type component of FCGI_Header
+ */
+#define FCGI_BEGIN_REQUEST       1
+#define FCGI_ABORT_REQUEST       2
+#define FCGI_END_REQUEST         3
+#define FCGI_PARAMS              4
+#define FCGI_STDIN               5
+#define FCGI_STDOUT              6
+#define FCGI_STDERR              7
+#define FCGI_DATA                8
+#define FCGI_GET_VALUES          9
+#define FCGI_GET_VALUES_RESULT  10
+#define FCGI_UNKNOWN_TYPE       11
+#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)
+
+/*
+ * Value for requestId component of FCGI_Header
+ */
+#define FCGI_NULL_REQUEST_ID     0
+
+
+typedef struct {
+    unsigned char roleB1;
+    unsigned char roleB0;
+    unsigned char flags;
+    unsigned char reserved[5];
+} FCGI_BeginRequestBody;
+
+typedef struct {
+    FCGI_Header header;
+    FCGI_BeginRequestBody body;
+} FCGI_BeginRequestRecord;
+
+/*
+ * Mask for flags component of FCGI_BeginRequestBody
+ */
+#define FCGI_KEEP_CONN  1
+
+/*
+ * Values for role component of FCGI_BeginRequestBody
+ */
+#define FCGI_RESPONDER  1
+#define FCGI_AUTHORIZER 2
+#define FCGI_FILTER     3
+
+
+typedef struct {
+    unsigned char appStatusB3;
+    unsigned char appStatusB2;
+    unsigned char appStatusB1;
+    unsigned char appStatusB0;
+    unsigned char protocolStatus;
+    unsigned char reserved[3];
+} FCGI_EndRequestBody;
+
+typedef struct {
+    FCGI_Header header;
+    FCGI_EndRequestBody body;
+} FCGI_EndRequestRecord;
+
+/*
+ * Values for protocolStatus component of FCGI_EndRequestBody
+ */
+#define FCGI_REQUEST_COMPLETE 0
+#define FCGI_CANT_MPX_CONN    1
+#define FCGI_OVERLOADED       2
+#define FCGI_UNKNOWN_ROLE     3
+
+
+/*
+ * Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT records
+ */
+#define FCGI_MAX_CONNS  "FCGI_MAX_CONNS"
+#define FCGI_MAX_REQS   "FCGI_MAX_REQS"
+#define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS"
+
+
+typedef struct {
+    unsigned char type;    
+    unsigned char reserved[7];
+} FCGI_UnknownTypeBody;
+
+typedef struct {
+    FCGI_Header header;
+    FCGI_UnknownTypeBody body;
+} FCGI_UnknownTypeRecord;
+
+#endif /* _FASTCGI_H */
+
diff --git a/include/fcgi_config.h.in b/include/fcgi_config.h.in
new file mode 100644 (file)
index 0000000..9996fe3
--- /dev/null
@@ -0,0 +1,101 @@
+/* include/fcgi_config.h.in.  Generated automatically from configure.in by autoheader.  */
+
+/* Define if on AIX 3.
+   System headers sometimes define this.
+   We just want to avoid a redefinition error message.  */
+#ifndef _ALL_SOURCE
+#undef _ALL_SOURCE
+#endif
+
+/* Define if you have the ANSI C header files.  */
+#undef STDC_HEADERS
+
+/* Define to type of ssize_t, if not on this platform.  */
+#undef ssize_t
+
+/* Define if you have the <sys/select.h> include file */
+#undef HAVE_SYS_SELECT_H
+
+/* Define if you don't have fd_set */
+#undef NO_FD_SET
+
+/* Define if want to compile with ASSERT() statements */
+#undef WITH_ASSERT
+
+/* Define if want to compile with additional debugging code */
+#undef WITH_DEBUG
+
+/* Define if want to compile with hooks for testing */
+#undef WITH_TEST
+
+/* Define if sockaddr_un in <sys/un.h> contains the sun_len component */
+#undef HAVE_SOCKADDR_UN_SUN_LEN
+
+/* Define if we have f{set,get}pos functions */
+#undef HAVE_FPOS
+
+/* Define if we have strerror */
+#undef HAVE_STRERROR
+
+/* Define if we have strtol */
+#undef HAVE_STRTOL
+
+/* Define if we need cross-process locking */
+#undef USE_LOCKING
+
+/* Define if va_arg(arg, long double) crashes the compiler. */
+#undef HAVE_VA_ARG_LONG_DOUBLE_BUG
+
+/* Don't know what this stuff is for */
+#undef HAVE_MATHLIB
+#undef WITH_DOMESTIC
+#undef WITH_EXPORT
+#undef WITH_GLOBAL
+
+/* The number of bytes in a int.  */
+#undef SIZEOF_INT
+
+/* The number of bytes in a long.  */
+#undef SIZEOF_LONG
+
+/* The number of bytes in a off_t.  */
+#undef SIZEOF_OFF_T
+
+/* The number of bytes in a size_t.  */
+#undef SIZEOF_SIZE_T
+
+/* The number of bytes in a unsigned int.  */
+#undef SIZEOF_UNSIGNED_INT
+
+/* The number of bytes in a unsigned long.  */
+#undef SIZEOF_UNSIGNED_LONG
+
+/* The number of bytes in a unsigned short.  */
+#undef SIZEOF_UNSIGNED_SHORT
+
+/* Define if you have the <limits.h> header file.  */
+#undef HAVE_LIMITS_H
+
+/* Define if you have the <unistd.h> header file.  */
+#undef HAVE_UNISTD_H
+
+/* Define if you have the <netdb.h> header file.  */
+#undef HAVE_NETDB_H
+
+/* Define if you have the <netinet/in.h> header file.  */
+#undef HAVE_NETINET_IN_H
+
+/* Define if you have the <windows.h> header file.  */
+#undef HAVE_WINDOWS_H
+
+/* Define if you have the <winsock.h> header file.  */
+#undef HAVE_WINSOCK_H
+
+/* Define if you have the <sys/socket.h> header file.  */
+#undef HAVE_SYS_SOCKET_H
+
+/* Define if you have the <strings.h> header file.  */
+#undef HAVE_STRINGS_H
+
+/* Define if you have the <sys/time.h> header file.  */
+#undef HAVE_SYS_TIME_H
diff --git a/include/fcgi_config_x86.h b/include/fcgi_config_x86.h
new file mode 100644 (file)
index 0000000..d3c8d67
--- /dev/null
@@ -0,0 +1,97 @@
+/* include/fcgi_config.h.  Generated automatically by configure.  */
+/* include/fcgi_config.h.in.  Generated automatically from configure.in by autoheader.  */
+
+/* Define if on AIX 3.
+   System headers sometimes define this.
+   We just want to avoid a redefinition error message.  */
+#ifndef _ALL_SOURCE
+/* #undef _ALL_SOURCE */
+#endif
+
+/* Define if you have the ANSI C header files.  */
+#define STDC_HEADERS 1
+
+/* Define to type of ssize_t, if not on this platform.  */
+#define ssize_t int
+
+/* Define if you have the <sys/select.h> include file */
+/* #undef HAVE_SYS_SELECT_H */
+
+/* Define if you don't have fd_set */
+#define NO_FD_SET 1
+
+/* Define if want to compile with ASSERT() statements */
+#define WITH_ASSERT 1
+
+/* Define if want to compile with additional debugging code */
+#define WITH_DEBUG 1
+
+/* Define if want to compile with hooks for testing */
+#define WITH_TEST 1
+
+/* Define if sockaddr_un in <sys/un.h> contains the sun_len component */
+/* #undef HAVE_SOCKADDR_UN_SUN_LEN */
+
+/* Define if we have f{set,get}pos functions */
+#define HAVE_FPOS 1
+
+/* Define if we need cross-process locking */
+/* #undef USE_LOCKING */
+
+/* Define if va_arg(arg, long double) crashes the compiler. */
+/* #undef HAVE_VA_ARG_LONG_DOUBLE_BUG */
+
+/* Don't know what this stuff is for */
+#define HAVE_MATHLIB 1
+/* #undef WITH_DOMESTIC */
+/* #undef WITH_EXPORT */
+/* #undef WITH_GLOBAL */
+
+/* The number of bytes in a int.  */
+#define SIZEOF_INT 4
+
+/* The number of bytes in a long.  */
+#define SIZEOF_LONG 4
+
+/* The number of bytes in a off_t.  */
+#define SIZEOF_OFF_T 0
+
+/* The number of bytes in a size_t.  */
+#define SIZEOF_SIZE_T 4
+
+/* The number of bytes in a unsigned int.  */
+#define SIZEOF_UNSIGNED_INT 4
+
+/* The number of bytes in a unsigned long.  */
+#define SIZEOF_UNSIGNED_LONG 4
+
+/* The number of bytes in a unsigned short.  */
+#define SIZEOF_UNSIGNED_SHORT 2
+
+/* Define if you have the <limits.h> header file.  */
+#define HAVE_LIMITS_H 1
+
+/* Define if you have the <unistd.h> header file.  */
+/* #undef HAVE_UNISTD_H */
+
+/* Define if you have the <netdb.h> header file.  */
+/* #undef HAVE_NETDB_H */
+
+/* Define if you have the <netinet/in.h> header file.  */
+/* #undef HAVE_NETINET_IN_H */
+
+/* Define if you have the <windows.h> header file.  */
+#define HAVE_WINDOWS_H 1
+
+/* Define if you have the <winsock.h> header file.  */
+#define HAVE_WINSOCK_H 1
+
+/* Define if you have the <sys/socket.h> header file.  */
+/* #undef HAVE_SYS_SOCKET_H */
+
+/* Define if you have the <strings.h> header file.  */
+/* #undef HAVE_STRINGS_H */
+
+/* Define if you have the <sys/time.h> header file.  */
+/* #undef HAVE_SYS_TIME_H */
+
diff --git a/include/fcgi_stdio.h b/include/fcgi_stdio.h
new file mode 100644 (file)
index 0000000..ae9243a
--- /dev/null
@@ -0,0 +1,240 @@
+/* 
+ * fcgi_stdio.h --
+ *
+ *      FastCGI-stdio compatibility package
+ *
+ *
+ * Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * $Id: fcgi_stdio.h,v 1.1 1997/09/16 15:36:32 stanleyg Exp $
+ */
+
+#ifndef _FCGI_STDIO
+#define _FCGI_STDIO 1
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "fcgiapp.h"
+
+#if defined (c_plusplus) || defined (__cplusplus)
+extern "C" {
+#endif
+
+#ifdef _WIN32
+#ifndef DLLAPI
+#define DLLAPI __declspec(dllimport)
+#endif
+#else
+#define DLLAPI
+#endif
+
+/*
+ * Wrapper type for FILE
+ */
+
+typedef struct {
+    FILE *stdio_stream;
+    FCGX_Stream *fcgx_stream;
+} FCGI_FILE;
+
+/*
+ * The four new functions and two new macros
+ */
+
+DLLAPI int FCGI_Accept(void);
+DLLAPI void FCGI_Finish(void);
+DLLAPI int FCGI_StartFilterData(void);
+DLLAPI void FCGI_SetExitStatus(int status);
+
+#define FCGI_ToFILE(fcgi_file) (fcgi_file->stdio_stream)
+#define FCGI_ToFcgiStream(fcgi_file) (fcgi_file->fcgx_stream)
+
+/*
+ * Wrapper stdin, stdout, and stderr variables, set up by FCGI_Accept()
+ */
+
+DLLAPI extern  FCGI_FILE       _fcgi_sF[];
+#define FCGI_stdin     (&_fcgi_sF[0])
+#define FCGI_stdout    (&_fcgi_sF[1])
+#define FCGI_stderr    (&_fcgi_sF[2])
+
+/*
+ * Wrapper function prototypes, grouped according to sections
+ * of Harbison & Steele, "C: A Reference Manual," fourth edition,
+ * Prentice-Hall, 1995.
+ */
+
+DLLAPI void FCGI_perror(const char *str);
+
+DLLAPI FCGI_FILE *FCGI_fopen(const char *path, const char *mode);
+DLLAPI int        FCGI_fclose(FCGI_FILE *fp);
+DLLAPI int        FCGI_fflush(FCGI_FILE *fp);
+DLLAPI FCGI_FILE *FCGI_freopen(const char *path, const char *mode, FCGI_FILE *fp);
+
+DLLAPI int        FCGI_setvbuf(FCGI_FILE *fp, char *buf, int bufmode, size_t size);
+DLLAPI void       FCGI_setbuf(FCGI_FILE *fp, char *buf);
+
+DLLAPI int        FCGI_fseek(FCGI_FILE *fp, long offset, int whence);
+DLLAPI int        FCGI_ftell(FCGI_FILE *fp);
+DLLAPI void       FCGI_rewind(FCGI_FILE *fp);
+#ifdef HAVE_FPOS
+DLLAPI int        FCGI_fgetpos(FCGI_FILE *fp, fpos_t *pos);
+DLLAPI int        FCGI_fsetpos(FCGI_FILE *fp, const fpos_t *pos);
+#endif
+DLLAPI int        FCGI_fgetc(FCGI_FILE *fp);
+DLLAPI int        FCGI_getchar(void);
+DLLAPI int        FCGI_ungetc(int c, FCGI_FILE *fp);
+
+DLLAPI char      *FCGI_fgets(char *str, int size, FCGI_FILE *fp);
+DLLAPI char      *FCGI_gets(char *str);
+
+/*
+ * Not yet implemented
+ *
+ * int        FCGI_fscanf(FCGI_FILE *fp, const char *format, ...);
+ * int        FCGI_scanf(const char *format, ...);
+ *
+ */
+
+DLLAPI int        FCGI_fputc(int c, FCGI_FILE *fp);
+DLLAPI int        FCGI_putchar(int c);
+
+DLLAPI int        FCGI_fputs(const char *str, FCGI_FILE *fp);
+DLLAPI int        FCGI_puts(const char *str);
+
+DLLAPI int        FCGI_fprintf(FCGI_FILE *fp, const char *format, ...);
+DLLAPI int        FCGI_printf(const char *format, ...);
+
+DLLAPI int        FCGI_vfprintf(FCGI_FILE *fp, const char *format, va_list ap);
+DLLAPI int        FCGI_vprintf(const char *format, va_list ap);
+
+DLLAPI size_t     FCGI_fread(void *ptr, size_t size, size_t nmemb, FCGI_FILE *fp);
+DLLAPI size_t     FCGI_fwrite(void *ptr, size_t size, size_t nmemb, FCGI_FILE *fp);
+
+DLLAPI int        FCGI_feof(FCGI_FILE *fp);
+DLLAPI int        FCGI_ferror(FCGI_FILE *fp);
+DLLAPI void       FCGI_clearerr(FCGI_FILE *fp);
+
+DLLAPI int        FCGI_fileno(FCGI_FILE *fp);
+DLLAPI FCGI_FILE *FCGI_fdopen(int fd, const char *mode);
+DLLAPI FCGI_FILE *FCGI_popen(const char *cmd, const char *type);
+DLLAPI int        FCGI_pclose(FCGI_FILE *);
+
+/*
+ * The remaining definitions are for application programs,
+ * not for fcgi_stdio.c
+ */
+
+#ifndef NO_FCGI_DEFINES
+
+/*
+ * Replace standard types, variables, and functions with FastCGI wrappers.
+ * Use undef in case a macro is already defined.
+ */
+
+#undef  FILE
+#define        FILE     FCGI_FILE
+
+#undef  stdin
+#define        stdin    FCGI_stdin
+#undef  stdout
+#define        stdout   FCGI_stdout
+#undef  stderr
+#define        stderr   FCGI_stderr
+
+#undef  perror
+#define        perror   FCGI_perror
+
+#undef  fopen
+#define        fopen    FCGI_fopen
+#undef  fclose
+#define        fclose   FCGI_fclose
+#undef  fflush
+#define        fflush   FCGI_fflush
+#undef  freopen
+#define        freopen  FCGI_freopen
+
+#undef  setvbuf
+#define        setvbuf  FCGI_setvbuf
+#undef  setbuf
+#define        setbuf   FCGI_setbuf
+
+#undef  fseek
+#define fseek    FCGI_fseek
+#undef  ftell
+#define ftell    FCGI_ftell
+#undef  rewind
+#define rewind   FCGI_rewind
+#undef  fgetpos
+#define fgetpos  FCGI_fgetpos
+#undef  fsetpos
+#define fsetpos  FCGI_fsetpos
+
+#undef  fgetc
+#define        fgetc    FCGI_fgetc
+#undef  getc
+#define getc     FCGI_fgetc
+#undef  getchar
+#define        getchar  FCGI_getchar
+#undef  ungetc
+#define ungetc   FCGI_ungetc
+
+#undef  fgets
+#define fgets    FCGI_fgets
+#undef  gets
+#define        gets     FCGI_gets
+
+#undef  fputc
+#define fputc    FCGI_fputc
+#undef  putc
+#define putc     FCGI_fputc
+#undef  putchar
+#define        putchar  FCGI_putchar
+
+#undef  fputs
+#define        fputs    FCGI_fputs
+#undef  puts
+#define        puts     FCGI_puts
+
+#undef  fprintf
+#define        fprintf  FCGI_fprintf
+#undef  printf
+#define        printf   FCGI_printf
+
+#undef  vfprintf
+#define vfprintf FCGI_vfprintf
+#undef  vprintf
+#define vprintf  FCGI_vprintf
+
+#undef  fread
+#define        fread    FCGI_fread
+#undef  fwrite
+#define fwrite   FCGI_fwrite
+
+#undef  feof
+#define        feof     FCGI_feof
+#undef  ferror
+#define ferror   FCGI_ferror
+#undef  clearerr
+#define        clearerr FCGI_clearerr
+
+#undef  fileno
+#define fileno   FCGI_fileno
+#undef  fdopen
+#define fdopen   FCGI_fdopen
+#undef  popen
+#define popen    FCGI_popen
+#undef  pclose
+#define        pclose   FCGI_pclose
+
+#endif /* NO_FCGI_DEFINES */
+
+#if defined (__cplusplus) || defined (c_plusplus)
+} /* terminate extern "C" { */
+#endif
+
+#endif /* _FCGI_STDIO */
+
diff --git a/include/fcgiapp.h b/include/fcgiapp.h
new file mode 100644 (file)
index 0000000..ebde17a
--- /dev/null
@@ -0,0 +1,444 @@
+/* 
+ * fcgiapp.h --
+ *
+ *      Definitions for FastCGI application server programs
+ *
+ *
+ * Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * $Id: fcgiapp.h,v 1.1 1997/09/16 15:36:32 stanleyg Exp $
+ */
+
+#ifndef _FCGIAPP_H
+#define _FCGIAPP_H
+
+#ifndef TCL_LIBRARY    /* Hack to see if we are building TCL since TCL
+                        * needs varargs not stdarg
+                        */
+#ifdef _WIN32
+#ifndef DLLAPI
+#define DLLAPI __declspec(dllimport)
+#endif
+#else
+#define DLLAPI
+#endif
+
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif /* TCL_LIBARARY */
+#include "fcgi_config.h"
+
+#if defined (c_plusplus) || defined (__cplusplus)
+extern "C" {
+#endif
+
+/*
+ * Error codes.  Assigned to avoid conflict with EOF and errno(2).
+ */
+#define FCGX_UNSUPPORTED_VERSION -2
+#define FCGX_PROTOCOL_ERROR -3
+#define FCGX_PARAMS_ERROR -4
+#define FCGX_CALL_SEQ_ERROR -5
+
+/*
+ * This structure defines the state of a FastCGI stream.
+ * Streams are modeled after the FILE type defined in stdio.h.
+ * (We wouldn't need our own if platform vendors provided a
+ * standard way to subclass theirs.)
+ * The state of a stream is private and should only be accessed
+ * by the procedures defined below.
+ */
+typedef struct FCGX_Stream {
+    unsigned char *rdNext;    /* reader: first valid byte
+                               * writer: equals stop */
+    unsigned char *wrNext;    /* writer: first free byte
+                               * reader: equals stop */
+    unsigned char *stop;      /* reader: last valid byte + 1
+                               * writer: last free byte + 1 */
+    unsigned char *stopUnget; /* reader: first byte of current buffer
+                               * fragment, for ungetc
+                               * writer: undefined */
+    int isReader;
+    int isClosed;
+    int wasFCloseCalled;
+    int FCGI_errno;                /* error status */
+    void (*fillBuffProc) (struct FCGX_Stream *stream);
+    void (*emptyBuffProc) (struct FCGX_Stream *stream, int doClose);
+    void *data;
+} FCGX_Stream;
+
+/*
+ * An environment (as defined by environ(7)): A NULL-terminated array
+ * of strings, each string having the form name=value.
+ */
+typedef char **FCGX_ParamArray;
+
+\f
+/*
+ *======================================================================
+ * Control
+ *======================================================================
+ */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_IsCGI --
+ *
+ *      Returns TRUE iff this process appears to be a CGI process
+ *      rather than a FastCGI process.
+ *
+ *----------------------------------------------------------------------
+ */
+DLLAPI int FCGX_IsCGI(void);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_Accept --
+ *
+ *      Accepts a new request from the HTTP server.
+ *
+ * Results:
+ *     0 for successful call, -1 for error.
+ *
+ * Side effects:
+ *
+ *      Finishes the request accepted by (and frees any
+ *      storage allocated by) the previous call to FCGX_Accept.
+ *      Creates input, output, and error streams and
+ *      assigns them to *in, *out, and *err respectively.
+ *      Creates a parameters data structure to be accessed
+ *      via getenv(3) (if assigned to environ) or by FCGX_GetParam
+ *      and assigns it to *envp.
+ *
+ *      DO NOT retain pointers to the envp array or any strings
+ *      contained in it (e.g. to the result of calling FCGX_GetParam),
+ *      since these will be freed by the next call to FCGX_Finish
+ *      or FCGX_Accept.
+ *
+ *----------------------------------------------------------------------
+ */
+DLLAPI int FCGX_Accept(
+        FCGX_Stream **in,
+        FCGX_Stream **out,
+        FCGX_Stream **err,
+        FCGX_ParamArray *envp);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_Finish --
+ *
+ *      Finishes the current request from the HTTP server.
+ *
+ * Side effects:
+ *
+ *      Finishes the request accepted by (and frees any
+ *      storage allocated by) the previous call to FCGX_Accept.
+ *
+ *      DO NOT retain pointers to the envp array or any strings
+ *      contained in it (e.g. to the result of calling FCGX_GetParam),
+ *      since these will be freed by the next call to FCGX_Finish
+ *      or FCGX_Accept.
+ *
+ *----------------------------------------------------------------------
+ */
+DLLAPI void FCGX_Finish(void);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_StartFilterData --
+ *
+ *      stream is an input stream for a FCGI_FILTER request.
+ *      stream is positioned at EOF on FCGI_STDIN.
+ *      Repositions stream to the start of FCGI_DATA.
+ *      If the preconditions are not met (e.g. FCGI_STDIN has not
+ *      been read to EOF) sets the stream error code to
+ *      FCGX_CALL_SEQ_ERROR.
+ *
+ * Results:
+ *      0 for a normal return, < 0 for error 
+ *
+ *----------------------------------------------------------------------
+ */
+DLLAPI int FCGX_StartFilterData(FCGX_Stream *stream);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_SetExitStatus --
+ *
+ *      Sets the exit status for stream's request. The exit status
+ *      is the status code the request would have exited with, had
+ *      the request been run as a CGI program.  You can call
+ *      SetExitStatus several times during a request; the last call
+ *      before the request ends determines the value.
+ *
+ *----------------------------------------------------------------------
+ */
+DLLAPI void FCGX_SetExitStatus(int status, FCGX_Stream *stream);
+\f
+/*
+ *======================================================================
+ * Parameters
+ *======================================================================
+ */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_GetParam -- obtain value of FCGI parameter in environment
+ *
+ *
+ * Results:
+ *     Value bound to name, NULL if name not present in the
+ *      environment envp.  Caller must not mutate the result
+ *      or retain it past the end of this request.
+ *
+ *----------------------------------------------------------------------
+ */
+DLLAPI char *FCGX_GetParam(const char *name, FCGX_ParamArray envp);
+\f
+/*
+ *======================================================================
+ * Readers
+ *======================================================================
+ */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_GetChar --
+ *
+ *      Reads a byte from the input stream and returns it.
+ *
+ * Results:
+ *     The byte, or EOF (-1) if the end of input has been reached.
+ *
+ *----------------------------------------------------------------------
+ */
+DLLAPI int FCGX_GetChar(FCGX_Stream *stream);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_UnGetChar --
+ *
+ *      Pushes back the character c onto the input stream.  One
+ *      character of pushback is guaranteed once a character
+ *      has been read.  No pushback is possible for EOF.
+ *
+ * Results:
+ *     Returns c if the pushback succeeded, EOF if not.
+ *
+ *----------------------------------------------------------------------
+ */
+DLLAPI int FCGX_UnGetChar(int c, FCGX_Stream *stream);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_GetStr --
+ *
+ *      Reads up to n consecutive bytes from the input stream
+ *      into the character array str.  Performs no interpretation
+ *      of the input bytes.
+ *
+ * Results:
+ *     Number of bytes read.  If result is smaller than n,
+ *      the end of input has been reached.
+ *
+ *----------------------------------------------------------------------
+ */
+DLLAPI int FCGX_GetStr(char *str, int n, FCGX_Stream *stream);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_GetLine --
+ *
+ *      Reads up to n-1 consecutive bytes from the input stream
+ *      into the character array str.  Stops before n-1 bytes
+ *      have been read if '\n' or EOF is read.  The terminating '\n'
+ *      is copied to str.  After copying the last byte into str,
+ *      stores a '\0' terminator.
+ *
+ * Results:
+ *     NULL if EOF is the first thing read from the input stream,
+ *      str otherwise.
+ *
+ *----------------------------------------------------------------------
+ */
+DLLAPI char *FCGX_GetLine(char *str, int n, FCGX_Stream *stream);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_HasSeenEOF --
+ *
+ *      Returns EOF if end-of-file has been detected while reading
+ *      from stream; otherwise returns 0.
+ *
+ *      Note that FCGX_HasSeenEOF(s) may return 0, yet an immediately
+ *      following FCGX_GetChar(s) may return EOF.  This function, like
+ *      the standard C stdio function feof, does not provide the
+ *      ability to peek ahead.
+ *
+ * Results:
+ *     EOF if end-of-file has been detected, 0 if not.
+ *
+ *----------------------------------------------------------------------
+ */
+
+DLLAPI  int FCGX_HasSeenEOF(FCGX_Stream *stream);
+\f
+/*
+ *======================================================================
+ * Writers
+ *======================================================================
+ */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_PutChar --
+ *
+ *      Writes a byte to the output stream.
+ *
+ * Results:
+ *     The byte, or EOF (-1) if an error occurred.
+ * 
+ *----------------------------------------------------------------------
+ */
+DLLAPI int FCGX_PutChar(int c, FCGX_Stream *stream);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_PutStr --
+ *
+ *      Writes n consecutive bytes from the character array str
+ *      into the output stream.  Performs no interpretation
+ *      of the output bytes.
+ *
+ * Results:
+ *      Number of bytes written (n) for normal return,
+ *      EOF (-1) if an error occurred.
+ * 
+ *----------------------------------------------------------------------
+ */
+DLLAPI int FCGX_PutStr(const char *str, int n, FCGX_Stream *stream);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_PutS --
+ *
+ *      Writes a null-terminated character string to the output stream.
+ *
+ * Results:
+ *      number of bytes written for normal return,
+ *      EOF (-1) if an error occurred.
+ * 
+ *----------------------------------------------------------------------
+ */
+DLLAPI int FCGX_PutS(const char *str, FCGX_Stream *stream);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_FPrintF, FCGX_VFPrintF --
+ *
+ *      Performs printf-style output formatting and writes the results
+ *      to the output stream.
+ *
+ * Results:
+ *      number of bytes written for normal return,
+ *      EOF (-1) if an error occurred.
+ * 
+ *----------------------------------------------------------------------
+ */
+DLLAPI int FCGX_FPrintF(FCGX_Stream *stream, const char *format, ...);
+
+DLLAPI int FCGX_VFPrintF(FCGX_Stream *stream, const char *format, va_list arg);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_FFlush --
+ *
+ *      Flushes any buffered output.
+ *
+ *      Server-push is a legitimate application of FCGX_FFlush.
+ *      Otherwise, FCGX_FFlush is not very useful, since FCGX_Accept
+ *      does it implicitly.  Calling FCGX_FFlush in non-push applications
+ *      results in extra writes and therefore reduces performance.
+ *
+ * Results:
+ *      EOF (-1) if an error occurred.
+ * 
+ *----------------------------------------------------------------------
+ */
+DLLAPI int FCGX_FFlush(FCGX_Stream *stream);
+\f
+/*
+ *======================================================================
+ * Both Readers and Writers
+ *======================================================================
+ */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_FClose --
+ *
+ *      Closes the stream.  For writers, flushes any buffered
+ *      output.
+ *
+ *      Close is not a very useful operation since FCGX_Accept
+ *      does it implicitly.  Closing the out stream before the
+ *      err stream results in an extra write if there's nothing
+ *      in the err stream, and therefore reduces performance.
+ *
+ * Results:
+ *      EOF (-1) if an error occurred.
+ * 
+ *----------------------------------------------------------------------
+ */
+DLLAPI int FCGX_FClose(FCGX_Stream *stream);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_GetError --
+ *
+ *      Return the stream error code.  0 means no error, > 0
+ *      is an errno(2) error, < 0 is an FastCGI error.
+ *
+ *----------------------------------------------------------------------
+ */
+DLLAPI int FCGX_GetError(FCGX_Stream *stream);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_ClearError --
+ *
+ *      Clear the stream error code and end-of-file indication.
+ *
+ *----------------------------------------------------------------------
+ */
+DLLAPI void FCGX_ClearError(FCGX_Stream *stream);
+
+
+#if defined (__cplusplus) || defined (c_plusplus)
+} /* terminate extern "C" { */
+#endif
+
+#endif /* _FCGIAPP_H */
diff --git a/include/fcgiappmisc.h b/include/fcgiappmisc.h
new file mode 100644 (file)
index 0000000..a8f0f6c
--- /dev/null
@@ -0,0 +1,46 @@
+/* 
+ * fcgiappmisc.h --
+ *
+ *      Functions implemented by fcgiapp.h that aren't needed
+ *      by normal applications, but may be useful to special
+ *      applications.
+ *
+ *
+ * Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * $Id: fcgiappmisc.h,v 1.1 1997/09/16 15:36:32 stanleyg Exp $
+ */
+
+#ifndef _FCGIAPPMISC_H
+#define _FCGIAPPMISC_H
+
+#include "fcgiapp.h"         /* for FCGX_Stream */
+
+#if defined (c_plusplus) || defined (__cplusplus)
+extern "C" {
+#endif
+
+#ifdef _WIN32
+#ifndef DLLAPI
+#define DLLAPI __declspec(dllimport)
+#endif
+#else
+#define DLLAPI
+#endif
+
+DLLAPI FCGX_Stream *CreateWriter(
+        int socket,
+        int requestId,
+        int bufflen,
+        int streamType);
+
+DLLAPI void FreeStream(FCGX_Stream **stream);
+
+#if defined (__cplusplus) || defined (c_plusplus)
+} /* terminate extern "C" { */
+#endif
+
+#endif /* _FCGIAPPMISC_H */
diff --git a/include/fcgimisc.h b/include/fcgimisc.h
new file mode 100644 (file)
index 0000000..027e3d3
--- /dev/null
@@ -0,0 +1,66 @@
+/* 
+ * fcgimisc.h --
+ *
+ *      Miscellaneous definitions
+ *
+ *
+ * Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * $Id: fcgimisc.h,v 1.1 1997/09/16 15:36:32 stanleyg Exp $
+ */
+
+#ifndef _FCGIMISC_H
+#define _FCGIMISC_H
+
+#include <stdio.h>
+#include <limits.h>
+
+#include <fcgi_config.h>
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_WINSOCK_H
+#include <winsock.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
+/*
+ * Where does this junk normally come from?
+ */
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#ifndef TRUE
+#define TRUE  (1)
+#endif
+
+#ifndef min
+#define min(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef max
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef ASSERT
+#define ASSERT(assertion) (assert(assertion))
+#endif
+
+#endif /* _FCGIMISC_H */
diff --git a/include/fcgios.h b/include/fcgios.h
new file mode 100755 (executable)
index 0000000..04dfbe2
--- /dev/null
@@ -0,0 +1,104 @@
+/* 
+ * fcgios.h --
+ *
+ *      Description of file.
+ *
+ *
+ *  Copyright (c) 1996 Open Market, Inc.
+ *  All rights reserved.
+ *
+ *  This file contains proprietary and confidential information and
+ *  remains the unpublished property of Open Market, Inc. Use, 
+ *  disclosure, or reproduction is prohibited except as permitted by 
+ *  express written license agreement with Open Market, Inc. 
+ *
+ *  Bill Snapper
+ *  snapper@openmarket.com
+ */
+#ifndef _FCGIOS_H
+#define _FCGIOS_H
+
+#ifdef _WIN32
+#define OS_Errno GetLastError()
+#define OS_SetErrno(err) SetLastError(err)
+#ifndef DLLAPI
+#define DLLAPI __declspec(dllimport)
+#endif
+#else
+#define DLLAPI
+#define OS_Errno errno
+#define OS_SetErrno(err) errno = (err)
+#endif
+
+#ifdef _WIN32
+#include <io.h>
+#endif
+
+#ifndef STDIN_FILENO
+#define STDIN_FILENO  0
+#endif
+
+
+#ifndef STDOUT_FILENO
+#define STDOUT_FILENO 1
+#endif
+
+#ifndef STDERR_FILENO
+#define STDERR_FILENO 2
+#endif
+
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 1024
+#endif
+
+#ifndef X_OK
+#define X_OK       0x01
+#endif
+
+#ifdef _WIN32
+#ifndef O_NONBLOCK
+#define O_NONBLOCK     0x0004  /* no delay */
+#endif
+#endif
+
+#if defined (c_plusplus) || defined (__cplusplus)
+extern "C" {
+#endif
+
+#ifndef _CLIENTDATA
+#   if defined(__STDC__) || defined(__cplusplus)
+    typedef void *ClientData;
+#   else
+    typedef int *ClientData;
+#   endif /* __STDC__ */
+#define _CLIENTDATA
+#endif
+
+typedef void (*OS_AsyncProc) (ClientData clientData, int len);
+
+DLLAPI int OS_LibInit(int stdioFds[3]);
+DLLAPI void OS_LibShutdown(void);
+DLLAPI int OS_CreateLocalIpcFd(char *bindPath);
+DLLAPI int OS_FcgiConnect(char *bindPath);
+DLLAPI int OS_Read(int fd, char * buf, size_t len);
+DLLAPI int OS_Write(int fd, char * buf, size_t len);
+DLLAPI int OS_SpawnChild(char *execPath, int listenFd);
+DLLAPI int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr, 
+                      ClientData clientData);
+DLLAPI int OS_AsyncRead(int fd, int offset, void *buf, int len,
+                OS_AsyncProc procPtr, ClientData clientData);
+DLLAPI int OS_AsyncWrite(int fd, int offset, void *buf, int len, 
+                 OS_AsyncProc procPtr, ClientData clientData);
+DLLAPI int OS_Close(int fd);
+DLLAPI int OS_CloseRead(int fd);
+DLLAPI int OS_DoIo(struct timeval *tmo);
+DLLAPI int OS_FcgiIpcAccept(char *clientAddrList);
+DLLAPI int OS_IpcClose(int ipcFd);
+DLLAPI int OS_IsFcgi(void);
+DLLAPI void OS_SetFlags(int fd, int flags);
+
+#if defined (__cplusplus) || defined (c_plusplus)
+} /* terminate extern "C" { */
+#endif
+
+#endif /* _FCGIOS_H */
diff --git a/include/tcl.h b/include/tcl.h
new file mode 100644 (file)
index 0000000..ed3f108
--- /dev/null
@@ -0,0 +1,669 @@
+/*
+ * tcl.h --
+ *
+ *     This header file describes the externally-visible facilities
+ *     of the Tcl interpreter.
+ *
+ * Copyright (c) 1987-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ *
+ * This software is copyrighted by the Regents of the University of
+ * California, Sun Microsystems, Inc., and other parties.  The following
+ * terms apply to all files associated with the software unless explicitly
+ * disclaimed in individual files.
+ *
+ * The authors hereby grant permission to use, copy, modify, distribute,
+ * and license this software and its documentation for any purpose, provided
+ * that existing copyright notices are retained in all copies and that this
+ * notice is included verbatim in any distributions. No written agreement,
+ * license, or royalty fee is required for any of the authorized uses.
+ * Modifications to this software may be copyrighted by their authors
+ * and need not follow the licensing terms described here, provided that
+ * the new terms are clearly indicated on the first page of each file where
+ * they apply.
+ * 
+ * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
+ * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
+ * DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
+ * IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
+ * NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+ * MODIFICATIONS.
+ * 
+ * RESTRICTED RIGHTS: Use, duplication or disclosure by the government
+ * is subject to the restrictions as set forth in subparagraph (c) (1) (ii)
+ * of the Rights in Technical Data and Computer Software Clause as DFARS
+ * 252.227-7013 and FAR 52.227-19.
+ *
+ * $Id: tcl.h,v 1.1 1997/09/16 15:36:32 stanleyg Exp $
+ *
+ * @(#) tcl.h 1.153 95/06/27 15:42:31
+ */
+
+#ifndef _TCL
+#define _TCL
+
+#ifndef BUFSIZ
+#include <stdio.h>
+#endif
+
+#define TCL_VERSION "7.4"
+#define TCL_MAJOR_VERSION 7
+#define TCL_MINOR_VERSION 4
+
+/*
+ * Definitions that allow this header file to be used either with or
+ * without ANSI C features like function prototypes.
+ */
+
+#undef _ANSI_ARGS_
+#undef CONST
+#if ((defined(__STDC__) || defined(SABER)) && !defined(NO_PROTOTYPE)) || defined(__cplusplus)
+#   define _USING_PROTOTYPES_ 1
+#   define _ANSI_ARGS_(x)      x
+#   define CONST const
+#   ifdef __cplusplus
+#       define VARARGS(first) (first, ...)
+#   else
+#       define VARARGS(first) ()
+#   endif
+#else
+#   define _ANSI_ARGS_(x)      ()
+#   define CONST
+#endif
+
+#ifdef __cplusplus
+#   define EXTERN extern "C"
+#else
+#   define EXTERN extern
+#endif
+
+/*
+ * Macro to use instead of "void" for arguments that must have
+ * type "void *" in ANSI C;  maps them to type "char *" in
+ * non-ANSI systems.
+ */
+
+#ifndef VOID
+#   ifdef __STDC__
+#       define VOID void
+#   else
+#       define VOID char
+#   endif
+#endif
+
+/*
+ * Miscellaneous declarations (to allow Tcl to be used stand-alone,
+ * without the rest of Sprite).
+ */
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef _CLIENTDATA
+#   if defined(__STDC__) || defined(__cplusplus)
+    typedef void *ClientData;
+#   else
+    typedef int *ClientData;
+#   endif /* __STDC__ */
+#define _CLIENTDATA
+#endif
+
+/*
+ * Data structures defined opaquely in this module.  The definitions
+ * below just provide dummy types.  A few fields are made visible in
+ * Tcl_Interp structures, namely those for returning string values.
+ * Note:  any change to the Tcl_Interp definition below must be mirrored
+ * in the "real" definition in tclInt.h.
+ */
+
+typedef struct Tcl_Interp{
+    char *result;              /* Points to result string returned by last
+                                * command. */
+    void (*freeProc) _ANSI_ARGS_((char *blockPtr));
+                               /* Zero means result is statically allocated.
+                                * If non-zero, gives address of procedure
+                                * to invoke to free the result.  Must be
+                                * freed by Tcl_Eval before executing next
+                                * command. */
+    int errorLine;             /* When TCL_ERROR is returned, this gives
+                                * the line number within the command where
+                                * the error occurred (1 means first line). */
+} Tcl_Interp;
+
+typedef int *Tcl_Trace;
+typedef int *Tcl_Command;
+typedef struct Tcl_AsyncHandler_ *Tcl_AsyncHandler;
+typedef struct Tcl_RegExp_ *Tcl_RegExp;
+
+/*
+ * When a TCL command returns, the string pointer interp->result points to
+ * a string containing return information from the command.  In addition,
+ * the command procedure returns an integer value, which is one of the
+ * following:
+ *
+ * TCL_OK              Command completed normally;  interp->result contains
+ *                     the command's result.
+ * TCL_ERROR           The command couldn't be completed successfully;
+ *                     interp->result describes what went wrong.
+ * TCL_RETURN          The command requests that the current procedure
+ *                     return;  interp->result contains the procedure's
+ *                     return value.
+ * TCL_BREAK           The command requests that the innermost loop
+ *                     be exited;  interp->result is meaningless.
+ * TCL_CONTINUE                Go on to the next iteration of the current loop;
+ *                     interp->result is meaningless.
+ */
+
+#define TCL_OK         0
+#define TCL_ERROR      1
+#define TCL_RETURN     2
+#define TCL_BREAK      3
+#define TCL_CONTINUE   4
+
+#define TCL_RESULT_SIZE 200
+
+/*
+ * Argument descriptors for math function callbacks in expressions:
+ */
+
+typedef enum {TCL_INT, TCL_DOUBLE, TCL_EITHER} Tcl_ValueType;
+typedef struct Tcl_Value {
+    Tcl_ValueType type;                /* Indicates intValue or doubleValue is
+                                * valid, or both. */
+    long intValue;             /* Integer value. */
+    double doubleValue;                /* Double-precision floating value. */
+} Tcl_Value;
+
+/*
+ * Procedure types defined by Tcl:
+ */
+
+typedef int (Tcl_AppInitProc) _ANSI_ARGS_((Tcl_Interp *interp));
+typedef int (Tcl_AsyncProc) _ANSI_ARGS_((ClientData clientData,
+       Tcl_Interp *interp, int code));
+typedef void (Tcl_CmdDeleteProc) _ANSI_ARGS_((ClientData clientData));
+typedef int (Tcl_CmdProc) _ANSI_ARGS_((ClientData clientData,
+       Tcl_Interp *interp, int argc, char *argv[]));
+typedef void (Tcl_CmdTraceProc) _ANSI_ARGS_((ClientData clientData,
+       Tcl_Interp *interp, int level, char *command, Tcl_CmdProc *proc,
+       ClientData cmdClientData, int argc, char *argv[]));
+typedef void (Tcl_FreeProc) _ANSI_ARGS_((char *blockPtr));
+typedef void (Tcl_InterpDeleteProc) _ANSI_ARGS_((ClientData clientData,
+       Tcl_Interp *interp));
+typedef int (Tcl_MathProc) _ANSI_ARGS_((ClientData clientData,
+       Tcl_Interp *interp, Tcl_Value *args, Tcl_Value *resultPtr));
+typedef char *(Tcl_VarTraceProc) _ANSI_ARGS_((ClientData clientData,
+       Tcl_Interp *interp, char *part1, char *part2, int flags));
+
+/*
+ * The structure returned by Tcl_GetCmdInfo and passed into
+ * Tcl_SetCmdInfo:
+ */
+
+typedef struct Tcl_CmdInfo {
+    Tcl_CmdProc *proc;                 /* Procedure that implements command. */
+    ClientData clientData;             /* ClientData passed to proc. */
+    Tcl_CmdDeleteProc *deleteProc;     /* Procedure to call when command
+                                        * is deleted. */
+    ClientData deleteData;             /* Value to pass to deleteProc (usually
+                                        * the same as clientData). */
+} Tcl_CmdInfo;
+
+/*
+ * The structure defined below is used to hold dynamic strings.  The only
+ * field that clients should use is the string field, and they should
+ * never modify it.
+ */
+
+#define TCL_DSTRING_STATIC_SIZE 200
+typedef struct Tcl_DString {
+    char *string;              /* Points to beginning of string:  either
+                                * staticSpace below or a malloc'ed array. */
+    int length;                        /* Number of non-NULL characters in the
+                                * string. */
+    int spaceAvl;              /* Total number of bytes available for the
+                                * string and its terminating NULL char. */
+    char staticSpace[TCL_DSTRING_STATIC_SIZE];
+                               /* Space to use in common case where string
+                                * is small. */
+} Tcl_DString;
+
+#define Tcl_DStringLength(dsPtr) ((dsPtr)->length)
+#define Tcl_DStringValue(dsPtr) ((dsPtr)->string)
+#define Tcl_DStringTrunc Tcl_DStringSetLength
+
+/*
+ * Definitions for the maximum number of digits of precision that may
+ * be specified in the "tcl_precision" variable, and the number of
+ * characters of buffer space required by Tcl_PrintDouble.
+ */
+
+#define TCL_MAX_PREC 17
+#define TCL_DOUBLE_SPACE (TCL_MAX_PREC+10)
+
+/*
+ * Flag that may be passed to Tcl_ConvertElement to force it not to
+ * output braces (careful!  if you change this flag be sure to change
+ * the definitions at the front of tclUtil.c).
+ */
+
+#define TCL_DONT_USE_BRACES    1
+
+/*
+ * Flag values passed to Tcl_RecordAndEval.
+ * WARNING: these bit choices must not conflict with the bit choices
+ * for evalFlag bits in tclInt.h!!
+ */
+
+#define TCL_NO_EVAL            0x10000
+#define TCL_EVAL_GLOBAL                0x20000
+
+/*
+ * Special freeProc values that may be passed to Tcl_SetResult (see
+ * the man page for details):
+ */
+
+#define TCL_VOLATILE   ((Tcl_FreeProc *) 1)
+#define TCL_STATIC     ((Tcl_FreeProc *) 0)
+#define TCL_DYNAMIC    ((Tcl_FreeProc *) 3)
+
+/*
+ * Flag values passed to variable-related procedures.
+ */
+
+#define TCL_GLOBAL_ONLY                1
+#define TCL_APPEND_VALUE       2
+#define TCL_LIST_ELEMENT       4
+#define TCL_TRACE_READS                0x10
+#define TCL_TRACE_WRITES       0x20
+#define TCL_TRACE_UNSETS       0x40
+#define TCL_TRACE_DESTROYED    0x80
+#define TCL_INTERP_DESTROYED   0x100
+#define TCL_LEAVE_ERR_MSG      0x200
+
+/*
+ * Types for linked variables:
+ */
+
+#define TCL_LINK_INT           1
+#define TCL_LINK_DOUBLE                2
+#define TCL_LINK_BOOLEAN       3
+#define TCL_LINK_STRING                4
+#define TCL_LINK_READ_ONLY     0x80
+
+/*
+ * Permission flags for files:
+ */
+
+#define TCL_FILE_READABLE      1
+#define TCL_FILE_WRITABLE      2
+
+/*
+ * The following declarations either map ckalloc and ckfree to
+ * malloc and free, or they map them to procedures with all sorts
+ * of debugging hooks defined in tclCkalloc.c.
+ */
+
+#ifdef TCL_MEM_DEBUG
+
+EXTERN char *          Tcl_DbCkalloc _ANSI_ARGS_((unsigned int size,
+                           char *file, int line));
+EXTERN int             Tcl_DbCkfree _ANSI_ARGS_((char *ptr,
+                           char *file, int line));
+EXTERN char *          Tcl_DbCkrealloc _ANSI_ARGS_((char *ptr,
+                           unsigned int size, char *file, int line));
+EXTERN int             Tcl_DumpActiveMemory _ANSI_ARGS_((char *fileName));
+EXTERN void            Tcl_ValidateAllMemory _ANSI_ARGS_((char *file,
+                           int line));
+#  define ckalloc(x) Tcl_DbCkalloc(x, __FILE__, __LINE__)
+#  define ckfree(x)  Tcl_DbCkfree(x, __FILE__, __LINE__)
+#  define ckrealloc(x,y) Tcl_DbCkrealloc((x), (y),__FILE__, __LINE__)
+
+#else
+
+#  define ckalloc(x) malloc(x)
+#  define ckfree(x)  free(x)
+#  define ckrealloc(x,y) realloc(x,y)
+#  define Tcl_DumpActiveMemory(x)
+#  define Tcl_ValidateAllMemory(x,y)
+
+#endif /* TCL_MEM_DEBUG */
+
+/*
+ * Macro to free up result of interpreter.
+ */
+
+#define Tcl_FreeResult(interp)                                 \
+    if ((interp)->freeProc != 0) {                             \
+       if ((interp)->freeProc == (Tcl_FreeProc *) free) {      \
+           ckfree((interp)->result);                           \
+       } else {                                                \
+           (*(interp)->freeProc)((interp)->result);            \
+       }                                                       \
+       (interp)->freeProc = 0;                                 \
+    }
+
+/*
+ * Forward declaration of Tcl_HashTable.  Needed by some C++ compilers
+ * to prevent errors when the forward reference to Tcl_HashTable is
+ * encountered in the Tcl_HashEntry structure.
+ */
+
+#ifdef __cplusplus
+struct Tcl_HashTable;
+#endif
+
+/*
+ * Structure definition for an entry in a hash table.  No-one outside
+ * Tcl should access any of these fields directly;  use the macros
+ * defined below.
+ */
+
+typedef struct Tcl_HashEntry {
+    struct Tcl_HashEntry *nextPtr;     /* Pointer to next entry in this
+                                        * hash bucket, or NULL for end of
+                                        * chain. */
+    struct Tcl_HashTable *tablePtr;    /* Pointer to table containing entry. */
+    struct Tcl_HashEntry **bucketPtr;  /* Pointer to bucket that points to
+                                        * first entry in this entry's chain:
+                                        * used for deleting the entry. */
+    ClientData clientData;             /* Application stores something here
+                                        * with Tcl_SetHashValue. */
+    union {                            /* Key has one of these forms: */
+       char *oneWordValue;             /* One-word value for key. */
+       int words[1];                   /* Multiple integer words for key.
+                                        * The actual size will be as large
+                                        * as necessary for this table's
+                                        * keys. */
+       char string[4];                 /* String for key.  The actual size
+                                        * will be as large as needed to hold
+                                        * the key. */
+    } key;                             /* MUST BE LAST FIELD IN RECORD!! */
+} Tcl_HashEntry;
+
+/*
+ * Structure definition for a hash table.  Must be in tcl.h so clients
+ * can allocate space for these structures, but clients should never
+ * access any fields in this structure.
+ */
+
+#define TCL_SMALL_HASH_TABLE 4
+typedef struct Tcl_HashTable {
+    Tcl_HashEntry **buckets;           /* Pointer to bucket array.  Each
+                                        * element points to first entry in
+                                        * bucket's hash chain, or NULL. */
+    Tcl_HashEntry *staticBuckets[TCL_SMALL_HASH_TABLE];
+                                       /* Bucket array used for small tables
+                                        * (to avoid mallocs and frees). */
+    int numBuckets;                    /* Total number of buckets allocated
+                                        * at **bucketPtr. */
+    int numEntries;                    /* Total number of entries present
+                                        * in table. */
+    int rebuildSize;                   /* Enlarge table when numEntries gets
+                                        * to be this large. */
+    int downShift;                     /* Shift count used in hashing
+                                        * function.  Designed to use high-
+                                        * order bits of randomized keys. */
+    int mask;                          /* Mask value used in hashing
+                                        * function. */
+    int keyType;                       /* Type of keys used in this table. 
+                                        * It's either TCL_STRING_KEYS,
+                                        * TCL_ONE_WORD_KEYS, or an integer
+                                        * giving the number of ints in a
+                                        */
+    Tcl_HashEntry *(*findProc) _ANSI_ARGS_((struct Tcl_HashTable *tablePtr,
+           char *key));
+    Tcl_HashEntry *(*createProc) _ANSI_ARGS_((struct Tcl_HashTable *tablePtr,
+           char *key, int *newPtr));
+} Tcl_HashTable;
+
+/*
+ * Structure definition for information used to keep track of searches
+ * through hash tables:
+ */
+
+typedef struct Tcl_HashSearch {
+    Tcl_HashTable *tablePtr;           /* Table being searched. */
+    int nextIndex;                     /* Index of next bucket to be
+                                        * enumerated after present one. */
+    Tcl_HashEntry *nextEntryPtr;       /* Next entry to be enumerated in the
+                                        * the current bucket. */
+} Tcl_HashSearch;
+
+/*
+ * Acceptable key types for hash tables:
+ */
+
+#define TCL_STRING_KEYS                0
+#define TCL_ONE_WORD_KEYS      1
+
+/*
+ * Macros for clients to use to access fields of hash entries:
+ */
+
+#define Tcl_GetHashValue(h) ((h)->clientData)
+#define Tcl_SetHashValue(h, value) ((h)->clientData = (ClientData) (value))
+#define Tcl_GetHashKey(tablePtr, h) \
+    ((char *) (((tablePtr)->keyType == TCL_ONE_WORD_KEYS) ? (h)->key.oneWordValue \
+                                               : (h)->key.string))
+
+/*
+ * Macros to use for clients to use to invoke find and create procedures
+ * for hash tables:
+ */
+
+#define Tcl_FindHashEntry(tablePtr, key) \
+       (*((tablePtr)->findProc))(tablePtr, key)
+#define Tcl_CreateHashEntry(tablePtr, key, newPtr) \
+       (*((tablePtr)->createProc))(tablePtr, key, newPtr)
+
+/*
+ * Exported Tcl variables:
+ */
+
+EXTERN int             tcl_AsyncReady;
+EXTERN void            (*tcl_FileCloseProc) _ANSI_ARGS_((FILE *f));
+EXTERN char *          tcl_RcFileName;
+
+/*
+ * Exported Tcl procedures:
+ */
+
+EXTERN void            Tcl_AddErrorInfo _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *message));
+EXTERN void            Tcl_AllowExceptions _ANSI_ARGS_((Tcl_Interp *interp));
+EXTERN void            Tcl_AppendElement _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *string));
+EXTERN void            Tcl_AppendResult _ANSI_ARGS_(
+                           VARARGS(Tcl_Interp *interp));
+EXTERN int             Tcl_AppInit _ANSI_ARGS_((Tcl_Interp *interp));
+EXTERN void            Tcl_AsyncMark _ANSI_ARGS_((Tcl_AsyncHandler async));
+EXTERN Tcl_AsyncHandler        Tcl_AsyncCreate _ANSI_ARGS_((Tcl_AsyncProc *proc,
+                           ClientData clientData));
+EXTERN void            Tcl_AsyncDelete _ANSI_ARGS_((Tcl_AsyncHandler async));
+EXTERN int             Tcl_AsyncInvoke _ANSI_ARGS_((Tcl_Interp *interp,
+                           int code));
+EXTERN char            Tcl_Backslash _ANSI_ARGS_((char *src,
+                           int *readPtr));
+EXTERN void            Tcl_CallWhenDeleted _ANSI_ARGS_((Tcl_Interp *interp,
+                           Tcl_InterpDeleteProc *proc,
+                           ClientData clientData));
+EXTERN int             Tcl_CommandComplete _ANSI_ARGS_((char *cmd));
+EXTERN char *          Tcl_Concat _ANSI_ARGS_((int argc, char **argv));
+EXTERN int             Tcl_ConvertElement _ANSI_ARGS_((char *src,
+                           char *dst, int flags));
+EXTERN Tcl_Command     Tcl_CreateCommand _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *cmdName, Tcl_CmdProc *proc,
+                           ClientData clientData,
+                           Tcl_CmdDeleteProc *deleteProc));
+EXTERN Tcl_Interp *    Tcl_CreateInterp _ANSI_ARGS_((void));
+EXTERN void            Tcl_CreateMathFunc _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *name, int numArgs, Tcl_ValueType *argTypes,
+                           Tcl_MathProc *proc, ClientData clientData));
+EXTERN int             Tcl_CreatePipeline _ANSI_ARGS_((Tcl_Interp *interp,
+                           int argc, char **argv, int **pidArrayPtr,
+                           int *inPipePtr, int *outPipePtr,
+                           int *errFilePtr));
+EXTERN Tcl_Trace       Tcl_CreateTrace _ANSI_ARGS_((Tcl_Interp *interp,
+                           int level, Tcl_CmdTraceProc *proc,
+                           ClientData clientData));
+EXTERN int             Tcl_DeleteCommand _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *cmdName));
+EXTERN void            Tcl_DeleteHashEntry _ANSI_ARGS_((
+                           Tcl_HashEntry *entryPtr));
+EXTERN void            Tcl_DeleteHashTable _ANSI_ARGS_((
+                           Tcl_HashTable *tablePtr));
+EXTERN void            Tcl_DeleteInterp _ANSI_ARGS_((Tcl_Interp *interp));
+EXTERN void            Tcl_DeleteTrace _ANSI_ARGS_((Tcl_Interp *interp,
+                           Tcl_Trace trace));
+EXTERN void            Tcl_DetachPids _ANSI_ARGS_((int numPids, int *pidPtr));
+EXTERN void            Tcl_DontCallWhenDeleted _ANSI_ARGS_((
+                           Tcl_Interp *interp, Tcl_InterpDeleteProc *proc,
+                           ClientData clientData));
+EXTERN char *          Tcl_DStringAppend _ANSI_ARGS_((Tcl_DString *dsPtr,
+                           char *string, int length));
+EXTERN char *          Tcl_DStringAppendElement _ANSI_ARGS_((
+                           Tcl_DString *dsPtr, char *string));
+EXTERN void            Tcl_DStringEndSublist _ANSI_ARGS_((Tcl_DString *dsPtr));
+EXTERN void            Tcl_DStringFree _ANSI_ARGS_((Tcl_DString *dsPtr));
+EXTERN void            Tcl_DStringGetResult _ANSI_ARGS_((Tcl_Interp *interp,
+                           Tcl_DString *dsPtr));
+EXTERN void            Tcl_DStringInit _ANSI_ARGS_((Tcl_DString *dsPtr));
+EXTERN void            Tcl_DStringResult _ANSI_ARGS_((Tcl_Interp *interp,
+                           Tcl_DString *dsPtr));
+EXTERN void            Tcl_DStringSetLength _ANSI_ARGS_((Tcl_DString *dsPtr,
+                           int length));
+EXTERN void            Tcl_DStringStartSublist _ANSI_ARGS_((
+                           Tcl_DString *dsPtr));
+EXTERN void            Tcl_EnterFile _ANSI_ARGS_((Tcl_Interp *interp,
+                           FILE *file, int permissions));
+EXTERN char *          Tcl_ErrnoId _ANSI_ARGS_((void));
+EXTERN int             Tcl_Eval _ANSI_ARGS_((Tcl_Interp *interp, char *cmd));
+EXTERN int             Tcl_EvalFile _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *fileName));
+EXTERN int             Tcl_ExprBoolean _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *string, int *ptr));
+EXTERN int             Tcl_ExprDouble _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *string, double *ptr));
+EXTERN int             Tcl_ExprLong _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *string, long *ptr));
+EXTERN int             Tcl_ExprString _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *string));
+EXTERN int             Tcl_FilePermissions _ANSI_ARGS_((FILE *file));
+EXTERN Tcl_HashEntry * Tcl_FirstHashEntry _ANSI_ARGS_((
+                           Tcl_HashTable *tablePtr,
+                           Tcl_HashSearch *searchPtr));
+EXTERN int             Tcl_GetBoolean _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *string, int *boolPtr));
+EXTERN int             Tcl_GetCommandInfo _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *cmdName, Tcl_CmdInfo *infoPtr));
+EXTERN char *          Tcl_GetCommandName _ANSI_ARGS_((Tcl_Interp *interp,
+                           Tcl_Command command));
+EXTERN int             Tcl_GetDouble _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *string, double *doublePtr));
+EXTERN int             Tcl_GetInt _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *string, int *intPtr));
+EXTERN int             Tcl_GetOpenFile _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *string, int write, int checkUsage,
+                           FILE **filePtr));
+EXTERN char *          Tcl_GetVar _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *varName, int flags));
+EXTERN char *          Tcl_GetVar2 _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *part1, char *part2, int flags));
+EXTERN int             Tcl_GlobalEval _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *command));
+EXTERN char *          Tcl_HashStats _ANSI_ARGS_((Tcl_HashTable *tablePtr));
+EXTERN int             Tcl_Init _ANSI_ARGS_((Tcl_Interp *interp));
+EXTERN void            Tcl_InitHashTable _ANSI_ARGS_((Tcl_HashTable *tablePtr,
+                           int keyType));
+EXTERN void            Tcl_InitMemory _ANSI_ARGS_((Tcl_Interp *interp));
+EXTERN int             Tcl_LinkVar _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *varName, char *addr, int type));
+EXTERN void            Tcl_Main _ANSI_ARGS_((int argc, char **argv,
+                           Tcl_AppInitProc *appInitProc));
+EXTERN char *          Tcl_Merge _ANSI_ARGS_((int argc, char **argv));
+EXTERN Tcl_HashEntry * Tcl_NextHashEntry _ANSI_ARGS_((
+                           Tcl_HashSearch *searchPtr));
+EXTERN char *          Tcl_ParseVar _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *string, char **termPtr));
+EXTERN char *          Tcl_PosixError _ANSI_ARGS_((Tcl_Interp *interp));
+EXTERN void            Tcl_PrintDouble _ANSI_ARGS_((Tcl_Interp *interp,
+                           double value, char *dst));
+EXTERN void            Tcl_ReapDetachedProcs _ANSI_ARGS_((void));
+EXTERN int             Tcl_RecordAndEval _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *cmd, int flags));
+EXTERN Tcl_RegExp      Tcl_RegExpCompile _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *string));
+EXTERN int             Tcl_RegExpExec _ANSI_ARGS_((Tcl_Interp *interp,
+                           Tcl_RegExp regexp, char *string, char *start));
+EXTERN int             Tcl_RegExpMatch _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *string, char *pattern));
+EXTERN void            Tcl_RegExpRange _ANSI_ARGS_((Tcl_RegExp regexp,
+                           int index, char **startPtr, char **endPtr));
+EXTERN void            Tcl_ResetResult _ANSI_ARGS_((Tcl_Interp *interp));
+#define Tcl_Return Tcl_SetResult
+EXTERN int             Tcl_ScanElement _ANSI_ARGS_((char *string,
+                           int *flagPtr));
+EXTERN int             Tcl_SetCommandInfo _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *cmdName, Tcl_CmdInfo *infoPtr));
+EXTERN void            Tcl_SetErrorCode _ANSI_ARGS_(
+                           VARARGS(Tcl_Interp *interp));
+EXTERN int             Tcl_SetRecursionLimit _ANSI_ARGS_((Tcl_Interp *interp,
+                           int depth));
+EXTERN void            Tcl_SetResult _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *string, Tcl_FreeProc *freeProc));
+EXTERN char *          Tcl_SetVar _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *varName, char *newValue, int flags));
+EXTERN char *          Tcl_SetVar2 _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *part1, char *part2, char *newValue,
+                           int flags));
+EXTERN char *          Tcl_SignalId _ANSI_ARGS_((int sig));
+EXTERN char *          Tcl_SignalMsg _ANSI_ARGS_((int sig));
+EXTERN int             Tcl_SplitList _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *list, int *argcPtr, char ***argvPtr));
+EXTERN int             Tcl_StringMatch _ANSI_ARGS_((char *string,
+                           char *pattern));
+EXTERN char *          Tcl_TildeSubst _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *name, Tcl_DString *bufferPtr));
+EXTERN int             Tcl_TraceVar _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *varName, int flags, Tcl_VarTraceProc *proc,
+                           ClientData clientData));
+EXTERN int             Tcl_TraceVar2 _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *part1, char *part2, int flags,
+                           Tcl_VarTraceProc *proc, ClientData clientData));
+EXTERN void            Tcl_UnlinkVar _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *varName));
+EXTERN int             Tcl_UnsetVar _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *varName, int flags));
+EXTERN int             Tcl_UnsetVar2 _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *part1, char *part2, int flags));
+EXTERN void            Tcl_UntraceVar _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *varName, int flags, Tcl_VarTraceProc *proc,
+                           ClientData clientData));
+EXTERN void            Tcl_UntraceVar2 _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *part1, char *part2, int flags,
+                           Tcl_VarTraceProc *proc, ClientData clientData));
+EXTERN int             Tcl_UpVar _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *frameName, char *varName,
+                           char *localName, int flags));
+EXTERN int             Tcl_UpVar2 _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *frameName, char *part1, char *part2,
+                           char *localName, int flags));
+EXTERN int             Tcl_VarEval _ANSI_ARGS_(VARARGS(Tcl_Interp *interp));
+EXTERN ClientData      Tcl_VarTraceInfo _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *varName, int flags,
+                           Tcl_VarTraceProc *procPtr,
+                           ClientData prevClientData));
+EXTERN ClientData      Tcl_VarTraceInfo2 _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *part1, char *part2, int flags,
+                           Tcl_VarTraceProc *procPtr,
+                           ClientData prevClientData));
+
+#endif /* _TCL */
diff --git a/include/tclInt.h b/include/tclInt.h
new file mode 100644 (file)
index 0000000..6ec10fe
--- /dev/null
@@ -0,0 +1,972 @@
+/*
+ * tclInt.h --
+ *
+ *     Declarations of things used internally by the Tcl interpreter.
+ *
+ * Copyright (c) 1987-1993 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ *
+ * This software is copyrighted by the Regents of the University of
+ * California, Sun Microsystems, Inc., and other parties.  The following
+ * terms apply to all files associated with the software unless explicitly
+ * disclaimed in individual files.
+ *
+ * The authors hereby grant permission to use, copy, modify, distribute,
+ * and license this software and its documentation for any purpose, provided
+ * that existing copyright notices are retained in all copies and that this
+ * notice is included verbatim in any distributions. No written agreement,
+ * license, or royalty fee is required for any of the authorized uses.
+ * Modifications to this software may be copyrighted by their authors
+ * and need not follow the licensing terms described here, provided that
+ * the new terms are clearly indicated on the first page of each file where
+ * they apply.
+ * 
+ * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
+ * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
+ * DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
+ * IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
+ * NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+ * MODIFICATIONS.
+ * 
+ * RESTRICTED RIGHTS: Use, duplication or disclosure by the government
+ * is subject to the restrictions as set forth in subparagraph (c) (1) (ii)
+ * of the Rights in Technical Data and Computer Software Clause as DFARS
+ * 252.227-7013 and FAR 52.227-19.
+ *
+ * $Id: tclInt.h,v 1.1 1997/09/16 15:36:32 stanleyg Exp $
+ *
+ * @(#) tclInt.h 1.106 95/08/25 15:44:50
+ */
+
+#ifndef _TCLINT
+#define _TCLINT
+
+/*
+ * Common include files needed by most of the Tcl source files are
+ * included here, so that system-dependent personalizations for the
+ * include files only have to be made in once place.  This results
+ * in a few extra includes, but greater modularity.  The order of
+ * the three groups of #includes is important.  For example, stdio.h
+ * is needed by tcl.h, and the _ANSI_ARGS_ declaration in tcl.h is
+ * needed by stdlib.h in some configurations.
+ */
+
+#include <stdio.h>
+
+#ifndef _TCL
+#include "tcl.h"
+#endif
+#ifndef _REGEXP
+#include "tclRegexp.h"
+#endif
+
+#include <ctype.h>
+#ifdef NO_LIMITS_H
+#   include "compat/limits.h"
+#else
+#   include <limits.h>
+#endif
+#ifdef NO_STDLIB_H
+#   include "compat/stdlib.h"
+#else
+#   include <stdlib.h>
+#endif
+#ifdef NO_STRING_H
+#include "compat/string.h"
+#else
+#include <string.h>
+#endif
+#include <varargs.h>
+
+/*
+ * At present (12/91) not all stdlib.h implementations declare strtod.
+ * The declaration below is here to ensure that it's declared, so that
+ * the compiler won't take the default approach of assuming it returns
+ * an int.  There's no ANSI prototype for it because there would end
+ * up being too many conflicts with slightly-different prototypes.
+ */
+
+extern double strtod();
+
+/*
+ *----------------------------------------------------------------
+ * Data structures related to variables.   These are used primarily
+ * in tclVar.c
+ *----------------------------------------------------------------
+ */
+
+/*
+ * The following structure defines a variable trace, which is used to
+ * invoke a specific C procedure whenever certain operations are performed
+ * on a variable.
+ */
+
+typedef struct VarTrace {
+    Tcl_VarTraceProc *traceProc;/* Procedure to call when operations given
+                                * by flags are performed on variable. */
+    ClientData clientData;     /* Argument to pass to proc. */
+    int flags;                 /* What events the trace procedure is
+                                * interested in:  OR-ed combination of
+                                * TCL_TRACE_READS, TCL_TRACE_WRITES, and
+                                * TCL_TRACE_UNSETS. */
+    struct VarTrace *nextPtr;  /* Next in list of traces associated with
+                                * a particular variable. */
+} VarTrace;
+
+/*
+ * When a variable trace is active (i.e. its associated procedure is
+ * executing), one of the following structures is linked into a list
+ * associated with the variable's interpreter.  The information in
+ * the structure is needed in order for Tcl to behave reasonably
+ * if traces are deleted while traces are active.
+ */
+
+typedef struct ActiveVarTrace {
+    struct Var *varPtr;                /* Variable that's being traced. */
+    struct ActiveVarTrace *nextPtr;
+                               /* Next in list of all active variable
+                                * traces for the interpreter, or NULL
+                                * if no more. */
+    VarTrace *nextTracePtr;    /* Next trace to check after current
+                                * trace procedure returns;  if this
+                                * trace gets deleted, must update pointer
+                                * to avoid using free'd memory. */
+} ActiveVarTrace;
+
+/*
+ * The following structure describes an enumerative search in progress on
+ * an array variable;  this are invoked with options to the "array"
+ * command.
+ */
+
+typedef struct ArraySearch {
+    int id;                    /* Integer id used to distinguish among
+                                * multiple concurrent searches for the
+                                * same array. */
+    struct Var *varPtr;                /* Pointer to array variable that's being
+                                * searched. */
+    Tcl_HashSearch search;     /* Info kept by the hash module about
+                                * progress through the array. */
+    Tcl_HashEntry *nextEntry;  /* Non-null means this is the next element
+                                * to be enumerated (it's leftover from
+                                * the Tcl_FirstHashEntry call or from
+                                * an "array anymore" command).  NULL
+                                * means must call Tcl_NextHashEntry
+                                * to get value to return. */
+    struct ArraySearch *nextPtr;/* Next in list of all active searches
+                                * for this variable, or NULL if this is
+                                * the last one. */
+} ArraySearch;
+
+/*
+ * The structure below defines a variable, which associates a string name
+ * with a string value.  Pointers to these structures are kept as the
+ * values of hash table entries, and the name of each variable is stored
+ * in the hash entry.
+ */
+
+typedef struct Var {
+    int valueLength;           /* Holds the number of non-null bytes
+                                * actually occupied by the variable's
+                                * current value in value.string (extra
+                                * space is sometimes left for expansion).
+                                * For array and global variables this is
+                                * meaningless. */
+    int valueSpace;            /* Total number of bytes of space allocated
+                                * at value.string.  0 means there is no
+                                * space allocated. */
+    union {
+       char *string;           /* String value of variable, used for scalar
+                                * variables and array elements.  Malloc-ed. */
+       Tcl_HashTable *tablePtr;/* For array variables, this points to
+                                * information about the hash table used
+                                * to implement the associative array. 
+                                * Points to malloc-ed data. */
+       struct Var *upvarPtr;   /* If this is a global variable being
+                                * referred to in a procedure, or a variable
+                                * created by "upvar", this field points to
+                                * the record for the higher-level variable. */
+    } value;
+    Tcl_HashEntry *hPtr;       /* Hash table entry that refers to this
+                                * variable, or NULL if the variable has
+                                * been detached from its hash table (e.g.
+                                * an array is deleted, but some of its
+                                * elements are still referred to in upvars). */
+    int refCount;              /* Counts number of active uses of this
+                                * variable, not including its main hash
+                                * table entry: 1 for each additional variable
+                                * whose upVarPtr points here, 1 for each
+                                * nested trace active on variable.  This
+                                * record can't be deleted until refCount
+                                * becomes 0. */
+    VarTrace *tracePtr;                /* First in list of all traces set for this
+                                * variable. */
+    ArraySearch *searchPtr;    /* First in list of all searches active
+                                * for this variable, or NULL if none. */
+    int flags;                 /* Miscellaneous bits of information about
+                                * variable.  See below for definitions. */
+} Var;
+
+/*
+ * Flag bits for variables:
+ *
+ * VAR_ARRAY   -               1 means this is an array variable rather
+ *                             than a scalar variable.
+ * VAR_UPVAR -                         1 means this variable just contains a
+ *                             pointer to another variable that has the
+ *                             real value.  Variables like this come
+ *                             about through the "upvar" and "global"
+ *                             commands.
+ * VAR_UNDEFINED -             1 means that the variable is currently
+ *                             undefined.  Undefined variables usually
+ *                             go away completely, but if an undefined
+ *                             variable has a trace on it, or if it is
+ *                             a global variable being used by a procedure,
+ *                             then it stays around even when undefined.
+ * VAR_TRACE_ACTIVE -          1 means that trace processing is currently
+ *                             underway for a read or write access, so
+ *                             new read or write accesses should not cause
+ *                             trace procedures to be called and the
+ *                             variable can't be deleted.
+ */
+
+#define VAR_ARRAY              1
+#define VAR_UPVAR              2
+#define VAR_UNDEFINED          4
+#define VAR_TRACE_ACTIVE       0x10
+
+/*
+ *----------------------------------------------------------------
+ * Data structures related to procedures.   These are used primarily
+ * in tclProc.c
+ *----------------------------------------------------------------
+ */
+
+/*
+ * The structure below defines an argument to a procedure, which
+ * consists of a name and an (optional) default value.
+ */
+
+typedef struct Arg {
+    struct Arg *nextPtr;       /* Next argument for this procedure,
+                                * or NULL if this is the last argument. */
+    char *defValue;            /* Pointer to arg's default value, or NULL
+                                * if no default value. */
+    char name[4];              /* Name of argument starts here.  The name
+                                * is followed by space for the default,
+                                * if there is one.  The actual size of this
+                                * field will be as large as necessary to
+                                * hold both name and default value.  THIS
+                                * MUST BE THE LAST FIELD IN THE STRUCTURE!! */
+} Arg;
+
+/*
+ * The structure below defines a command procedure, which consists of
+ * a collection of Tcl commands plus information about arguments and
+ * variables.
+ */
+
+typedef struct Proc {
+    struct Interp *iPtr;       /* Interpreter for which this command
+                                * is defined. */
+    int refCount;              /* Reference count:  1 if still present
+                                * in command table plus 1 for each call
+                                * to the procedure that is currently
+                                * active.  This structure can be freed
+                                * when refCount becomes zero. */
+    char *command;             /* Command that constitutes the body of
+                                * the procedure (dynamically allocated). */
+    Arg *argPtr;               /* Pointer to first of procedure's formal
+                                * arguments, or NULL if none. */
+} Proc;
+
+/*
+ * The structure below defines a command trace.  This is used to allow Tcl
+ * clients to find out whenever a command is about to be executed.
+ */
+
+typedef struct Trace {
+    int level;                 /* Only trace commands at nesting level
+                                * less than or equal to this. */
+    Tcl_CmdTraceProc *proc;    /* Procedure to call to trace command. */
+    ClientData clientData;     /* Arbitrary value to pass to proc. */
+    struct Trace *nextPtr;     /* Next in list of traces for this interp. */
+} Trace;
+
+/*
+ * The stucture below defines a deletion callback, which is
+ * a procedure to invoke just before an interpreter is deleted.
+ */
+
+typedef struct DeleteCallback {
+    Tcl_InterpDeleteProc *proc;        /* Procedure to call. */
+    ClientData clientData;     /* Value to pass to procedure. */
+    struct DeleteCallback *nextPtr;
+                               /* Next in list of callbacks for this
+                                * interpreter (or NULL for end of list). */
+} DeleteCallback;
+
+/*
+ * The structure below defines a frame, which is a procedure invocation.
+ * These structures exist only while procedures are being executed, and
+ * provide a sort of call stack.
+ */
+
+typedef struct CallFrame {
+    Tcl_HashTable varTable;    /* Hash table containing all of procedure's
+                                * local variables. */
+    int level;                 /* Level of this procedure, for "uplevel"
+                                * purposes (i.e. corresponds to nesting of
+                                * callerVarPtr's, not callerPtr's).  1 means
+                                * outer-most procedure, 0 means top-level. */
+    int argc;                  /* This and argv below describe name and
+                                * arguments for this procedure invocation. */
+    char **argv;               /* Array of arguments. */
+    struct CallFrame *callerPtr;
+                               /* Value of interp->framePtr when this
+                                * procedure was invoked (i.e. next in
+                                * stack of all active procedures). */
+    struct CallFrame *callerVarPtr;
+                               /* Value of interp->varFramePtr when this
+                                * procedure was invoked (i.e. determines
+                                * variable scoping within caller;  same
+                                * as callerPtr unless an "uplevel" command
+                                * or something equivalent was active in
+                                * the caller). */
+} CallFrame;
+
+/*
+ * The structure below defines one history event (a previously-executed
+ * command that can be re-executed in whole or in part).
+ */
+
+typedef struct {
+    char *command;             /* String containing previously-executed
+                                * command. */
+    int bytesAvl;              /* Total # of bytes available at *event (not
+                                * all are necessarily in use now). */
+} HistoryEvent;
+
+/*
+ *----------------------------------------------------------------
+ * Data structures related to history.   These are used primarily
+ * in tclHistory.c
+ *----------------------------------------------------------------
+ */
+
+/*
+ * The structure below defines a pending revision to the most recent
+ * history event.  Changes are linked together into a list and applied
+ * during the next call to Tcl_RecordHistory.  See the comments at the
+ * beginning of tclHistory.c for information on revisions.
+ */
+
+typedef struct HistoryRev {
+    int firstIndex;            /* Index of the first byte to replace in
+                                * current history event. */
+    int lastIndex;             /* Index of last byte to replace in
+                                * current history event. */
+    int newSize;               /* Number of bytes in newBytes. */
+    char *newBytes;            /* Replacement for the range given by
+                                * firstIndex and lastIndex (malloced). */
+    struct HistoryRev *nextPtr;        /* Next in chain of revisions to apply, or
+                                * NULL for end of list. */
+} HistoryRev;
+
+/*
+ *----------------------------------------------------------------
+ * Data structures related to files.  These are used primarily in
+ * tclUnixUtil.c and tclUnixAZ.c.
+ *----------------------------------------------------------------
+ */
+
+/*
+ * The data structure below defines an open file (or connection to
+ * a process pipeline) as returned by the "open" command.
+ */
+
+typedef struct OpenFile {
+    FILE *f;                   /* Stdio file to use for reading and/or
+                                * writing. */
+    FILE *f2;                  /* Normally NULL.  In the special case of
+                                * a command pipeline with pipes for both
+                                * input and output, this is a stdio file
+                                * to use for writing to the pipeline. */
+    int permissions;           /* OR-ed combination of TCL_FILE_READABLE
+                                * and TCL_FILE_WRITABLE. */
+    int numPids;               /* If this is a connection to a process
+                                * pipeline, gives number of processes
+                                * in pidPtr array below;  otherwise it
+                                * is 0. */
+    int *pidPtr;               /* Pointer to malloc-ed array of child
+                                * process ids (numPids of them), or NULL
+                                * if this isn't a connection to a process
+                                * pipeline. */
+    int errorId;               /* File id of file that receives error
+                                * output from pipeline.  -1 means not
+                                * used (i.e. this is a normal file). */
+} OpenFile;
+
+/*
+ *----------------------------------------------------------------
+ * Data structures related to expressions.  These are used only in
+ * tclExpr.c.
+ *----------------------------------------------------------------
+ */
+
+/*
+ * The data structure below defines a math function (e.g. sin or hypot)
+ * for use in Tcl expressions.
+ */
+
+#define MAX_MATH_ARGS 5
+typedef struct MathFunc {
+    int numArgs;               /* Number of arguments for function. */
+    Tcl_ValueType argTypes[MAX_MATH_ARGS];
+                               /* Acceptable types for each argument. */
+    Tcl_MathProc *proc;                /* Procedure that implements this function. */
+    ClientData clientData;     /* Additional argument to pass to the function
+                                * when invoking it. */
+} MathFunc;
+
+/*
+ *----------------------------------------------------------------
+ * One of the following structures exists for each command in
+ * an interpreter.  The Tcl_Command opaque type actually refers
+ * to these structures.
+ *----------------------------------------------------------------
+ */
+
+typedef struct Command {
+    Tcl_HashEntry *hPtr;       /* Pointer to the hash table entry in
+                                * interp->commandTable that refers to
+                                * this command.  Used to get a command's
+                                * name from its Tcl_Command handle.  NULL
+                                * means that the hash table entry has
+                                * been removed already (this can happen
+                                * if deleteProc causes the command to be
+                                * deleted or recreated). */
+    Tcl_CmdProc *proc;         /* Procedure to process command. */
+    ClientData clientData;     /* Arbitrary value to pass to proc. */
+    Tcl_CmdDeleteProc *deleteProc;
+                               /* Procedure to invoke when deleting
+                                * command. */
+    ClientData deleteData;     /* Arbitrary value to pass to deleteProc
+                                * (usually the same as clientData). */
+    int deleted;               /* Means that the command is in the process
+                                * of being deleted (its deleteProc is
+                                * currently executing).  Any other attempts
+                                * to delete the command should be ignored. */
+} Command;
+
+/*
+ *----------------------------------------------------------------
+ * This structure defines an interpreter, which is a collection of
+ * commands plus other state information related to interpreting
+ * commands, such as variable storage.  Primary responsibility for
+ * this data structure is in tclBasic.c, but almost every Tcl
+ * source file uses something in here.
+ *----------------------------------------------------------------
+ */
+
+typedef struct Interp {
+
+    /*
+     * Note:  the first three fields must match exactly the fields in
+     * a Tcl_Interp struct (see tcl.h).  If you change one, be sure to
+     * change the other.
+     */
+
+    char *result;              /* Points to result returned by last
+                                * command. */
+    Tcl_FreeProc *freeProc;    /* Zero means result is statically allocated.
+                                * If non-zero, gives address of procedure
+                                * to invoke to free the result.  Must be
+                                * freed by Tcl_Eval before executing next
+                                * command. */
+    int errorLine;             /* When TCL_ERROR is returned, this gives
+                                * the line number within the command where
+                                * the error occurred (1 means first line). */
+    Tcl_HashTable commandTable;        /* Contains all of the commands currently
+                                * registered in this interpreter.  Indexed
+                                * by strings; values have type (Command *). */
+    Tcl_HashTable mathFuncTable;/* Contains all of the math functions currently
+                                * defined for the interpreter.  Indexed by
+                                * strings (function names);  values have
+                                * type (MathFunc *). */
+
+    /*
+     * Information related to procedures and variables.  See tclProc.c
+     * and tclvar.c for usage.
+     */
+
+    Tcl_HashTable globalTable; /* Contains all global variables for
+                                * interpreter. */
+    int numLevels;             /* Keeps track of how many nested calls to
+                                * Tcl_Eval are in progress for this
+                                * interpreter.  It's used to delay deletion
+                                * of the table until all Tcl_Eval invocations
+                                * are completed. */
+    int maxNestingDepth;       /* If numLevels exceeds this value then Tcl
+                                * assumes that infinite recursion has
+                                * occurred and it generates an error. */
+    CallFrame *framePtr;       /* Points to top-most in stack of all nested
+                                * procedure invocations.  NULL means there
+                                * are no active procedures. */
+    CallFrame *varFramePtr;    /* Points to the call frame whose variables
+                                * are currently in use (same as framePtr
+                                * unless an "uplevel" command is being
+                                * executed).  NULL means no procedure is
+                                * active or "uplevel 0" is being exec'ed. */
+    ActiveVarTrace *activeTracePtr;
+                               /* First in list of active traces for interp,
+                                * or NULL if no active traces. */
+    int returnCode;            /* Completion code to return if current
+                                * procedure exits with a TCL_RETURN code. */
+    char *errorInfo;           /* Value to store in errorInfo if returnCode
+                                * is TCL_ERROR.  Malloc'ed, may be NULL */
+    char *errorCode;           /* Value to store in errorCode if returnCode
+                                * is TCL_ERROR.  Malloc'ed, may be NULL */
+
+    /*
+     * Information related to history:
+     */
+
+    int numEvents;             /* Number of previously-executed commands
+                                * to retain. */
+    HistoryEvent *events;      /* Array containing numEvents entries
+                                * (dynamically allocated). */
+    int curEvent;              /* Index into events of place where current
+                                * (or most recent) command is recorded. */
+    int curEventNum;           /* Event number associated with the slot
+                                * given by curEvent. */
+    HistoryRev *revPtr;                /* First in list of pending revisions. */
+    char *historyFirst;                /* First char. of current command executed
+                                * from history module or NULL if none. */
+    int revDisables;           /* 0 means history revision OK;  > 0 gives
+                                * a count of number of times revision has
+                                * been disabled. */
+    char *evalFirst;           /* If TCL_RECORD_BOUNDS flag set, Tcl_Eval
+                                * sets this field to point to the first
+                                * char. of text from which the current
+                                * command came.  Otherwise Tcl_Eval sets
+                                * this to NULL. */
+    char *evalLast;            /* Similar to evalFirst, except points to
+                                * last character of current command. */
+
+    /*
+     * Information used by Tcl_AppendResult to keep track of partial
+     * results.  See Tcl_AppendResult code for details.
+     */
+
+    char *appendResult;                /* Storage space for results generated
+                                * by Tcl_AppendResult.  Malloc-ed.  NULL
+                                * means not yet allocated. */
+    int appendAvl;             /* Total amount of space available at
+                                * partialResult. */
+    int appendUsed;            /* Number of non-null bytes currently
+                                * stored at partialResult. */
+
+    /*
+     * A cache of compiled regular expressions.  See Tcl_RegExpCompile
+     * in tclUtil.c for details.
+     */
+
+#define NUM_REGEXPS 5
+    char *patterns[NUM_REGEXPS];/* Strings corresponding to compiled
+                                * regular expression patterns.  NULL
+                                * means that this slot isn't used.
+                                * Malloc-ed. */
+    int patLengths[NUM_REGEXPS];/* Number of non-null characters in
+                                * corresponding entry in patterns.
+                                * -1 means entry isn't used. */
+    regexp *regexps[NUM_REGEXPS];
+                               /* Compiled forms of above strings.  Also
+                                * malloc-ed, or NULL if not in use yet. */
+
+    /*
+     * Information used by Tcl_PrintDouble:
+     */
+
+    char pdFormat[10];         /* Format string used by Tcl_PrintDouble. */
+    int pdPrec;                        /* Current precision (used to restore the
+                                * the tcl_precision variable after a bogus
+                                * value has been put into it). */
+
+    /*
+     * Miscellaneous information:
+     */
+
+    int cmdCount;              /* Total number of times a command procedure
+                                * has been called for this interpreter. */
+    int noEval;                        /* Non-zero means no commands should actually
+                                * be executed:  just parse only.  Used in
+                                * expressions when the result is already
+                                * determined. */
+    int evalFlags;             /* Flags to control next call to Tcl_Eval.
+                                * Normally zero, but may be set before
+                                * calling Tcl_Eval.  See below for valid
+                                * values. */
+    char *termPtr;             /* Character just after the last one in
+                                * a command.  Set by Tcl_Eval before
+                                * returning. */
+    char *scriptFile;          /* NULL means there is no nested source
+                                * command active;  otherwise this points to
+                                * the name of the file being sourced (it's
+                                * not malloc-ed:  it points to an argument
+                                * to Tcl_EvalFile. */
+    int flags;                 /* Various flag bits.  See below. */
+    Trace *tracePtr;           /* List of traces for this interpreter. */
+    DeleteCallback *deleteCallbackPtr;
+                               /* First in list of callbacks to invoke when
+                                * interpreter is deleted. */
+    char resultSpace[TCL_RESULT_SIZE+1];
+                               /* Static space for storing small results. */
+} Interp;
+
+/*
+ * EvalFlag bits for Interp structures:
+ *
+ * TCL_BRACKET_TERM    1 means that the current script is terminated by
+ *                     a close bracket rather than the end of the string.
+ * TCL_RECORD_BOUNDS   Tells Tcl_Eval to record information in the
+ *                     evalFirst and evalLast fields for each command
+ *                     executed directly from the string (top-level
+ *                     commands and those from command substitution).
+ * TCL_ALLOW_EXCEPTIONS        1 means it's OK for the script to terminate with
+ *                     a code other than TCL_OK or TCL_ERROR;  0 means
+ *                     codes other than these should be turned into errors.
+ */
+
+#define TCL_BRACKET_TERM       1
+#define TCL_RECORD_BOUNDS      2
+#define TCL_ALLOW_EXCEPTIONS   4
+
+/*
+ * Flag bits for Interp structures:
+ *
+ * DELETED:            Non-zero means the interpreter has been deleted:
+ *                     don't process any more commands for it, and destroy
+ *                     the structure as soon as all nested invocations of
+ *                     Tcl_Eval are done.
+ * ERR_IN_PROGRESS:    Non-zero means an error unwind is already in progress.
+ *                     Zero means a command proc has been invoked since last
+ *                     error occured.
+ * ERR_ALREADY_LOGGED: Non-zero means information has already been logged
+ *                     in $errorInfo for the current Tcl_Eval instance,
+ *                     so Tcl_Eval needn't log it (used to implement the
+ *                     "error message log" command).
+ * ERROR_CODE_SET:     Non-zero means that Tcl_SetErrorCode has been
+ *                     called to record information for the current
+ *                     error.  Zero means Tcl_Eval must clear the
+ *                     errorCode variable if an error is returned.
+ * EXPR_INITIALIZED:   1 means initialization specific to expressions has
+ *                     been carried out.
+ */
+
+#define DELETED                        1
+#define ERR_IN_PROGRESS                2
+#define ERR_ALREADY_LOGGED     4
+#define ERROR_CODE_SET         8
+#define EXPR_INITIALIZED       0x10
+
+/*
+ * Default value for the pdPrec and pdFormat fields of interpreters:
+ */
+
+#define DEFAULT_PD_PREC 6
+#define DEFAULT_PD_FORMAT "%g"
+
+/*
+ *----------------------------------------------------------------
+ * Data structures related to command parsing.   These are used in
+ * tclParse.c and its clients.
+ *----------------------------------------------------------------
+ */
+
+/*
+ * The following data structure is used by various parsing procedures
+ * to hold information about where to store the results of parsing
+ * (e.g. the substituted contents of a quoted argument, or the result
+ * of a nested command).  At any given time, the space available
+ * for output is fixed, but a procedure may be called to expand the
+ * space available if the current space runs out.
+ */
+
+typedef struct ParseValue {
+    char *buffer;              /* Address of first character in
+                                * output buffer. */
+    char *next;                        /* Place to store next character in
+                                * output buffer. */
+    char *end;                 /* Address of the last usable character
+                                * in the buffer. */
+    void (*expandProc) _ANSI_ARGS_((struct ParseValue *pvPtr, int needed));
+                               /* Procedure to call when space runs out;
+                                * it will make more space. */
+    ClientData clientData;     /* Arbitrary information for use of
+                                * expandProc. */
+} ParseValue;
+
+/*
+ * A table used to classify input characters to assist in parsing
+ * Tcl commands.  The table should be indexed with a signed character
+ * using the CHAR_TYPE macro.  The character may have a negative
+ * value.
+ */
+
+extern char tclTypeTable[];
+#define CHAR_TYPE(c) (tclTypeTable+128)[c]
+
+/*
+ * Possible values returned by CHAR_TYPE:
+ *
+ * TCL_NORMAL -                All characters that don't have special significance
+ *                     to the Tcl language.
+ * TCL_SPACE -         Character is space, tab, or return.
+ * TCL_COMMAND_END -   Character is newline or null or semicolon or
+ *                     close-bracket.
+ * TCL_QUOTE -         Character is a double-quote.
+ * TCL_OPEN_BRACKET -  Character is a "[".
+ * TCL_OPEN_BRACE -    Character is a "{".
+ * TCL_CLOSE_BRACE -   Character is a "}".
+ * TCL_BACKSLASH -     Character is a "\".
+ * TCL_DOLLAR -                Character is a "$".
+ */
+
+#define TCL_NORMAL             0
+#define TCL_SPACE              1
+#define TCL_COMMAND_END                2
+#define TCL_QUOTE              3
+#define TCL_OPEN_BRACKET       4
+#define TCL_OPEN_BRACE         5
+#define TCL_CLOSE_BRACE                6
+#define TCL_BACKSLASH          7
+#define TCL_DOLLAR             8
+
+/*
+ * Maximum number of levels of nesting permitted in Tcl commands (used
+ * to catch infinite recursion).
+ */
+
+#define MAX_NESTING_DEPTH      1000
+
+/*
+ * The macro below is used to modify a "char" value (e.g. by casting
+ * it to an unsigned character) so that it can be used safely with
+ * macros such as isspace.
+ */
+
+#define UCHAR(c) ((unsigned char) (c))
+
+/*
+ * Given a size or address, the macro below "aligns" it to the machine's
+ * memory unit size (e.g. an 8-byte boundary) so that anything can be
+ * placed at the aligned address without fear of an alignment error.
+ */
+
+#define TCL_ALIGN(x) ((x + 7) & ~7)
+
+/*
+ * Variables shared among Tcl modules but not used by the outside
+ * world:
+ */
+
+extern int             tclNumFiles;
+extern OpenFile **     tclOpenFiles;
+
+/*
+ *----------------------------------------------------------------
+ * Procedures shared among Tcl modules but not used by the outside
+ * world:
+ *----------------------------------------------------------------
+ */
+
+extern void            panic();
+extern void            TclCopyAndCollapse _ANSI_ARGS_((int count, char *src,
+                           char *dst));
+extern void            TclDeleteVars _ANSI_ARGS_((Interp *iPtr,
+                           Tcl_HashTable *tablePtr));
+extern void            TclExpandParseValue _ANSI_ARGS_((ParseValue *pvPtr,
+                           int needed));
+extern void            TclExprFloatError _ANSI_ARGS_((Tcl_Interp *interp,
+                           double value));
+extern int             TclFindElement _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *list, char **elementPtr, char **nextPtr,
+                           int *sizePtr, int *bracePtr));
+extern Proc *          TclFindProc _ANSI_ARGS_((Interp *iPtr,
+                           char *procName));
+extern int             TclGetFrame _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *string, CallFrame **framePtrPtr));
+extern int             TclGetListIndex _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *string, int *indexPtr));
+extern Proc *          TclIsProc _ANSI_ARGS_((Command *cmdPtr));
+extern int             TclNeedSpace _ANSI_ARGS_((char *start, char *end));
+extern int             TclParseBraces _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *string, char **termPtr, ParseValue *pvPtr));
+extern int             TclParseNestedCmd _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *string, int flags, char **termPtr,
+                           ParseValue *pvPtr));
+extern int             TclParseQuotes _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *string, int termChar, int flags,
+                           char **termPtr, ParseValue *pvPtr));
+extern int             TclParseWords _ANSI_ARGS_((Tcl_Interp *interp,
+                           char *string, int flags, int maxWords,
+                           char **termPtr, int *argcPtr, char **argv,
+                           ParseValue *pvPtr));
+extern char *          TclPrecTraceProc _ANSI_ARGS_((ClientData clientData,
+                           Tcl_Interp *interp, char *name1, char *name2,
+                           int flags));
+extern void            TclSetupEnv _ANSI_ARGS_((Tcl_Interp *interp));
+extern int             TclUpdateReturnInfo _ANSI_ARGS_((Interp *iPtr));
+extern char *          TclWordEnd _ANSI_ARGS_((char *start, int nested,
+                           int *semiPtr));
+
+/*
+ *----------------------------------------------------------------
+ * Command procedures in the generic core:
+ *----------------------------------------------------------------
+ */
+
+extern int     Tcl_AppendCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_ArrayCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_BreakCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_CaseCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_CatchCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_ConcatCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_ContinueCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_ErrorCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_EvalCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_ExprCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_ForCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_ForeachCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_FormatCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_GlobalCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_HistoryCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_IfCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_IncrCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_InfoCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_JoinCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_LappendCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_LindexCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_LinsertCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_LlengthCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_ListCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_LrangeCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_LreplaceCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_LsearchCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_LsortCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_ProcCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_RegexpCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_RegsubCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_RenameCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_ReturnCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_ScanCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_SetCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_SplitCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_StringCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_SubstCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_SwitchCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_TraceCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_UnsetCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_UplevelCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_UpvarCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_WhileCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_Cmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_Cmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+
+/*
+ *----------------------------------------------------------------
+ * Command procedures in the UNIX core:
+ *----------------------------------------------------------------
+ */
+
+extern int     Tcl_CdCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_CloseCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_EofCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_ExecCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_ExitCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_FileCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_FlushCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_GetsCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_GlobCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_OpenCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_PutsCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_PidCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_PwdCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_ReadCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_SeekCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_SourceCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_TellCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+extern int     Tcl_TimeCmd _ANSI_ARGS_((ClientData clientData,
+                   Tcl_Interp *interp, int argc, char **argv));
+
+#endif /* _TCLINT */
diff --git a/include/tclRegexp.h b/include/tclRegexp.h
new file mode 100644 (file)
index 0000000..1567919
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Definitions etc. for regexp(3) routines.
+ *
+ * Caveat:  this is V8 regexp(3) [actually, a reimplementation thereof],
+ * not the System V one.
+ *
+ * @(#) tclRegexp.h 1.4 95/05/03 17:07:16
+ */
+
+#ifndef _REGEXP
+#define _REGEXP 1
+
+#ifndef _TCL
+#include "tcl.h"
+#endif
+
+#define NSUBEXP  50
+typedef struct regexp {
+       char *startp[NSUBEXP];
+       char *endp[NSUBEXP];
+       char regstart;          /* Internal use only. */
+       char reganch;           /* Internal use only. */
+       char *regmust;          /* Internal use only. */
+       int regmlen;            /* Internal use only. */
+       char program[1];        /* Unwarranted chumminess with compiler. */
+} regexp;
+
+EXTERN regexp *TclRegComp _ANSI_ARGS_((char *exp));
+EXTERN int TclRegExec _ANSI_ARGS_((regexp *prog, char *string, char *start));
+EXTERN void TclRegSub _ANSI_ARGS_((regexp *prog, char *source, char *dest));
+EXTERN void TclRegError _ANSI_ARGS_((char *msg));
+EXTERN char *TclGetRegError _ANSI_ARGS_((void));
+
+#endif /* REGEXP */
diff --git a/index.html b/index.html
new file mode 100755 (executable)
index 0000000..eab9be7
--- /dev/null
@@ -0,0 +1,169 @@
+<html>
+<head>
+<title>FastCGI Developer's Kit Index Page</title>
+</head>
+
+<body>
+<center>
+<h2>FastCGI Developer's Kit Index Page</h2>
+</center>
+
+<!--Copyright (c) 1996 Open Market, Inc.                                    -->
+<!--See the file "LICENSE.TERMS" for information on usage and redistribution-->
+<!--of this file, and for a DISCLAIMER OF ALL WARRANTIES.                   -->
+<!-- $Id: index.html,v 1.1 1997/09/16 15:36:25 stanleyg Exp $ -->
+
+<ul>
+    <li><a HREF = "doc">doc/</a>
+        directory.
+    <ul>
+        <li><a href = "doc/fastcgi-whitepaper/fastcgi.htm">FastCGI
+            Technical White Paper.</a>  Start here.<br>
+            Motivates FastCGI, then explains the FastCGI interface,
+            FastCGI application roles, the FastCGI application
+            library, server support for FastCGI, and FastCGI performance.
+        <li><a href = "doc/fcgi-perf.htm">Understanding FastCGI
+            Application Performance</a> document.<br>
+            Why FastCGI applications often run faster than applications
+            coded directly to Web server APIs.
+        <li><a HREF = "doc/fcgi-devel-kit.htm">FastCGI Developer's Kit</a>
+            document.<br>
+            <ul>
+                <li>Describes how to configure and build the
+                    kit for your development platform.
+                <li>Tells how to write applications using the
+                    libraries in the kit.
+                <li>Documents <tt>cgi-fcgi</tt>, a tool in the kit
+                    that allows you to develop and test FastCGI applications
+                    using a Web server that lacks FastCGI support.
+            </ul>
+        <li><a href = "doc/fastcgi-prog-guide/cover.htm">Open Market
+             FastCGI 1.0 Programmer's Guide.</a><br>
+             Programmer-oriented documentation for developers of
+             applications that run on the Open Market's Secure
+             WebServer 2.0.  The content overlaps considerably with Section 3
+             of the Developer's Kit document.
+        <li><a HREF = "doc/FCGI_Accept.3">FCGI_Accept.3</a>,
+            <a HREF = "doc/FCGI_Finish.3">FCGI_Finish.3</a>,
+            <a HREF = "doc/FCGI_SetExitStatus.3">FCGI_SetExitStatus.3</a>,
+            <a HREF = "doc/FCGI_StartFilterData.3">FCGI_StartFilterData.3</a>,
+                 and
+            <a HREF = "doc/cgi-fcgi.1">cgi-fcgi.1</a> manpages.
+       <li><a HREF = "doc/fcgi-perl.htm">Integrating FastCGI with Perl-5</a>
+            document.<br>
+            How to build FastCGI support into the Perl-5 interpreter
+             and how to write FastCGI applications in Perl.
+       <li> <a HREF = "doc/fcgi-tcl.htm">Integrating FastCGI with Tcl</a>
+            document.<br>
+            How to build FastCGI support into the Tcl interpreter
+             and how to write FastCGI applications in Tcl.
+       <li> <a HREF = "doc/fcgi-java.htm">Integrating FastCGI with Java</a>
+            document.<br>
+            How to build Web server applications in Java using
+             FastCGI.
+       <li><a HREF = "doc/www5-api-workshop.html">FastCGI:
+            A High-Performance Gateway Interface</a> document.<br>
+            Position paper presented at the workshop
+            "Programming the Web -- a search for APIs",
+            Fifth International World Wide Web Conference,
+            6 May 1996, Paris, France.  Short paper, addressed to an
+            audience of technical specialists.
+       <li><a HREF = "doc/fcgi-spec.html">FastCGI Specification</a>
+            document.<br>
+            Defines the interface between a FastCGI
+            application and a Web server that supports FastCGI.
+            This is dry stuff, not needed for writing applications!
+     </ul>
+    <li><a HREF = "include">include/</a>
+        directory.<br>
+        .h files for the FastCGI libraries.
+    <li><a HREF = "libfcgi">libfcgi/</a>
+        directory.<br>
+        .c files for the FastCGI libraries.
+    <li><a HREF = "examples">examples/</a>
+        directory.<br>
+        Several example programs.  The links below invoke the
+        programs.  Use the section that corresponds to the
+        Web server you have configured for FastCGI:
+        <ul>
+          <li>Open Market Secure WebServer:
+          <ul>
+            <li><a href="/apps/echo"><tt>echo</tt></a>,
+              an example program that returns a page containing all of its
+              inputs -- environment variables and <tt>stdin</tt>. (Source code:
+              <a href="examples/echo.c"><tt>echo.c</tt></a>.)
+              Notice the FastCGI process ID in the initial
+              environment (there are two <tt>echo</tt> processes
+              running).  Also notice the request variables
+              that carry ticketing information (<tt>SI_*</tt>);
+              the anonymous ticket has been forced into the URL so you
+              can see it.  Alter it if you wish -- see what happens.
+            <li><a href="/apps/echo-protected"><tt>echo</tt></a>,
+              the same program accessed via a URL that is access
+              controlled by a simple Authorizer.
+              (Source code:
+              <a href="examples/tiny-authorizer.c"><tt>tiny-authorizer.c</tt></a>.)
+              Authenticate with user ID <tt>fastcgi</tt> and
+              password <tt>sano</tt> to gain access.  In addition
+              to demonstrating Authorizer-based access control, this
+              application demonstrates Authorizer-controlled session affinity:
+              Use the query string
+              <a href="/apps/echo-protected?0"><tt>?0</tt></a>
+              to route your request to <tt>echo</tt> process 0,
+              and the query string
+              <a href="/apps/echo-protected?1"><tt>?1</tt></a>
+              to route your request to <tt>echo</tt> process 1.
+            <li><a href="/SampleStore/App"><tt>sample-store</tt></a>,
+              a more realistic example. (Source code:
+              <a href="examples/sample-store.c"><tt>sample-store.c</tt></a>.)
+              This shopping cart implementation demonstrates how
+              FastCGI applications can get high performance using
+              in-memory caching yet preserve data across crashes.
+              Request routing is controlled by the anonymous ticket in
+              the URL; if you erase your ticket you'll get a new
+              shopping cart.  Using <tt>ps</tt>, locate and kill the
+              two <tt>sample-store</tt> processes; the Web server will
+              restart them and the new processes will recover your
+              shopping cart.  The program also illustrates how a
+              single application can play multiple roles.
+          </ul>
+          <li>NCSA and Apache servers:
+          <ul>
+            <li><a href="examples/echo.fcg"><tt>echo</tt></a>,
+              an example program that returns a page containing all of its
+              inputs -- environment variables and <tt>stdin</tt>. (Source code:
+              <a href="examples/echo.c"><tt>echo.c</tt></a>.)
+          </ul>
+           <li>Any Web server, using <tt>cgi-fcgi</tt>:
+          <ul>
+            <li><a href="examples/echo.cgi"><tt>echo</tt></a>,
+              an example program that returns a page containing all of its
+              inputs -- environment variables and <tt>stdin</tt>. (Source code:
+              <a href="examples/echo.c"><tt>echo.c</tt></a>.)
+          </ul>
+        </ul>
+    <li><a HREF = "perl-5">perl-5/</a>
+        directory.<br>
+        Bits for building FastCGI-integrated Perl-5 if you can't
+        use the already-built Perl-5 interpreters from
+        <a href="http://www.fastcgi.com/applibs/perl-5">www.fastcgi.com</a>.
+    <li><a HREF = "tcl">tcl/</a>
+        directory.<br>
+        Bits for building FastCGI-integrated Tcl if you can't
+        use the already-built Tcl interpreters from
+        <a href="http://www.fastcgi.com/applibs/tcl">www.fastcgi.com</a>.
+    <li><a HREF = "java/src">java/src</a>
+        directory.<br>
+        Source code for FastCGI Java classes.
+    <li><a HREF = "cgi-fcgi">cgi-fcgi/</a>
+        directory.<br>
+        .c file for the CGI-to-FastCGI bridge.
+</ul>
+
+<hr>
+<address>
+&#169 1996, Open Market, Inc.
+</address>
+
+</body>
+</html>
diff --git a/install.sh b/install.sh
new file mode 100755 (executable)
index 0000000..0ff4b6a
--- /dev/null
@@ -0,0 +1,119 @@
+#!/bin/sh
+
+#
+# install - install a program, script, or datafile
+# This comes from X11R5; it is not part of GNU.
+#
+# $XConsortium: install.sh,v 1.2 89/12/18 14:47:22 jim Exp $
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+
+instcmd="$mvprog"
+chmodcmd=""
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+       -c) instcmd="$cpprog"
+           shift
+           continue;;
+
+       -m) chmodcmd="$chmodprog $2"
+           shift
+           shift
+           continue;;
+
+       -o) chowncmd="$chownprog $2"
+           shift
+           shift
+           continue;;
+
+       -g) chgrpcmd="$chgrpprog $2"
+           shift
+           shift
+           continue;;
+
+       -s) stripcmd="$stripprog"
+           shift
+           continue;;
+
+       *)  if [ x"$src" = x ]
+           then
+               src=$1
+           else
+               dst=$1
+           fi
+           shift
+           continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+       echo "install:  no input file specified"
+       exit 1
+fi
+
+if [ x"$dst" = x ]
+then
+       echo "install:  no destination specified"
+       exit 1
+fi
+
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+if [ -d $dst ]
+then
+       dst="$dst"/`basename $src`
+fi
+
+# Make a temp file name in the proper directory.
+
+dstdir=`dirname $dst`
+dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+$doit $instcmd $src $dsttmp
+
+# and set any options; do chmod last to preserve setuid bits
+
+if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; fi
+if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; fi
+if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; fi
+if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; fi
+
+# Now rename the file to the real destination.
+
+$doit $rmcmd $dst
+$doit $mvcmd $dsttmp $dst
+
+
+exit 0
diff --git a/java/classes/FCGIGlobalDefs.class b/java/classes/FCGIGlobalDefs.class
new file mode 100644 (file)
index 0000000..0455309
Binary files /dev/null and b/java/classes/FCGIGlobalDefs.class differ
diff --git a/java/classes/FCGIInputStream.class b/java/classes/FCGIInputStream.class
new file mode 100644 (file)
index 0000000..780596a
Binary files /dev/null and b/java/classes/FCGIInputStream.class differ
diff --git a/java/classes/FCGIInterface.class b/java/classes/FCGIInterface.class
new file mode 100644 (file)
index 0000000..bb85213
Binary files /dev/null and b/java/classes/FCGIInterface.class differ
diff --git a/java/classes/FCGIMessage.class b/java/classes/FCGIMessage.class
new file mode 100644 (file)
index 0000000..26016cf
Binary files /dev/null and b/java/classes/FCGIMessage.class differ
diff --git a/java/classes/FCGIOutputStream.class b/java/classes/FCGIOutputStream.class
new file mode 100644 (file)
index 0000000..d0ffb2a
Binary files /dev/null and b/java/classes/FCGIOutputStream.class differ
diff --git a/java/classes/FCGIRequest.class b/java/classes/FCGIRequest.class
new file mode 100644 (file)
index 0000000..a17c9fb
Binary files /dev/null and b/java/classes/FCGIRequest.class differ
diff --git a/java/src/FCGIGlobalDefs.java b/java/src/FCGIGlobalDefs.java
new file mode 100644 (file)
index 0000000..f74b19b
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * @(#)FCGIGlobalDefs.java     
+ *
+ *
+ *      FastCGi compatibility package Interface
+ *
+ *
+ *  Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+
+
+/* This class contains FCGI global definitions corresponding to 
+ * the #defs in the C version.
+ */
+import java.io.PrintStream;
+public abstract class FCGIGlobalDefs {
+
+public static final int def_FCGIMaxLen = 0xffff;
+/* 
+ * Define Length of FCGI message bodies in bytes
+ */
+public static final int def_FCGIHeaderLen      = 8;
+public static final int def_FCGIEndReqBodyLen  = 8;
+public static final int def_FCGIBeginReqBodyLen = 8;
+public static final int def_FCGIUnknownBodyTypeBodyLen = 8;
+/* 
+ * Header defines
+ */
+public static int def_FCGIVersion1             = 1;
+       /* FCGI Record Types */
+public static final int def_FCGIBeginRequest   = 1;
+public static final int def_FCGIAbortRequest   = 2;
+public static final int def_FCGIEndRequest     = 3;
+public static final int def_FCGIParams                 = 4;
+public static final int def_FCGIStdin          = 5;
+public static final int def_FCGIStdout                 = 6;
+public static final int def_FCGIStderr                 = 7;
+public static final int def_FCGIData           = 8;
+public static final int def_FCGIGetValues      = 9;
+public static final int def_FCGIGetValuesResult = 10;
+public static final int def_FCGIUnknownType    = 11;
+public static final int def_FCGIMaxType = def_FCGIUnknownType;
+       /* Request ID Values */
+public static final int def_FCGINullRequestID  = 0;
+/*
+ * Begin Request defines
+*/
+       /* Mask flags */
+public static int def_FCGIKeepConn             = 1;
+       /* Roles */
+public static final int def_FCGIResponder      = 1;
+public static final int def_FCGIAuthorizer     = 2;
+public static final int def_FCGIFilter                 = 3;
+/*
+ * End Request defines
+ */
+       /* Protocol status */
+public static final int def_FCGIRequestComplete        = 0;
+public static final int def_FCGICantMpxConn    = 1;
+public static final int def_FCGIOverload       = 2;
+public static final int def_FCGIUnknownRole    = 3;
+/*
+ * Get Values, Get Values Results  defines
+ */
+public static final String def_FCGIMaxConns    = "FCGI_MAX_CONNS";
+public static final String def_FCGIMaxReqs     = "FCGI_MAX_REQS";
+public static final String def_FCGIMpxsConns   = "FCGI_MPXS_CONNS";
+/* 
+ * Return codes for Process* functions 
+ */
+public static final int def_FCGIStreamRecord   = 0;
+public static final int def_FCGISkip           = 1;
+public static final int def_FCGIBeginRecord    = 2;
+public static final int def_FCGIMgmtRecord = 3;
+/*
+ * Error Codes 
+ */
+public static final int def_FCGIUnsupportedVersion = -2;
+public static final int def_FCGIProtocolError  = -3;
+public static final int def_FCGIParamsError    = -4;
+public static final int def_FCGICallSeqError   = -5;
+}
\ No newline at end of file
diff --git a/java/src/FCGIInputStream.java b/java/src/FCGIInputStream.java
new file mode 100644 (file)
index 0000000..92e59d5
--- /dev/null
@@ -0,0 +1,384 @@
+/*
+ * @(#)FCGIInputStream.java    
+ *
+ *      FastCGi compatibility package Interface
+ *
+ *
+ *  Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+
+
+import java.io.*;
+import  FCGIRequest;
+import  FCGIGlobalDefs;
+
+
+/**
+ * This stream manages buffered reads of FCGI messages.
+ *
+ *
+ */
+public
+class FCGIInputStream extends InputStream {
+
+ /* Stream vars */
+
+    public int rdNext;
+    public int stop;
+    public boolean isClosed;
+
+/* require methods to set, get and clear */
+    private int errno;         
+    private Exception errex;
+
+/* data vars */
+
+    public byte buff[];
+    public int buffLen;
+    public int buffStop;
+    public int type;
+    public int contentLen;
+    public int paddingLen;
+    public boolean skip;
+    public boolean eorStop;
+    public FCGIRequest request;
+
+    public InputStream in;
+
+
+    /**
+     * Creates a new input stream to manage fcgi prototcol stuff
+     * @param in the input stream  bufLen  length of buffer streamType   
+     */
+    public FCGIInputStream(FileInputStream inStream, int bufLen, 
+                                                       int streamType, 
+                                                       FCGIRequest inReq) {
+
+       in = inStream;
+       buffLen = Math.min(bufLen,FCGIGlobalDefs.def_FCGIMaxLen);
+       buff = new byte[buffLen];
+       type = streamType;
+       stop = rdNext = buffStop = 0;
+       isClosed = false;
+       contentLen = 0;
+       paddingLen = 0; 
+       skip = false;
+       eorStop = false;
+       request = inReq;
+
+    }
+  /**  
+     * Reads a byte of data. This method will block if no input is 
+     * available.
+     * @return         the byte read, or -1 if the end of the
+     *         stream is reached.
+     * @exception IOException If an I/O error has occurred.
+     */
+    public int read() throws IOException {
+       if (rdNext != stop) {
+               return buff[rdNext++];
+               }
+       if (isClosed){
+               return -1;
+               }
+       fill();
+       if (rdNext != stop){
+               return buff[rdNext++];
+               }
+       return -1;
+    }
+    /**
+     * Reads into an array of bytes.  This method will
+     * block until some input is available.
+     * @param b        the buffer into which the data is read
+     * @return  the actual number of bytes read, -1 is
+     *                 returned when the end of the stream is reached.
+     * @exception IOException If an I/O error has occurred.
+     */
+    public int read(byte b[]) throws IOException {
+       return read(b, 0, b.length);
+    }
+
+       
+
+   /**
+     * Reads into an array of bytes.
+     * Blocks until some input is available.
+     * @param b        the buffer into which the data is read
+     * @param off the start offset of the data
+     * @param len the maximum number of bytes read
+     * @return  the actual number of bytes read, -1 is
+     *                 returned when the end of the stream is reached.
+     * @exception IOException If an I/O error has occurred.
+     */
+    public int read(byte b[], int off, int len) throws IOException {
+       int m, bytesMoved;
+       
+       if (len <= 0){
+               return 0;
+               }
+       /* 
+        *Fast path: len bytes already available.
+        */
+
+       if (len <= stop - rdNext){
+               System.arraycopy(buff, rdNext, b, off, len);
+               rdNext += len;
+               return len;
+               }
+       /* 
+        *General case: stream is closed or fill needs to be called 
+        */
+        bytesMoved = 0;
+       for(;;){
+               if (rdNext != stop){
+                  m = Math.min(len - bytesMoved, stop - rdNext);
+                  System.arraycopy(buff, rdNext, b, off, m);
+                  bytesMoved += m;
+                  rdNext += m;
+                  if (bytesMoved == len)
+                       return bytesMoved;
+                  off += m;
+                  }
+               if (isClosed){
+                  return bytesMoved;
+                  }
+               fill();
+
+       }
+ }
+    /**
+     * Reads into an array of bytes.  This method will
+     * block until some input is available.
+     * @param b        the buffer into which the data is read
+     * @param off the start offset of the data
+     * @param len the maximum number of bytes read
+     * @return  the actual number of bytes read, -1 is
+     *                 returned when the end of the stream is reached.
+     * @exception IOException If an I/O error has occurred.
+     */        
+    public void  fill() throws IOException {
+       byte[] headerBuf = new byte[FCGIGlobalDefs.def_FCGIHeaderLen];
+       int headerLen = 0;
+       int status = 0;
+       int count = 0;
+       for(;;) {
+       /* 
+        * If buffer is empty, do a read 
+        */
+       if (rdNext == buffStop) {               
+               try {
+                       count = in.read(buff, 0, buffLen); 
+               } catch (IOException e) {
+                       setException(e);
+                       return;
+                       }
+               if (count == 0) {
+                       setFCGIError(FCGIGlobalDefs.def_FCGIProtocolError);
+                       return;
+                       }
+               rdNext = 0;
+               buffStop = count;       // 1 more than we read
+               }
+       /* Now buf is not empty: If the current record contains more content 
+        * bytes, deliver all that are present in buff to callers buffer
+        * unless he asked for less than we have, in which case give him less 
+        */
+       if (contentLen > 0) {
+               count = Math.min(contentLen, buffStop - rdNext);
+               contentLen -= count;
+               if (!skip) {    
+                       stop = rdNext + count; 
+                       return;
+                } else {
+                       rdNext += count;
+                       if (contentLen > 0) { 
+                               continue;
+                       } else {
+                               skip = false;
+                               }
+                       }
+       }
+       /* Content has been consumed by client. 
+        * If record was padded, skip over padding 
+        */
+       if (paddingLen > 0) {
+               count = Math.min(paddingLen, buffStop - rdNext);
+               paddingLen -= count;
+               rdNext += count;
+               if (paddingLen > 0) {
+                       continue;    // more padding to read 
+                       }
+               }
+       /* All done with current record, including the padding.
+        * If we are in a recursive call from Process Header, deliver EOF
+        */
+       if (eorStop){           
+               stop = rdNext;
+               isClosed = true;
+               return;
+               }
+       /* 
+        * Fill header with bytes from input buffer - get the whole header.
+        */
+       count = Math.min(headerBuf.length - headerLen, buffStop - rdNext);
+       System.arraycopy(buff,rdNext, headerBuf, headerLen, count);
+       headerLen += count;
+       rdNext  += count;
+       if (headerLen < headerBuf.length) {
+               continue;
+               }
+       headerLen = 0;
+       /* 
+        * Interperet the header. eorStop prevents ProcessHeader from 
+        * reading past the end of record when using stream to read content 
+        */
+       eorStop = true;
+       stop = rdNext;
+       status = 0; 
+       status = new FCGIMessage(this).processHeader(headerBuf);
+       eorStop = false;
+       isClosed = false;
+       switch (status){
+               case FCGIGlobalDefs.def_FCGIStreamRecord:
+                       if (contentLen == 0) {
+                               stop = rdNext;
+                               isClosed = true;
+                               return;
+                               }               
+                 break; 
+               case FCGIGlobalDefs.def_FCGISkip:
+                 skip = true;
+                 break; 
+               case FCGIGlobalDefs.def_FCGIBeginRecord:
+               /* 
+                * If this header marked the beginning of a new 
+                * request, return role info to caller 
+                */
+                 return;
+               case FCGIGlobalDefs.def_FCGIMgmtRecord:
+                 break; 
+               default:
+               /* 
+                * ASSERT
+                */
+               setFCGIError(status);
+               return;
+               
+       }
+     }
+   }
+
+       /**
+        * Skips n bytes of input.
+        * @param n the number of bytes to be skipped
+        * @return      the actual number of bytes skipped.
+        * @exception IOException If an I/O error has occurred.
+        */
+       public long skip(long n) throws IOException {
+               byte data[] = new byte[(int)n];
+               return in.read(data);
+       }
+
+       /*
+        * An FCGI error has occurred. Save the error code in the stream
+        * for diagnostic purposes and set the stream state so that
+        * reads return EOF 
+        */
+       public void setFCGIError(int errnum) {
+       /*
+        * Preserve only the first error.
+        */
+       if(errno == 0) {
+               errno = errnum;
+               }
+               isClosed = true;
+       }
+       /*
+        * An Exception has occurred. Save the Exception in the stream
+        * for diagnostic purposes and set the stream state so that
+        * reads return EOF 
+        */
+       public void setException(Exception errexpt) {
+       /*
+        * Preserve only the first error.
+        */
+       if(errex == null) {
+               errex = errexpt;
+               }
+               isClosed = true;
+       }
+
+       /*
+        * Clear the stream error code and end-of-file indication.
+        */
+       public void clearFCGIError() {
+       errno = 0;      
+       /*
+        * isClosed = false;
+        * XXX: should clear isClosed but work is needed to make it safe
+        * to do so. 
+        */
+       }
+/*
+        * Clear the stream error code and end-of-file indication.
+        */
+       public void clearException() {
+       errex = null;
+       /*
+        * isClosed = false;
+        * XXX: should clear isClosed but work is needed to make it safe
+        * to do so. 
+        */
+       }
+
+       /* 
+        * accessor method since var is private
+        */
+       public int getFCGIError() {
+               return errno;
+       }
+       /* 
+        * accessor method since var is private
+        */
+       public Exception getException() {
+               return errex;
+       }
+       /*
+        * Re-initializes the stream to read data of the specified type.
+        */
+       public void setReaderType(int streamType) {
+
+       type = streamType;
+       eorStop = false;
+       skip = false;
+       contentLen = 0;
+       paddingLen = 0;
+       stop = rdNext;
+       isClosed = false;
+       }
+
+       /* 
+        * Close the stream. This method does not really exist for BufferedInputStream in java,
+        * but is implemented here for compatibility with the FCGI structures being used. It
+        * doent really throw any IOExceptions either, but that's there for compatiblity with 
+        * the InputStreamInterface.    
+        */
+       public void close() throws IOException{
+       isClosed = true;
+       stop = rdNext;
+       }
+       
+       /* 
+        * Returns the number of bytes that can be read without blocking.
+        */
+
+       public int available() throws IOException {     
+               return stop - rdNext + in.available();
+       }
+       
+}
\ No newline at end of file
diff --git a/java/src/FCGIInterface.java b/java/src/FCGIInterface.java
new file mode 100644 (file)
index 0000000..5870a25
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * @(#)FCGIInterface.java      
+ *
+ *
+ *      FastCGi compatibility package Interface
+ *
+ *
+ *  Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ */
+
+import java.net.*;
+import java.io.*;
+import java.util.Properties;
+import FCGIGlobalDefs;
+import FCGIRequest;
+import FCGIInputStream;
+import FCGIOutputStream;
+import FCGIMessage;
+
+
+/*
+ * This is the FastCGI interface that the application calls to communicate with the
+ * FastCGI web server. This version is single threaded, and handles one request at
+ * a time, which is why we can have a static variable for it.
+ */
+public class FCGIInterface {
+
+
+
+/*
+ * Class variables
+ */ 
+public static FCGIRequest request = null;
+public static boolean acceptCalled = false;
+public static boolean isFCGI = true;
+public static Properties startupProps;
+public static ServerSocket srvSocket;
+
+
+
+
+
+ /*
+ * Accepts a new request from the HTTP server and creates
+ * a conventional execution environment for the request.
+ * If the application was invoked as a FastCGI server,
+ * the first call to FCGIaccept indicates that the application
+ * has completed its initialization and is ready to accept
+ * a request.  Subsequent calls to FCGI_accept indicate that
+ * the application has completed its processing of the
+ * current request and is ready to accept a new request.
+ * If the application was invoked as a CGI program, the first
+ * call to FCGIaccept is essentially a no-op and the second
+ * call returns EOF (-1) as does an error. Application should exit.
+ *
+ * If the application was invoked as a FastCGI server,
+ * and this is not the first call to this procedure,
+ * FCGIaccept first flushes any buffered output to the HTTP server. 
+ *
+ * On every call, FCGIaccept accepts the new request and
+ * reads the FCGI_PARAMS stream into System.props. It also creates
+ * streams that understand FastCGI protocol and take input from 
+ * the HTTP server send output and error output to the HTTP server, 
+ * and assigns these new streams to System.in, System.out and  
+ * System.err respectively.
+ *
+ * For now, we will just return an int to the caller, which is why 
+ * this method catches, but doen't throw Exceptions.
+ * 
+ */
+
+public int FCGIaccept() {      
+       int acceptResult = 0;
+       
+       /* 
+        * If first call, mark it and if fcgi save original system properties,
+        * If not first call, and  we are cgi, we should be gone.
+        */
+       if (!acceptCalled){              
+               isFCGI = System.getProperties().containsKey("FCGI_PORT");
+               acceptCalled = true;
+               if (isFCGI) {
+                       /*
+                        * save original system properties (nonrequest)
+                        * and get a server socket
+                        */
+                       System.out.close();
+                       System.err.close();
+                       startupProps = new Properties(System.getProperties());
+                       String str = 
+                               new String(System.getProperty("FCGI_PORT"));
+                       if (str.length() <= 0) {
+                               return -1;
+                               }
+                       int portNum = Integer.parseInt(str);
+                       
+                       try {
+                               srvSocket = new ServerSocket(portNum);
+                       } catch (IOException e) { 
+                               request.socket = null;
+                               srvSocket = null;
+                               request = null;
+                               return -1;
+                               }                       
+                       }
+       } else { 
+               if (!isFCGI){   
+                       return -1; 
+                       }
+               }
+       /* 
+        * If we are cgi, just leave everything as is, otherwise set up env 
+        */
+       if (isFCGI){
+               try {
+                       acceptResult = FCGIAccept();
+               } catch (IOException e) {
+                       return -1;
+                       }
+               if (acceptResult < 0){
+                       return -1;
+                       }
+               
+               /*
+               * redirect stdin, stdout and stderr to fcgi socket 
+               */
+               System.in = new BufferedInputStream(request.inStream, 8192);
+               System.out = new PrintStream(new BufferedOutputStream(
+                                               request.outStream, 8192));
+               System.err = new PrintStream(new BufferedOutputStream(
+                                               request.errStream, 512));
+               System.setProperties(request.params);
+               }
+       return 0;
+       } 
+
+       /* 
+        * Accepts a new request from the HTTP server. 
+         * Finishes the request accepted by the previous call 
+         * to FCGI_Accept. Sets up the FCGI environment and reads
+        * saved and per request environmental varaibles into
+        * the request object. (This is redundant on System.props
+        * as long as we can handle only one request object.)
+        */
+       int FCGIAccept() throws IOException{
+
+       boolean isNewConnection;
+       boolean errCloseEx = false;     
+       boolean outCloseEx = false;
+
+       if (request != null) {  
+               /*
+               * Complete the previous request
+               * 
+               */
+               System.err.close();
+               System.out.close();     
+               boolean prevRequestfailed = (errCloseEx || outCloseEx ||
+                               request.inStream.getFCGIError() != 0 ||
+                               request.inStream.getException() != null);
+               if (prevRequestfailed || !request.keepConnection ) {
+                       request.socket.close();
+                       request.socket = null;
+                       }
+               if (prevRequestfailed) {
+                       request = null;
+                       return -1;
+                       }
+       } else  {
+          /*
+           * Get a Request and initialize some variables 
+           */
+               request = new FCGIRequest();
+               request.socket = null;
+               request.inStream = null;
+               }
+       isNewConnection = false;
+       
+       /*
+        * if connection isnt open accept a new connection (blocking)
+        */
+       for(;;) {
+               if (request.socket == null){
+                       try {                   
+                               request.socket = srvSocket.accept();
+                       } catch (IOException e) { 
+                               request.socket = null;
+                               request = null;
+                               return -1;
+                               }
+                       isNewConnection = true;
+                       }
+                               
+       /* Try reading from new connection. If the read fails and 
+        * it was an old connection the web server probably closed it; 
+        *try making a new connection before giving up
+        */
+       request.isBeginProcessed = false;
+       request.inStream = 
+               new FCGIInputStream((FileInputStream)request.
+                                                     socket.getInputStream(), 
+                                                           8192, 0, request);
+       request.inStream.fill();
+       if (request.isBeginProcessed) {
+               break;
+               }
+       request.socket.close();
+       
+       request.socket = null;
+       if (isNewConnection) {
+               return -1;
+               }       
+       } 
+       /* 
+        * Set up the objects for the new request
+
+       */
+       request.params = new Properties(startupProps);  
+       switch(request.role) {
+               case FCGIGlobalDefs.def_FCGIResponder:  
+                       request.params.put("ROLE","RESPONDER");
+                       break;
+               case FCGIGlobalDefs.def_FCGIAuthorizer:  
+                       request.params.put("ROLE", "AUTHORIZER");
+                       break;
+               case FCGIGlobalDefs.def_FCGIFilter:  
+                       request.params.put("ROLE", "FILTER");
+                       break;
+               default:
+                       return -1;
+               }
+       request.inStream.setReaderType(FCGIGlobalDefs.def_FCGIParams);
+       /* 
+        * read the rest of request parameters 
+        */
+       if (new FCGIMessage(request.inStream).readParams(request.params) < 0) {
+               return -1;
+               }
+       request.inStream.setReaderType(FCGIGlobalDefs.def_FCGIStdin);
+       request.outStream 
+               =  new FCGIOutputStream((FileOutputStream)request.socket.
+                                       getOutputStream(), 8192, 
+                                       FCGIGlobalDefs.def_FCGIStdout,request);
+       request.errStream 
+               = new FCGIOutputStream((FileOutputStream)request.socket.
+                                       getOutputStream(), 512, 
+                                       FCGIGlobalDefs.def_FCGIStderr,request);
+       request.numWriters = 2; 
+       return 0;
+   }   
+}
\ No newline at end of file
diff --git a/java/src/FCGIMessage.java b/java/src/FCGIMessage.java
new file mode 100644 (file)
index 0000000..bacdbde
--- /dev/null
@@ -0,0 +1,427 @@
+/*
+ * @(#)FCGIMessage.java        
+ *
+ *
+ *      FastCGi compatibility package Interface
+ *
+ *
+ *  Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+
+
+import java.io.*;
+import java.util.Properties;
+
+/* This class handles reading and building the fastcgi messages.
+ *  For reading incoming mesages, we pass the input 
+ * stream as a param to the constructor rather than to each method. 
+ * Methods that build messages use and return internal buffers, so they 
+ * dont need a stream.
+*/
+public class FCGIMessage {
+
+/*
+ * Instance variables
+ */
+/*
+ * FCGI Message Records
+ * The logical structures of the FCGI Message Records.
+ * Fields are originally 1 unsigned byte in message 
+ * unless otherwise noted.
+ */
+/*
+ * FCGI Header 
+ */
+private int  h_version;
+private int  h_type;
+private int  h_requestID;      // 2 bytes
+private int  h_contentLength;  // 2 bytes
+private int  h_paddingLength;
+private int  h_reserved;
+/* 
+ * FCGI BeginRequest body.
+*/
+private int  br_role;          // 2 bytes
+private int  br_flags;
+private byte br_reserved[];    // 5 bytes
+
+private FCGIInputStream in;
+
+       /* 
+        * constructor - Java would do this implicitly.  
+       */
+       public FCGIMessage(){
+               super();
+       }
+       /* 
+        * constructor - get the stream.  
+       */
+       public FCGIMessage(FCGIInputStream instream){
+               in = instream;
+       }
+
+       /* 
+        * Message Reading Methods 
+        */
+
+       /* 
+        * Interpret the FCGI Message Header. Processes FCGI 
+        * BeginRequest and Management messages. Param hdr is the header. 
+        * The calling routine has to keep track of the stream reading 
+        * management or use FCGIInputStream.fill() which does just that.
+        */
+       public int processHeader(byte[] hdr) throws IOException{
+               processHeaderBytes(hdr);
+               if (h_version != FCGIGlobalDefs.def_FCGIVersion1) {
+                       return(FCGIGlobalDefs.def_FCGIUnsupportedVersion);
+                       }
+               in.contentLen = h_contentLength;
+               in.paddingLen = h_paddingLength;
+               if (h_type == FCGIGlobalDefs.def_FCGIBeginRequest) {
+                       return processBeginRecord(h_requestID); 
+                       }
+               if (h_requestID == FCGIGlobalDefs.def_FCGINullRequestID) {
+                       return processManagementRecord(h_type); 
+                       }
+               if (h_requestID != in.request.requestID) { 
+                       return(FCGIGlobalDefs.def_FCGISkip);
+                       }
+               if (h_type != in.type) {
+                       return(FCGIGlobalDefs.def_FCGIProtocolError);
+                       }                        
+               return(FCGIGlobalDefs.def_FCGIStreamRecord);
+       }
+
+/* Put the unsigned bytes in the incoming FCGI header into 
+        * integer form for Java, concatinating bytes when needed, 
+        * and preserving the original byte value when appropriate.
+        * Dont let low byte sign extend if high byte is empty.
+        */
+       private void processHeaderBytes(byte[] hdrBuf){
+
+            h_version                  = (int)hdrBuf[0] & 0x000000ff;
+            h_type                     = (int)hdrBuf[1] & 0x000000ff;
+            h_requestID                =  (hdrBuf[2] << 8) + hdrBuf[3]; 
+            if ((hdrBuf[3] & 0x80) != 0  && (hdrBuf[2] & 0xff) == 0){
+                       h_requestID = h_requestID & 0x000000ff;
+            } else {
+                       h_requestID = h_requestID & 0x0000ffff;
+                       }
+            h_contentLength            =  (hdrBuf[4] << 8) + hdrBuf[5];
+            if ((hdrBuf[5] & 0x80) != 0  && (hdrBuf[4] & 0xff) == 0){
+                       h_contentLength = h_contentLength & 0x000000ff;
+            } else {
+                       h_contentLength = h_contentLength & 0x0000ffff;
+                       }       
+            h_paddingLength            = (int)hdrBuf[6] & 0x000000ff;
+            h_reserved                 = (int)hdrBuf[7] & 0x000000ff;
+       }
+
+       /*
+        * Reads FCGI Begin Request Record.
+        */
+       public int processBeginRecord(int requestID) throws IOException {
+               byte beginReqBody[];
+               byte endReqMsg[];
+               if (requestID == 0 || in.contentLen 
+                               != FCGIGlobalDefs.def_FCGIEndReqBodyLen) {
+                       return FCGIGlobalDefs.def_FCGIProtocolError;
+                       }
+               /* 
+                * If the webserver is multiplexing the connection,
+                * this library can't deal with it, so repond with 
+                * FCGIEndReq message with protocolStatus FCGICantMpxConn
+                */
+               if (in.request.isBeginProcessed) {
+                    endReqMsg = new byte[FCGIGlobalDefs.def_FCGIHeaderLen 
+                               + FCGIGlobalDefs.def_FCGIEndReqBodyLen]; 
+                    System.arraycopy(makeHeader(
+                                       FCGIGlobalDefs.def_FCGIEndRequest, 
+                                       requestID, 
+                                       FCGIGlobalDefs.def_FCGIEndReqBodyLen, 
+                                       0), 0,  endReqMsg, 0, 
+                                       FCGIGlobalDefs.def_FCGIHeaderLen);
+                    System.arraycopy(makeEndrequestBody(0, 
+                                       FCGIGlobalDefs.def_FCGICantMpxConn), 0,
+                                       endReqMsg, 
+                                       FCGIGlobalDefs.def_FCGIHeaderLen,
+                                       FCGIGlobalDefs.def_FCGIEndReqBodyLen);
+                  /*
+                   * since isBeginProcessed is first set below,this 
+                   * can't be out first call, so request.out is properly set
+                   */
+                    try {
+                       in.request.outStream.write(endReqMsg, 0,
+                               FCGIGlobalDefs.def_FCGIHeaderLen 
+                                     + FCGIGlobalDefs.def_FCGIEndReqBodyLen);
+                   } catch (IOException e){
+                       in.request.outStream.setException(e); 
+                       return -1;
+                       } 
+               }
+               /* 
+                * Accept this  new request. Read the record body 
+                */
+               in.request.requestID = requestID;               
+               beginReqBody = 
+                       new byte[FCGIGlobalDefs.def_FCGIBeginReqBodyLen];
+               if (in.read(beginReqBody, 0,
+                               FCGIGlobalDefs.def_FCGIBeginReqBodyLen) != 
+                                 FCGIGlobalDefs.def_FCGIBeginReqBodyLen) {
+                       return FCGIGlobalDefs.def_FCGIProtocolError;
+                       }
+               br_flags = ((int)beginReqBody[2]) & 0x000000ff; 
+               in.request.keepConnection 
+                       = (br_flags & FCGIGlobalDefs.def_FCGIKeepConn) != 0; 
+               br_role =  ((int)((beginReqBody[0] << 8) + beginReqBody[1])) 
+                                               & 0x0000ffff;   
+               in.request.role = br_role;
+               in.request.isBeginProcessed = true;
+               return FCGIGlobalDefs.def_FCGIBeginRecord;
+       }
+
+       /*
+       * Reads and Responds to a Management Message. The only type of 
+       * management message this library understands is FCGIGetValues. 
+       * The only variables that this library's FCGIGetValues understands 
+       * are def_FCGIMaxConns, def_FCGIMaxReqs, and def_FCGIMpxsConns. 
+       * Ignore the other management variables, and repsond to other 
+       * management messages with FCGIUnknownType.
+       */
+       public int processManagementRecord(int type) throws IOException {
+       
+               byte[] response = new byte[64];
+               int wrndx = response[FCGIGlobalDefs.def_FCGIHeaderLen];
+               int value, len, plen;    
+               if (type == FCGIGlobalDefs.def_FCGIGetValues) {
+                       Properties tmpProps = new Properties();
+                       readParams(tmpProps);
+
+                       if (in.getFCGIError() != 0 || in.contentLen != 0) {
+                               return FCGIGlobalDefs.def_FCGIProtocolError;
+                               }
+                       if (tmpProps.containsKey(
+                               FCGIGlobalDefs.def_FCGIMaxConns)) {
+                               makeNameVal(
+                                       FCGIGlobalDefs.def_FCGIMaxConns, "1",
+                                                       response, wrndx);
+                       } else {
+                       if (tmpProps.containsKey(
+                               FCGIGlobalDefs.def_FCGIMaxReqs)) {
+                               makeNameVal(
+                                       FCGIGlobalDefs.def_FCGIMaxReqs, "1", 
+                                                       response, wrndx);
+                       } else {
+                       if (tmpProps.containsKey(
+                               FCGIGlobalDefs.def_FCGIMaxConns)) {
+                               makeNameVal(
+                                       FCGIGlobalDefs.def_FCGIMpxsConns, "0", 
+                                                       response, wrndx);
+                               }
+                       }}
+                       plen = 64 - wrndx;
+                       len = wrndx - FCGIGlobalDefs.def_FCGIHeaderLen; 
+                       System.arraycopy(makeHeader(
+                                       FCGIGlobalDefs.def_FCGIGetValuesResult,
+                                       FCGIGlobalDefs.def_FCGINullRequestID,  
+                                       len, plen), 0,
+                                       response, 0, 
+                                       FCGIGlobalDefs.def_FCGIHeaderLen);
+               } else  {
+                       plen = len = 
+                               FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen;
+                       System.arraycopy(makeHeader(
+                                       FCGIGlobalDefs.def_FCGIUnknownType,
+                                       FCGIGlobalDefs.def_FCGINullRequestID,  
+                                       len, 0), 0,
+                                       response, 0, 
+                                       FCGIGlobalDefs.def_FCGIHeaderLen);
+                       System.arraycopy(makeUnknownTypeBodyBody(h_type), 0,
+                                   response, 
+                               FCGIGlobalDefs.def_FCGIHeaderLen,
+                               FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen);
+                       }
+               /*
+                * No guarantee that we have a request yet, so
+                * dont use fcgi output stream to reference socket, instead
+                * use the FileInputStream that refrences it. Also
+                * nowhere to save exception, since this is not FCGI stream.
+                */
+               
+               try {             
+                       in.request.socket.getOutputStream().write(response, 0, 
+                               FCGIGlobalDefs.def_FCGIHeaderLen +
+                               FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen);
+                        
+               } catch (IOException e){                                
+                       return -1;
+               }
+               return FCGIGlobalDefs.def_FCGIMgmtRecord;
+       }
+       
+
+       
+       /* 
+        * Makes a name/value with name = string of some length, and
+        * value a 1 byte integer. Pretty specific to what we are doing
+        * above.
+        */             
+       void makeNameVal(String name, String value, byte[] dest, int pos) {
+               int nameLen = name.length();
+               if (nameLen < 0x80) {
+                       dest[pos++] = (byte)nameLen;
+               }else {
+                               dest[pos++] = (byte)(((nameLen >> 24) | 0x80) & 0xff);
+                       dest[pos++] = (byte)((nameLen >> 16) & 0xff);
+                       dest[pos++] = (byte)((nameLen >> 8) & 0xff);
+                       dest[pos++] = (byte)nameLen;
+                       }
+               int valLen = value.length();
+               if (valLen < 0x80) {
+                       dest[pos++] =  (byte)valLen;
+               }else {
+                               dest[pos++] = (byte)(((valLen >> 24) | 0x80) & 0xff);
+                       dest[pos++] = (byte)((valLen >> 16) & 0xff);
+                       dest[pos++] = (byte)((valLen >> 8) & 0xff);
+                       dest[pos++] = (byte)valLen;
+                       }
+               name.getBytes(0, nameLen, dest, pos);
+               pos += nameLen;
+               value.getBytes(0, valLen, dest, pos);
+               pos += valLen;          
+       }
+
+/* 
+        * Read FCGI name-value pairs from a stream until EOF. Put them
+         * into a Properties object, storing both as strings.
+        * 
+        */
+       public int readParams(Properties props) throws IOException{
+               int nameLen, valueLen;
+               byte lenBuff[] = new byte[3];
+               int i = 1;
+               
+               while ((nameLen = in.read()) != -1) {                   
+                       i++;
+                       if ((nameLen & 0x80) != 0) {
+                               if ((in.read( lenBuff, 0, 3)) != 3) {
+                                  in.setFCGIError(
+                                       FCGIGlobalDefs.def_FCGIParamsError);
+                                  return -1;
+                                  }
+                               nameLen = ((nameLen & 0x7f) << 24) 
+                                       + (lenBuff[0] << 16)
+                                       + (lenBuff[1] << 8) + lenBuff[2];
+                               }
+
+                       if ((valueLen = in.read()) == -1) {
+                               in.setFCGIError(
+                                       FCGIGlobalDefs.def_FCGIParamsError);
+                               return -1;
+                               }
+                       if ((valueLen & 0x80) != 0) {
+                               if ((in.read( lenBuff, 0, 3)) != 3) {
+                                  in.setFCGIError(
+                                       FCGIGlobalDefs.def_FCGIParamsError);
+                                  return -1;
+                                  }
+                               valueLen = ((valueLen & 0x7f) << 24) 
+                                       + (lenBuff[0] << 16)
+                                       + (lenBuff[1] << 8) + lenBuff[2];
+                       /* handle sign extention for only two bytes, since
+                        * max msg len is ffff.
+                        */
+                       if ((lenBuff[2] & 0x80) != 0  && 
+                                               (lenBuff[1] & 0xff) == 0){
+                               valueLen = (valueLen & 0x000000ff);
+                               }
+                       if ((lenBuff[1] & 0x80) != 0) { 
+                               valueLen = (valueLen & 0x0000ffff);     
+                               }
+               }
+
+               /* 
+                * nameLen and valueLen are now valid; read the name 
+                * and the value from the stream and construct a standard 
+                * environmental entity 
+                */
+               byte[] name  = new byte[nameLen];
+               byte[] value = new byte[valueLen];
+               if (in.read(name ,0,  nameLen) != nameLen) {
+                       in.setFCGIError(
+                               FCGIGlobalDefs.def_FCGIParamsError);
+                       return -1;
+                       }
+
+               if(in.read(value, 0, valueLen) != valueLen) {
+                       in.setFCGIError(
+                               FCGIGlobalDefs.def_FCGIParamsError);  
+                       return -1;
+                       }
+               String strName  = new String(name, 0, 0, name.length);
+               String strValue = new String(value, 0, 0, value.length);
+               props.put(strName, strValue); 
+               }
+               return 0;
+
+
+}      
+       /* 
+        * Message Building Methods
+        */
+
+       /* 
+        * Build an FCGI Message Header - 
+        */
+       public byte[] makeHeader(int type, 
+                                   int requestId, 
+                                   int contentLength, 
+                                   int paddingLength) { 
+               byte[] header = new byte[FCGIGlobalDefs.def_FCGIHeaderLen];
+               header[0]   = (byte)FCGIGlobalDefs.def_FCGIVersion1;
+               header[1]   = (byte)type;
+               header[2]   = (byte)((requestId      >> 8) & 0xff);
+               header[3]   = (byte)((requestId          ) & 0xff);
+               header[4]   = (byte)((contentLength  >> 8) & 0xff);
+               header[5]   = (byte)((contentLength      ) & 0xff);
+               header[6]   = (byte)paddingLength;
+               header[7]   =  0;                       //reserved byte
+               return header;
+       }
+       /* 
+        * Build an FCGI Message End Request Body
+        */
+       public byte[] makeEndrequestBody(int appStatus,int protocolStatus){
+               byte body[] = new byte[FCGIGlobalDefs.def_FCGIEndReqBodyLen];
+               body[0] = (byte)((appStatus >> 24) & 0xff);
+               body[1] = (byte)((appStatus >> 16) & 0xff);
+               body[2] = (byte)((appStatus >>  8) & 0xff);
+               body[3] = (byte)((appStatus      ) & 0xff);
+               body[4] = (byte)protocolStatus;
+               for (int i = 5; i < 8; i++) {
+                       body[i] = 0;
+                       }
+               return body;
+       }
+       /* 
+        * Build an FCGI Message UnknownTypeBodyBody
+        */
+       public byte[] makeUnknownTypeBodyBody(int type){
+               byte body[] = 
+                       new byte[FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen];
+               body[0] = (byte)type;
+               for (int i = 1; 
+               i < FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen; i++) {
+                        body[i] = 0;
+                       }
+               return body;
+       }
+
+
+
+ } //end class
\ No newline at end of file
diff --git a/java/src/FCGIOutputStream.java b/java/src/FCGIOutputStream.java
new file mode 100644 (file)
index 0000000..3c93845
--- /dev/null
@@ -0,0 +1,336 @@
+/*
+ * @(#)FCGIOutputStream.java   
+ *
+ *      FastCGi compatibility package Interface
+ *
+ *
+ *  Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+
+
+import java.io.*;
+import  FCGIRequest;
+import  FCGIGlobalDefs;
+import  FCGIMessage;
+import  FCGIInputStream;
+
+       
+/**
+ * This stream understands FCGI prototcol.
+ *
+ *
+ */
+
+       public
+       class FCGIOutputStream extends OutputStream {
+
+       /* Stream vars */
+
+       public int wrNext;
+       public int stop;
+       public boolean isClosed;
+
+       /* require methods to set, get and clear */
+       private int errno;
+       private Exception errex;
+
+       /* data vars */
+
+       public byte buff[];
+       public int buffLen;
+       public int buffStop;
+       public int type;
+       public boolean isAnythingWritten;
+       public boolean rawWrite;    
+       public FCGIRequest request;
+
+       public FileOutputStream out;
+
+       /**
+       * Creates a new output stream to manage fcgi prototcol stuff
+       * @param out the output stream  buflen  length of buffer streamType   
+       */
+       public FCGIOutputStream(FileOutputStream outStream, 
+                                       int bufLen, int streamType,
+                                       FCGIRequest inreq) {
+               out = outStream;
+               buffLen = Math.min(bufLen, FCGIGlobalDefs.def_FCGIMaxLen);
+               buff = new byte[buffLen];
+               type = streamType;
+               stop = buffStop = buffLen;
+               isAnythingWritten = false;
+               rawWrite = false;
+               wrNext = FCGIGlobalDefs.def_FCGIHeaderLen;
+               isClosed = false;
+               request = inreq;                         
+       }
+       /*
+        * Writes a byte to the output stream.
+        */
+       
+       public void  write(int c) throws IOException {
+               if(wrNext != stop) {
+                       buff[wrNext++] = (byte)c;
+                       return;
+                       }
+               if(isClosed) {
+                       throw new EOFException();
+                       }
+               empty(false);
+               if(wrNext != stop) {
+                       buff[wrNext++] = (byte)c;
+                       return;
+                       }
+               /* NOTE: ASSERT(stream->isClosed); */
+               /* bug in emptyBuffProc if not */
+               throw new EOFException();
+       }
+
+       /**
+       * Writes an array of bytes. This method will block until the bytes
+       * are actually written.
+       * @param b      the data to be written
+       */
+       public  void write(byte b[]) throws IOException{
+               write(b, 0, b.length);
+               }
+   
+
+       /*
+       * Writes len consecutive bytes from off in the array b
+       * into the output stream.  Performs no interpretation
+       * of the output bytes. Making the user convert the string to
+       * bytes is in line with current Java practice.  
+       */
+       public void write(byte b[], int off, int len) throws IOException {
+               int m, bytesMoved;
+               /*
+               * Fast path: room for n bytes in the buffer
+               */
+               if(len <= (stop - wrNext)) {
+                       System.arraycopy(b, off, buff, wrNext, len);
+                       wrNext += len;
+                       return;
+                       }
+               /*
+               * General case: stream is closed or buffer empty procedure
+               * needs to be called
+               */               
+                bytesMoved = 0;
+                for (;;) {
+                       if(wrNext != stop) {
+                               m = Math.min(len - bytesMoved, stop - wrNext);
+                               System.arraycopy(b, off, buff, wrNext, m);
+                               bytesMoved += m;
+                               wrNext += m;
+                               if(bytesMoved == len) {
+                                       return;
+                                       }
+                               off += m;
+                               }
+               if(isClosed) {
+                       throw new EOFException();
+                       }
+               empty(false);
+               }
+       }
+
+       /*
+        * Encapsulates any buffered stream content in a FastCGI
+        * record.  If !doClose, writes the data, making the buffer
+        * empty.
+        */
+       public void empty(boolean doClose) throws IOException {
+               int cLen;
+               /*
+               * Alignment padding omitted in Java
+               */
+               if (!rawWrite) {
+                       cLen = wrNext - FCGIGlobalDefs.def_FCGIHeaderLen;
+                       if(cLen > 0) {
+                       System.arraycopy(new FCGIMessage().makeHeader(type, 
+                                       request.requestID, cLen, 0), 
+                                       0, buff, 0, 
+                                       FCGIGlobalDefs.def_FCGIHeaderLen);
+                       } else {
+                               wrNext = 0;
+                               }
+               }
+               if (doClose) {
+                       writeCloseRecords();
+                       }
+               if (wrNext != 0) {
+                       isAnythingWritten = true;
+                       try {
+                               out.write(buff, 0, wrNext);
+                       } catch (IOException e) {
+                               setException(e);
+                               return;
+                               }
+                       wrNext = 0;
+                       }
+               /*
+               * The buffer is empty.
+               */
+               if(!rawWrite) {
+                       wrNext += FCGIGlobalDefs.def_FCGIHeaderLen;
+                       }
+       }
+
+       /* 
+        * Close the stream. 
+        */
+       public void  close() throws IOException {
+               if (isClosed) {
+                       return;
+                       }
+               empty(true); // 
+               /*
+                * if isClosed, will return with EOFException from write.
+                */ 
+               isClosed = true;
+               stop = wrNext;
+               return;
+       }
+
+       /*
+       *      Flushes any buffered output.
+       *      Server-push is a legitimate application of flush.
+       *      Otherwise, it is not very useful, since FCGIAccept
+       *      does it implicitly.  flush may reduce performance
+       *      by increasing the total number of operating system calls
+       *      the application makes.
+       */
+       public void flush() throws IOException {
+               if (isClosed) {
+                       return;
+                       }
+               empty(false);
+               /*
+                * if isClosed, will return with EOFException from write.
+                */
+               return; 
+       }
+               
+       /*
+        * An FCGI error has occurred. Save the error code in the stream
+        * for diagnostic purposes and set the stream state so that
+        * reads return EOF 
+        */
+       public void setFCGIError(int errnum) {
+       /*
+        * Preserve only the first error.
+        */
+               if (errno == 0) {
+                       errno = errnum;
+                       }
+                       isClosed = true;
+       }
+       /*
+        * An Exception has occurred. Save the Exception in the stream
+        * for diagnostic purposes and set the stream state so that
+        * reads return EOF 
+        */
+       public void setException(Exception errexpt) {
+       /*
+        * Preserve only the first error.
+        */
+               if (errex == null) {
+                       errex = errexpt;
+                       }
+               isClosed = true;
+       }
+
+       /*
+        * Clear the stream error code and end-of-file indication.
+        */
+       public void clearFCGIError() {
+               errno = 0;      
+       /*
+        * isClosed = false;
+        * XXX: should clear isClosed but work is needed to make it safe
+        * to do so. 
+        */
+       }
+/*
+        * Clear the stream error code and end-of-file indication.
+        */
+       public void clearException() {
+               errex = null;
+       /*
+        * isClosed = false;
+        * XXX: should clear isClosed but work is needed to make it safe
+        * to do so. 
+        */
+       }
+
+       /* 
+        * accessor method since var is private
+        */
+       public int etFCGIError() {
+               return errno;
+       }
+       /* 
+        * accessor method since var is private
+        */
+       public Exception getException() {
+               return errex;
+       }
+       /*
+        * Writes an EOF record for the stream content if necessary.
+        * If this is the last writer to close, writes an FCGI_END_REQUEST
+        * record.
+        */
+       public void writeCloseRecords() throws IOException {
+               FCGIMessage msg = new FCGIMessage();
+               /*
+                * Enter rawWrite mode so final records won't be 
+                * encapsulated as
+                * stream data.
+                */
+               rawWrite = true;
+               /*
+                * Generate EOF for stream content if needed.
+                */
+               if(!(type == FCGIGlobalDefs.def_FCGIStderr
+                       && wrNext == 0
+                       && !isAnythingWritten)) {
+                       byte hdr[] = 
+                               new byte[FCGIGlobalDefs.def_FCGIHeaderLen];
+                       System.arraycopy(msg.makeHeader(type, 
+                                        request.requestID, 
+                                        0, 0), 
+                                        0, hdr,0,
+                                        FCGIGlobalDefs.def_FCGIHeaderLen);
+                       write(hdr, 0, hdr.length);
+                       }
+               /*
+                * Generate FCGI_END_REQUEST record if needed.
+                */
+               if(request.numWriters == 1) {
+                       byte endReq[] = 
+                               new byte[FCGIGlobalDefs.def_FCGIHeaderLen
+                                    + FCGIGlobalDefs.def_FCGIEndReqBodyLen];
+                       System.arraycopy(msg.makeHeader( 
+                                      FCGIGlobalDefs.def_FCGIEndRequest, 
+                                      request.requestID, 
+                                      FCGIGlobalDefs.def_FCGIEndReqBodyLen,0),
+                                      0, endReq, 0, 
+                                      FCGIGlobalDefs.def_FCGIHeaderLen);
+                       System.arraycopy(msg.makeEndrequestBody(
+                                      request.appStatus, 
+                                      FCGIGlobalDefs.def_FCGIRequestComplete),
+                                      0,endReq,  
+                                      FCGIGlobalDefs.def_FCGIHeaderLen,
+                                      FCGIGlobalDefs.def_FCGIEndReqBodyLen);
+                       write(endReq,0, FCGIGlobalDefs.def_FCGIHeaderLen 
+                                     + FCGIGlobalDefs.def_FCGIEndReqBodyLen); 
+                       }
+       request.numWriters--;
+       }
+}
\ No newline at end of file
diff --git a/java/src/FCGIRequest.java b/java/src/FCGIRequest.java
new file mode 100644 (file)
index 0000000..d861b9e
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * @(#)FCGIRequest.java        
+ *
+ *
+ *
+ *      FastCGi compatibility package Interface
+ *
+ *
+ *  Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+import java.net.*;
+import java.io.FileDescriptor;
+import java.util.Properties;
+import FCGIInputStream;
+import FCGIOutputStream;
+public class FCGIRequest {
+
+/* This class has no methods. Right now we are single threaded 
+ * so there is only one request object at any given time which 
+ * is refrenced by an FCGIInterface class variable . All of this 
+ * object's data could just as easily be declared directly there. 
+ * When we thread, this will change, so we might as well use a 
+ * seperate class. In line with this thinking, though somewhat
+ * more perversely, we kept the socket here.
+ */
+       /*
+        * class variables
+        */
+/*public static Socket         socket; */
+// same for all requests
+
+       /*
+        * instance variables
+        */
+public Socket          socket; 
+public boolean                 isBeginProcessed;
+public int             requestID;
+public boolean                 keepConnection;
+public int             role;
+public int             appStatus;
+public int             numWriters;
+public FCGIInputStream inStream;
+public FCGIOutputStream        outStream;
+public FCGIOutputStream        errStream;
+public Properties      params;
+
+
+
+}    
+
diff --git a/libfcgi/Makefile.in b/libfcgi/Makefile.in
new file mode 100644 (file)
index 0000000..0da14bb
--- /dev/null
@@ -0,0 +1,40 @@
+#
+#  Makefile for FastCGI application library
+#
+#  Open Market, Inc.
+#
+#  $Id: Makefile.in,v 1.1 1997/09/16 15:36:33 stanleyg Exp $
+#
+
+SHELL = @SHELL@
+O =     @O@
+L =     @L@
+CC     = @CC@
+INCLUDEDIR  = ../include
+CFLAGS = @CCDEFS@ @PROFILE@ -I$(INCLUDEDIR)
+RANLIB = @RANLIB@
+LIBOBJS        = @LIBOBJS@
+
+OBJS       = fcgiapp.${O} fcgi_stdio.${O} os_@SYSTEM@.${O} $(LIBOBJS)
+INCLUDES    = $(INCLUDEDIR)/fastcgi.h $(INCLUDEDIR)/fcgiapp.h \
+             $(INCLUDEDIR)/fcgimisc.h $(INCLUDEDIR)/fcgiappmisc.h \
+             $(INCLUDEDIR)/fcgi_stdio.h $(INCLUDEDIR)/fcgios.h
+
+
+all: libfcgi.${L}
+
+libfcgi.${L}: $(OBJS)
+       ar cr libfcgi.${L} $(OBJS)
+       $(RANLIB) libfcgi.${L}
+
+clean:
+       rm -f *.${L} *.${O} core.* errs *~ \#* TAGS *.E a.out
+
+# ----------------------------------------------------------------------------
+
+fcgiapp.${O}: fcgiapp.c $(INCLUDES)
+
+fcgi_stdio.${O}: fcgi_stdio.c $(INCLUDES)
+
+os_@SYSTEM@.${O}: os_@SYSTEM@.c $(INCLUDES)
+
diff --git a/libfcgi/descrip.dfc b/libfcgi/descrip.dfc
new file mode 100644 (file)
index 0000000..bd1d3b4
--- /dev/null
@@ -0,0 +1,14 @@
+# descrip.dfc
+# This is a generated file, DO NOT EDIT
+# Another service of Ace Wrecking & Software
+# If we don't have it then you don't need it
+#
+InheritFrom ../
+IncludeDir ../include
+
+BuildObjectsForPlatform wnt os_win32.c 
+BuildObjectsForPlatform unix os_unix.c
+
+BuildDynamic libfcgi fcgi_stdio.c fcgiapp.c strerror.c os_unix.c os_win32.c
+
+ExportDynamic libfcgi
diff --git a/libfcgi/fcgi_stdio.c b/libfcgi/fcgi_stdio.c
new file mode 100644 (file)
index 0000000..b461ea5
--- /dev/null
@@ -0,0 +1,797 @@
+/* 
+ * fcgi_stdio.c --
+ *
+ *      FastCGI-stdio compatibility package
+ *
+ *
+ * Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] = "$Id: fcgi_stdio.c,v 1.1 1997/09/16 15:36:33 stanleyg Exp $";
+#endif /* not lint */
+
+#ifdef _WIN32
+#define DLLAPI  __declspec(dllexport)
+#endif
+
+#define NO_FCGI_DEFINES
+#include "fcgi_stdio.h"
+#undef NO_FCGI_DEFINES
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+#include <errno.h>
+    /* for errno */
+#include <stdarg.h>
+    /* for va_arg */
+#include <stdlib.h>
+    /* for malloc */
+#include <string.h>
+    /* for strerror */
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "fcgiapp.h"
+#include "fcgios.h"
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+#ifndef TRUE
+#define TRUE  (1)
+#endif
+
+#ifdef _WIN32
+FCGI_FILE _fcgi_sF[3];
+#else
+FCGI_FILE _fcgi_sF[3] = {{stdin, NULL}, {stdout, NULL}, {stderr, NULL}};
+#endif
+
+#ifdef _WIN32
+#define popen _popen
+#endif
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGI_Accept --
+ *
+ *      Accepts a new request from the HTTP server and creates
+ *      a conventional execution environment for the request.
+ *
+ *      If the application was invoked as a FastCGI server,
+ *      the first call to FCGI_Accept indicates that the application
+ *      has completed its initialization and is ready to accept
+ *      a request.  Subsequent calls to FCGI_Accept indicate that
+ *      the application has completed its processing of the
+ *      current request and is ready to accept a new request.
+ *
+ *      If the application was invoked as a CGI program, the first
+ *      call to FCGI_Accept is essentially a no-op and the second
+ *      call returns EOF (-1).
+ *
+ * Results:
+ *    0 for successful call, -1 for error (application should exit).
+ *
+ * Side effects:
+ *      If the application was invoked as a FastCGI server,
+ *      and this is not the first call to this procedure,
+ *      FCGI_Accept first performs the equivalent of FCGI_Finish.
+ *
+ *      On every call, FCGI_Accept accepts the new request and
+ *      reads the FCGI_PARAMS stream into an environment array,
+ *      i.e. a NULL-terminated array of strings of the form
+ *      ``name=value''.  It assigns a pointer to this array
+ *      to the global variable environ, used by the standard
+ *      library function getenv.  It creates new FCGI_FILE *s
+ *      representing input from the HTTP server, output to the HTTP
+ *      server, and error output to the HTTP server, and assigns these
+ *      new files to stdin, stdout, and stderr respectively.
+ *
+ *      DO NOT mutate or retain pointers to environ or any values
+ *      contained in it (e.g. to the result of calling getenv(3)),
+ *      since these are freed by the next call to FCGI_Finish or
+ *      FCGI_Accept.  In particular do not use setenv(3) or putenv(3)
+ *      in conjunction with FCGI_Accept.
+ *
+ *----------------------------------------------------------------------
+ */
+static int acceptCalled = FALSE;
+static int isCGI = FALSE;
+#ifndef _WIN32
+extern char **environ;
+#endif
+
+int FCGI_Accept(void)
+{
+    if(!acceptCalled) {
+        /*
+         * First call to FCGI_Accept.  Is application running
+         * as FastCGI or as CGI?
+         */
+        isCGI = FCGX_IsCGI();
+        acceptCalled = TRUE;
+    } else if(isCGI) {
+        /*
+         * Not first call to FCGI_Accept and running as CGI means
+         * application is done.
+         */
+        return(EOF);
+    }
+    if(isCGI) {
+        FCGI_stdin->stdio_stream = stdin;
+        FCGI_stdin->fcgx_stream = NULL;
+        FCGI_stdout->stdio_stream = stdout;
+        FCGI_stdout->fcgx_stream = NULL;
+        FCGI_stderr->stdio_stream = stderr;
+        FCGI_stderr->fcgx_stream = NULL;
+    } else {
+        FCGX_Stream *in, *out, *error;
+        FCGX_ParamArray envp;
+        int acceptResult = FCGX_Accept(&in, &out, &error, &envp);
+        if(acceptResult < 0) {
+            return acceptResult;
+        }
+        FCGI_stdin->stdio_stream = NULL;
+        FCGI_stdin->fcgx_stream = in;
+        FCGI_stdout->stdio_stream = NULL;
+        FCGI_stdout->fcgx_stream = out;
+        FCGI_stderr->stdio_stream = NULL;
+        FCGI_stderr->fcgx_stream = error;
+        environ = envp;
+    }
+    return 0;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGI_Finish --
+ *
+ *      Finishes the current request from the HTTP server.
+ *
+ * Side effects:
+ *
+ *      Flushes any buffered output to the HTTP server.  Then frees
+ *      all storage allocated by the previous call, including all
+ *      storage reachable from the value of environ set by the previous
+ *      call to FCGI_Accept.
+ *
+ *      DO NOT use stdin, stdout, stderr, or environ between calling
+ *      FCGI_Finish and calling FCGI_Accept.
+ *
+ *      DO NOT mutate or retain pointers to environ or any values
+ *      contained in it (e.g. to the result of calling getenv(3)),
+ *      since these are freed by the next call to FCGI_Finish or
+ *      FCGI_Accept.  In particular do not use setenv(3) or putenv(3)
+ *      in conjunction with FCGI_Accept.
+ *
+ *----------------------------------------------------------------------
+ */
+void FCGI_Finish(void)
+{
+    if(!acceptCalled || isCGI) {
+        return;
+    }
+    FCGX_Finish();
+    FCGI_stdin->fcgx_stream = NULL;
+    FCGI_stdout->fcgx_stream = NULL;
+    FCGI_stderr->fcgx_stream = NULL;
+    environ = NULL;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGI_StartFilterData --
+ *
+ *      
+ *      The current request is for the filter role, and stdin is
+ *      positioned at EOF of FCGI_STDIN.  The call repositions 
+ *      stdin to the start of FCGI_DATA.
+ *      If the preconditions are not met (e.g. FCGI_STDIN has not
+ *      been read to EOF), the call sets the stream error code to
+ *      FCGX_CALL_SEQ_ERROR.
+ *
+ * Results:
+ *      0 for a normal return, < 0 for error 
+ *
+ *----------------------------------------------------------------------
+ */
+
+int FCGI_StartFilterData(void)
+{
+    if(FCGI_stdin->stdio_stream) {
+        return -1;
+    } else {
+        return FCGX_StartFilterData(FCGI_stdin->fcgx_stream);
+    }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGI_SetExitStatus --
+ *
+ *      Sets the exit status for the current request. The exit status
+ *      is the status code the request would have exited with, had
+ *      the request been run as a CGI program.  You can call
+ *      FCGI_SetExitStatus several times during a request; the last call
+ *      before the request ends (by calling FCGI_Accept) determines the
+ *      value.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void FCGI_SetExitStatus(int status)
+{
+    if(FCGI_stdin->fcgx_stream) {
+        FCGX_SetExitStatus(status, FCGI_stdin->fcgx_stream);
+    }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGI_perror --
+ *
+ *       Wrapper for function defined in H&S Section 11.2
+ *
+ *----------------------------------------------------------------------
+ */
+
+void FCGI_perror(const char *str)
+{
+    FCGI_fputs(str, FCGI_stderr);
+    FCGI_fputs(": ", FCGI_stderr);
+    FCGI_fputs(strerror(OS_Errno), FCGI_stderr);
+    return;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGI_OpenFromFILE --
+ *
+ *      Constructs a new FCGI_FILE * from the FILE *stream.
+ *
+ * Results:
+ *     NULL if stream == NULL or storage could not be allocated,
+ *      otherwise the new FCGI_FILE *.
+ *
+ *----------------------------------------------------------------------
+ */ 
+
+static FCGI_FILE *FCGI_OpenFromFILE(FILE *stream)
+{
+    FCGI_FILE *fp;
+    if(stream == NULL)
+        return NULL;
+    fp = malloc(sizeof(FCGI_FILE));
+    if(fp == NULL)
+        return NULL;
+    fp->stdio_stream = stream;
+    fp->fcgx_stream = NULL;
+    return fp;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGI_fopen, FCGI_fclose, FCGI_fflush, FCGI_freopen --
+ *
+ *       Wrappers for functions defined in H&S Section 15.2
+ *
+ *----------------------------------------------------------------------
+ */
+
+FCGI_FILE *FCGI_fopen(const char *path, const char *mode)
+{
+    return FCGI_OpenFromFILE(fopen(path, mode));
+}
+
+int FCGI_fclose(FCGI_FILE *fp)
+{
+    int n = EOF;
+    if(fp->stdio_stream) {
+        n = fclose(fp->stdio_stream);
+        fp->stdio_stream = NULL;
+    } else if(fp->fcgx_stream) {
+        n = FCGX_FClose(fp->fcgx_stream);
+        fp->fcgx_stream = NULL;
+    }
+    if((fp != FCGI_stdin) && (fp != FCGI_stdout) && (fp != FCGI_stderr)) {
+        free(fp);
+    }
+    return n;
+}
+
+int FCGI_fflush(FCGI_FILE *fp)
+{
+    if(fp->stdio_stream)
+        return fflush(fp->stdio_stream);
+    else if(fp->fcgx_stream)
+        return FCGX_FFlush(fp->fcgx_stream);
+    return EOF;
+}
+
+FCGI_FILE *FCGI_freopen(const char *path, const char *mode,
+                        FCGI_FILE *fp)
+{
+    if(fp->stdio_stream) {
+        if(freopen(path, mode, fp->stdio_stream) == NULL)
+            return NULL;
+        else
+            return fp;
+    } else if(fp->fcgx_stream) {
+        (void) FCGX_FClose(fp->fcgx_stream);
+        fp->stdio_stream = fopen(path, mode);
+        if(fp->stdio_stream == NULL)
+            return NULL;
+        else {
+           fp->fcgx_stream = NULL;
+            return fp;
+       }
+    }
+    return NULL;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGI_setvbuf, FCGI_setbuf --
+ *
+ *       Wrappers for functions defined in H&S Section 15.3
+ *
+ *----------------------------------------------------------------------
+ */
+int FCGI_setvbuf(FCGI_FILE *fp, char *buf, int bufmode, size_t size)
+{
+    if(fp->stdio_stream)
+        return setvbuf(fp->stdio_stream, buf, bufmode, size);
+    else {
+        return -1;
+    }
+}
+
+void FCGI_setbuf(FCGI_FILE *fp, char *buf)
+{
+    if(fp->stdio_stream)
+        setbuf(fp->stdio_stream, buf);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGI_fseek, FCGI_ftell, FCGI_rewind, FCGI_fgetpos, FCGI_fsetpos --
+ *
+ *       Wrappers for functions defined in H&S Section 15.5
+ *
+ *----------------------------------------------------------------------
+ */
+
+int FCGI_fseek(FCGI_FILE *fp, long offset, int whence)
+{
+    if(fp->stdio_stream)
+        return fseek(fp->stdio_stream, offset, whence);
+    else {
+        OS_SetErrno(ESPIPE);
+        return -1;
+    }
+}
+
+int FCGI_ftell(FCGI_FILE *fp)
+{
+    if(fp->stdio_stream)
+        return ftell(fp->stdio_stream);
+    else {
+        OS_SetErrno(ESPIPE);
+        return -1;
+    } 
+}
+
+void FCGI_rewind(FCGI_FILE *fp)
+{
+    if(fp->stdio_stream)
+        rewind(fp->stdio_stream);
+    else
+        OS_SetErrno(ESPIPE);
+}
+
+#ifdef HAVE_FPOS
+int FCGI_fgetpos(FCGI_FILE *fp, fpos_t *pos)
+{
+    if(fp->stdio_stream)
+        return fgetpos(fp->stdio_stream, pos);
+    else {
+        OS_SetErrno(ESPIPE);
+        return -1;
+    } 
+}
+
+int FCGI_fsetpos(FCGI_FILE *fp, const fpos_t *pos)
+{
+    if(fp->stdio_stream)
+        return fsetpos(fp->stdio_stream, pos);
+    else {
+        OS_SetErrno(ESPIPE);
+        return -1;
+    } 
+}
+#endif
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGI_fgetc, FCGI_getchar, FCGI_ungetc --
+ *
+ *       Wrappers for functions defined in H&S Section 15.6
+ *
+ *       XXX: getc and getchar are generally defined as macros
+ *            for performance reasons
+ *
+ *----------------------------------------------------------------------
+ */
+
+int FCGI_fgetc(FCGI_FILE *fp)
+{
+    if(fp->stdio_stream)
+        return fgetc(fp->stdio_stream);
+    else if(fp->fcgx_stream)
+        return FCGX_GetChar(fp->fcgx_stream);
+    return EOF;
+}
+
+int FCGI_getchar(void)
+{
+    return FCGI_fgetc(FCGI_stdin);
+}
+
+int FCGI_ungetc(int c, FCGI_FILE *fp)
+{
+    if(fp->stdio_stream)
+        return ungetc(c, fp->stdio_stream);
+    else if(fp->fcgx_stream)
+        return FCGX_UnGetChar(c, fp->fcgx_stream);
+    return EOF;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGI_fgets, FCGI_gets --
+ *
+ *       Wrappers for functions defined in H&S Section 15.7
+ *
+ *----------------------------------------------------------------------
+ */
+
+char *FCGI_fgets(char *str, int size, FCGI_FILE *fp)
+{
+    if(fp->stdio_stream)
+        return fgets(str, size, fp->stdio_stream);
+    else if(fp->fcgx_stream)
+        return FCGX_GetLine(str, size, fp->fcgx_stream);
+    return NULL;
+}
+
+/*
+ * There is no standard equivalent of gets that takes an explicit
+ * FILE * argument, so the following "wrapper" implements
+ * gets for both FILE * and FCGX_Stream * in terms of FCGI_getchar.
+ */
+
+char *FCGI_gets(char *str)
+{
+    char *s;
+    int c;
+    for (s = str; ((c = FCGI_getchar()) != '\n');) {
+        if(c == EOF) {
+            if(s == str) 
+                return NULL;
+            else
+                break;
+        } else
+            *s++ = c;
+    }
+    *s = 0;
+    return str;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * --
+ *
+ *       Wrappers for functions defined in H&S Section 15.8
+ *
+ *       XXX: missing: fscanf, scanf
+ *
+ *----------------------------------------------------------------------
+ */
+
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGI_fputc, FCGI_putchar --
+ *
+ *       Wrappers for functions defined in H&S Section 15.9
+ *
+ *       XXX: putc and putchar are generally defined as macros
+ *            for performance reasons
+ *
+ *----------------------------------------------------------------------
+ */
+
+int FCGI_fputc(int c, FCGI_FILE *fp)
+{
+    if(fp->stdio_stream) 
+        return fputc(c, fp->stdio_stream);
+    else if(fp->fcgx_stream)
+        return FCGX_PutChar(c, fp->fcgx_stream);
+    else return EOF;
+}
+
+int FCGI_putchar(int c)
+{
+    return FCGI_fputc(c, FCGI_stdout);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGI_fputs, FCGI_puts
+ *
+ *       Wrappers for functions defined in H&S Section 15.10
+ *
+ *----------------------------------------------------------------------
+ */
+
+int FCGI_fputs(const char *str, FCGI_FILE *fp)
+{
+    if(fp->stdio_stream)
+        return fputs(str, fp->stdio_stream);
+    else if(fp->fcgx_stream)
+        return FCGX_PutS(str, fp->fcgx_stream);
+    return EOF;
+}
+
+int FCGI_puts(const char *str)
+{
+    int n;
+    if(FCGI_stdout->stdio_stream) {
+        n = fputs(str, FCGI_stdout->stdio_stream);
+        if(n < 0)
+            return n;
+        else
+            return fputc('\n', FCGI_stdout->stdio_stream);
+    } else if(FCGI_stdout->fcgx_stream) {
+        n = FCGX_PutS(str, FCGI_stdout->fcgx_stream);
+        if(n < 0)
+            return n;
+        else
+            return FCGX_PutChar('\n', FCGI_stdout->fcgx_stream);
+    }
+    return EOF;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGI_fprintf, FCGI_printf --
+ *
+ *       Wrappers for functions defined in H&S Section 15.11
+ *
+ *----------------------------------------------------------------------
+ */
+
+int FCGI_fprintf(FCGI_FILE *fp, const char *format, ...)
+{
+    va_list ap;
+    int n = 0;
+    va_start(ap, format);
+    if(fp->stdio_stream)
+        n = vfprintf(fp->stdio_stream, format, ap);
+    else if(fp->fcgx_stream)
+        n = FCGX_VFPrintF(fp->fcgx_stream, format, ap);
+    va_end(ap);
+    return n;
+}
+
+int FCGI_printf(const char *format, ...)
+{
+    va_list ap;
+    int n;
+    va_start(ap, format);
+    n = FCGI_vfprintf(FCGI_stdout, format, ap);
+    va_end(ap);
+    return n;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGI_vfprintf, FCGI_vprintf --
+ *
+ *       Wrappers for functions defined in H&S Section 15.12
+ *
+ *----------------------------------------------------------------------
+ */
+
+int FCGI_vfprintf(FCGI_FILE *fp, const char *format, va_list ap)
+{
+    if(fp->stdio_stream)
+        return vfprintf(fp->stdio_stream, format, ap);
+    else if(fp->fcgx_stream) 
+        return FCGX_VFPrintF(fp->fcgx_stream, format, ap);
+    return EOF;
+}
+
+int FCGI_vprintf(const char *format, va_list ap)
+{
+    if(FCGI_stdout->stdio_stream)
+        return vfprintf(FCGI_stdout->stdio_stream, format, ap);
+    else if(FCGI_stdout->fcgx_stream) 
+        return FCGX_VFPrintF(FCGI_stdout->fcgx_stream, format, ap);
+    return EOF;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGI_fread, FCGI_fwrite --
+ *
+ *       Wrappers for functions defined in H&S Section 15.13
+ *
+ *----------------------------------------------------------------------
+ */
+
+size_t FCGI_fread(void *ptr, size_t size, size_t nmemb, FCGI_FILE *fp)
+{
+    int n;
+    if(fp->stdio_stream)
+        return fread(ptr, size, nmemb, fp->stdio_stream);
+    else if(fp->fcgx_stream) {
+        if((size * nmemb) == 0) {
+            return 0;
+        }
+        n = FCGX_GetStr((char *) ptr, size * nmemb, fp->fcgx_stream);
+        return (n/size);
+    }
+    return (size_t)EOF;
+}
+
+size_t FCGI_fwrite(void *ptr, size_t size, size_t nmemb, FCGI_FILE *fp)
+{
+    int n;
+    if(fp->stdio_stream)
+        return fwrite(ptr, size, nmemb, fp->stdio_stream);
+    else if(fp->fcgx_stream) {
+        if((size * nmemb) == 0) {
+            return 0;
+        }
+        n = FCGX_PutStr((char *) ptr, size * nmemb, fp->fcgx_stream);
+        return (n/size);
+    }
+    return (size_t)EOF;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGI_feof, FCGI_ferror, FCGI_clearerr --
+ *
+ *       Wrappers for functions defined in H&S Section 15.14
+ *
+ *----------------------------------------------------------------------
+ */
+int FCGI_feof(FCGI_FILE *fp)
+{
+    if(fp->stdio_stream) {
+        return feof(fp->stdio_stream);
+    } else if (fp->fcgx_stream){
+        return FCGX_HasSeenEOF(fp->fcgx_stream);
+    }
+    return -1;
+
+}
+
+int FCGI_ferror(FCGI_FILE *fp)
+{
+    if(fp->stdio_stream) {
+        return ferror(fp->stdio_stream);
+    } else if(fp->fcgx_stream) {
+        return FCGX_GetError(fp->fcgx_stream);
+    }
+    return -1;
+}
+
+void FCGI_clearerr(FCGI_FILE *fp)
+{
+    if(fp->stdio_stream) {
+        clearerr(fp->stdio_stream);
+    } else if(fp->fcgx_stream) {
+        FCGX_ClearError(fp->fcgx_stream);
+    }
+    return;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGI_fileno, FCGI_fdopen, FCGI_popen, FCGI_pclose --
+ *
+ *       Wrappers for POSIX, X/OPEN functions not in ISO C
+ *
+ *----------------------------------------------------------------------
+ */
+
+/*
+ * These definitions should be supplied by stdio.h but for some
+ * reason they get lost on certain platforms.
+ */
+/*
+ * XXX: Need to find the right way to handle this for NT
+ */
+#ifndef _WIN32
+#ifndef fileno
+extern int fileno(FILE *stream);
+#endif
+extern FILE *fdopen(int fildes, const char *type);
+extern FILE *popen(const char *command, const char *type);
+extern int pclose(FILE *stream);
+#endif
+
+int FCGI_fileno(FCGI_FILE *fp)
+{
+    if(fp->stdio_stream)
+        return fileno(fp->stdio_stream);
+    else
+        return -1;
+}
+
+FCGI_FILE *FCGI_fdopen(int fd, const char *mode)
+{
+#ifndef _WIN32
+    return FCGI_OpenFromFILE(fdopen(fd, mode));
+#else
+    return NULL;
+#endif
+}
+
+FCGI_FILE *FCGI_popen(const char *cmd, const char *type)
+{
+    return FCGI_OpenFromFILE(popen(cmd, type));
+}
+
+int FCGI_pclose(FCGI_FILE *fp)
+{
+    int n = EOF;
+    if(fp->stdio_stream) {
+#ifndef _WIN32
+        n = pclose(fp->stdio_stream);
+#endif
+        fp->stdio_stream = NULL;
+    } else if(fp->fcgx_stream) {
+        /*
+         * The caller is deeply confused; don't free the storage.
+         */
+        return EOF;
+    }
+    if((fp != FCGI_stdin) && (fp != FCGI_stdout) && (fp != FCGI_stderr)) {
+        free(fp);
+    }
+    return n;
+}
+
+/*
+ *----------------------------------------------------------------------
+ */
diff --git a/libfcgi/fcgiapp.c b/libfcgi/fcgiapp.c
new file mode 100644 (file)
index 0000000..b9f2e04
--- /dev/null
@@ -0,0 +1,2176 @@
+/* 
+ * fcgiapp.c --
+ *
+ *     FastCGI application library: request-at-a-time
+ *
+ *
+ * Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] = "$Id: fcgiapp.c,v 1.1 1997/09/16 15:36:33 stanleyg Exp $";
+#endif /* not lint */
+
+#ifdef _WIN32
+#define DLLAPI  __declspec(dllexport)
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#include "fcgi_config.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>     /* for memchr() */
+#include <errno.h>
+#include <stdarg.h>
+#include <math.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h> /* for getpeername */
+#endif
+#include <fcntl.h>      /* for fcntl */
+
+#include "fcgimisc.h"
+#include "fcgiapp.h"
+#include "fcgiappmisc.h"
+#include "fastcgi.h"
+#include "fcgios.h"
+
+/*
+ * This is a workaround for one version of the HP C compiler 
+ * (c89 on HP-UX 9.04, also Stratus FTX), which will dump core
+ * if given 'long double' for varargs.
+ */
+#ifdef HAVE_VA_ARG_LONG_DOUBLE_BUG
+#define LONG_DOUBLE double
+#else
+#define LONG_DOUBLE long double
+#endif
+
+static int osLibInitialized = 0;
+
+static void *Malloc(size_t size)
+{
+    void *result = malloc(size);
+    ASSERT(size == 0 || result != NULL);
+    return result;
+}
+
+static char *StringCopy(char *str)
+{
+    int strLen = strlen(str);
+    char *newString = Malloc(strLen + 1);
+    memcpy(newString, str, strLen);
+    newString[strLen] = '\000';
+    return newString;
+}
+
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_GetChar --
+ *
+ *      Reads a byte from the input stream and returns it.
+ *
+ * Results:
+ *     The byte, or EOF (-1) if the end of input has been reached.
+ *
+ *----------------------------------------------------------------------
+ */
+int FCGX_GetChar(FCGX_Stream *stream)
+{
+    if(stream->rdNext != stream->stop)
+        return *stream->rdNext++;
+    if(stream->isClosed || !stream->isReader)
+        return EOF;
+    stream->fillBuffProc(stream);
+    stream->stopUnget = stream->rdNext;
+    if(stream->rdNext != stream->stop)
+        return *stream->rdNext++;
+    ASSERT(stream->isClosed); /* bug in fillBufProc if not */
+    return EOF;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_GetStr --
+ *
+ *      Reads up to n consecutive bytes from the input stream
+ *      into the character array str.  Performs no interpretation
+ *      of the input bytes.
+ *
+ * Results:
+ *     Number of bytes read.  If result is smaller than n,
+ *      the end of input has been reached.
+ *
+ *----------------------------------------------------------------------
+ */
+int FCGX_GetStr(char *str, int n, FCGX_Stream *stream)
+{
+    int m, bytesMoved;
+
+    if(n <= 0) {
+        return 0;
+    }
+    /*
+     * Fast path: n bytes are already available
+     */
+    if(n <= (stream->stop - stream->rdNext)) {
+        memcpy(str, stream->rdNext, n);
+        stream->rdNext += n;
+        return n;
+    }
+    /*
+     * General case: stream is closed or buffer fill procedure
+     * needs to be called
+     */
+    bytesMoved = 0;
+    for (;;) {
+        if(stream->rdNext != stream->stop) {
+            m = min(n - bytesMoved, stream->stop - stream->rdNext);
+            memcpy(str, stream->rdNext, m);
+            bytesMoved += m;
+            stream->rdNext += m;
+            if(bytesMoved == n)
+                return bytesMoved;
+            str += m;
+       }
+        if(stream->isClosed || !stream->isReader)
+            return bytesMoved;
+        stream->fillBuffProc(stream);
+        stream->stopUnget = stream->rdNext;
+    }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_GetLine --
+ *
+ *      Reads up to n-1 consecutive bytes from the input stream
+ *      into the character array str.  Stops before n-1 bytes
+ *      have been read if '\n' or EOF is read.  The terminating '\n'
+ *      is copied to str.  After copying the last byte into str,
+ *      stores a '\0' terminator.
+ *
+ * Results:
+ *     NULL if EOF is the first thing read from the input stream,
+ *      str otherwise.
+ *
+ *----------------------------------------------------------------------
+ */
+char *FCGX_GetLine(char *str, int n, FCGX_Stream *stream)
+{
+    int c;
+    char *p = str;
+    n--;
+    while (n > 0) {
+        c = FCGX_GetChar(stream);
+        if(c == EOF) {
+            if(p == str)
+                return NULL;
+            else 
+                break;
+        } 
+        *p++ = c;
+        n--;
+        if(c == '\n')
+            break;
+    }
+    *p = '\0';
+    return str;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_UnGetChar --
+ *
+ *      Pushes back the character c onto the input stream.  One
+ *      character of pushback is guaranteed once a character
+ *      has been read.  No pushback is possible for EOF.
+ *
+ * Results:
+ *     Returns c if the pushback succeeded, EOF if not.
+ *
+ *----------------------------------------------------------------------
+ */
+int FCGX_UnGetChar(int c, FCGX_Stream *stream) {
+    if(c == EOF
+            || stream->isClosed
+            || !stream->isReader
+            || stream->rdNext == stream->stopUnget)
+        return EOF;
+    --(stream->rdNext);
+    *stream->rdNext = c;
+    return c;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_HasSeenEOF --
+ *
+ *      Returns EOF if end-of-file has been detected while reading
+ *      from stream; otherwise returns 0.
+ *
+ *      Note that FCGX_HasSeenEOF(s) may return 0, yet an immediately
+ *      following FCGX_GetChar(s) may return EOF.  This function, like
+ *      the standard C stdio function feof, does not provide the
+ *      ability to peek ahead.
+ *
+ * Results:
+ *     EOF if end-of-file has been detected, 0 if not.
+ *
+ *----------------------------------------------------------------------
+ */
+int FCGX_HasSeenEOF(FCGX_Stream *stream) {
+    return (stream->isClosed) ? EOF : 0;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_PutChar --
+ *
+ *      Writes a byte to the output stream.
+ *
+ * Results:
+ *     The byte, or EOF (-1) if an error occurred.
+ * 
+ *----------------------------------------------------------------------
+ */
+int FCGX_PutChar(int c, FCGX_Stream *stream)
+{
+    if(stream->wrNext != stream->stop)
+        return (*stream->wrNext++ = c);
+    if(stream->isClosed || stream->isReader)
+        return EOF;
+    stream->emptyBuffProc(stream, FALSE);
+    if(stream->wrNext != stream->stop)
+        return (*stream->wrNext++ = c);
+    ASSERT(stream->isClosed); /* bug in emptyBuffProc if not */
+    return EOF;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_PutStr --
+ *
+ *      Writes n consecutive bytes from the character array str
+ *      into the output stream.  Performs no interpretation
+ *      of the output bytes.
+ *
+ * Results:
+ *      Number of bytes written (n) for normal return,
+ *      EOF (-1) if an error occurred.
+ * 
+ *----------------------------------------------------------------------
+ */
+int FCGX_PutStr(const char *str, int n, FCGX_Stream *stream)
+{
+    int m, bytesMoved;
+
+    /*
+     * Fast path: room for n bytes in the buffer
+     */
+    if(n <= (stream->stop - stream->wrNext)) {
+        memcpy(stream->wrNext, str, n);
+        stream->wrNext += n;
+        return n;
+    }
+    /*
+     * General case: stream is closed or buffer empty procedure
+     * needs to be called
+     */
+    bytesMoved = 0;
+    for (;;) {
+        if(stream->wrNext != stream->stop) {
+            m = min(n - bytesMoved, stream->stop - stream->wrNext);
+            memcpy(stream->wrNext, str, m);
+            bytesMoved += m;
+            stream->wrNext += m;
+            if(bytesMoved == n)
+                return bytesMoved;
+            str += m;
+       }
+        if(stream->isClosed || stream->isReader)
+            return -1;
+        stream->emptyBuffProc(stream, FALSE);
+    }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_PutS --
+ *
+ *      Writes a character string to the output stream.
+ *
+ * Results:
+ *      number of bytes written for normal return,
+ *      EOF (-1) if an error occurred.
+ * 
+ *----------------------------------------------------------------------
+ */
+int FCGX_PutS(const char *str, FCGX_Stream *stream)
+{
+    return FCGX_PutStr(str, strlen(str), stream);
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_FPrintF --
+ *
+ *      Performs output formatting and writes the results
+ *      to the output stream.
+ *
+ * Results:
+ *      number of bytes written for normal return,
+ *      EOF (-1) if an error occurred.
+ * 
+ *----------------------------------------------------------------------
+ */
+int FCGX_FPrintF(FCGX_Stream *stream, const char *format, ...)
+{
+    int result;
+    va_list ap;
+    va_start(ap, format);
+    result = FCGX_VFPrintF(stream, format, ap);
+    va_end(ap);
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_VFPrintF --
+ *
+ *      Performs output formatting and writes the results
+ *      to the output stream.
+ *
+ * Results:
+ *      number of bytes written for normal return,
+ *      EOF (-1) if an error occurred.
+ * 
+ *----------------------------------------------------------------------
+ */
+
+#define PRINTF_BUFFLEN 100
+    /*
+     * More than sufficient space for all unmodified conversions
+     * except %s and %f.
+     */
+#define FMT_BUFFLEN 25
+    /*
+     * Max size of a format specifier is 1 + 5 + 7 + 7 + 2 + 1 + slop
+     */
+static void CopyAndAdvance(char **destPtr, char **srcPtr, int n);
+
+int FCGX_VFPrintF(FCGX_Stream *stream, const char *format, va_list arg)
+{
+    char *f, *fStop, *percentPtr, *p, *fmtBuffPtr, *buffPtr;
+    int op, performedOp, sizeModifier, buffCount, buffLen, specifierLength;
+    int fastPath, n, auxBuffLen, buffReqd, minWidth, precision, exp;
+    char *auxBuffPtr = NULL;
+    int streamCount = 0;
+    char fmtBuff[FMT_BUFFLEN];
+    char buff[PRINTF_BUFFLEN];
+
+    int intArg;
+    short shortArg;
+    long longArg;
+    unsigned unsignedArg;
+    unsigned long uLongArg;
+    unsigned short uShortArg;
+    char *charPtrArg;
+    void *voidPtrArg;
+    int *intPtrArg;
+    long *longPtrArg;
+    short *shortPtrArg;
+    double doubleArg;
+    LONG_DOUBLE lDoubleArg;
+
+    fmtBuff[0] = '%';
+    f = (char *) format;
+    fStop = f + strlen(f);
+    while (f != fStop) {
+        percentPtr = memchr(f, '%', fStop - f);
+        if(percentPtr == NULL) percentPtr = fStop;
+        if(percentPtr != f) {
+            if(FCGX_PutStr(f, percentPtr - f, stream) < 0) goto ErrorReturn;
+            streamCount += percentPtr - f;
+            f = percentPtr;
+            if(f == fStop) break;
+       }
+        fastPath = TRUE;
+        /*
+         * The following loop always executes either once or twice.
+         */
+        for (;;) {
+            if(fastPath) {
+                /*
+                 * Fast path: Scan optimistically, hoping that no flags,
+                 * minimum field width, or precision are specified.
+                 * Use the preallocated buffer, which is large enough
+                 * for all fast path cases.  If the conversion specifier
+                 * is really more complex, run the loop a second time
+                 * using the slow path.
+                 * Note that fast path execution of %s bypasses the buffer
+                 * and %f is not attempted on the fast path due to
+                 * its large buffering requirements.
+                 */
+                op = *(percentPtr + 1);
+                switch(op) {
+                   case 'l':
+                   case 'L':
+                    case 'h':
+                        sizeModifier = op;
+                        op = *(percentPtr + 2);
+                        fmtBuff[1] = sizeModifier;
+                        fmtBuff[2] = op;
+                        fmtBuff[3] = '\0';
+                        specifierLength = 3;
+                        break;
+                   default:
+                        sizeModifier = ' ';
+                        fmtBuff[1] = op;
+                        fmtBuff[2] = '\0';
+                        specifierLength = 2;
+                        break;
+               }
+                buffPtr = buff;
+                buffLen = PRINTF_BUFFLEN;
+           } else {
+                /*
+                 * Slow path: Scan the conversion specifier and construct
+                 * a new format string, compute an upper bound on the
+                 * amount of buffering that sprintf will require,
+                 * and allocate a larger buffer if necessary.
+                 */
+                p = percentPtr + 1;
+                fmtBuffPtr = &fmtBuff[1];
+                /*
+                 * Scan flags
+                 */
+                n = strspn(p, "-0+ #");
+                if(n > 5) goto ErrorReturn;
+                CopyAndAdvance(&fmtBuffPtr, &p, n);
+                /*
+                 * Scan minimum field width
+                 */
+                n = strspn(p, "0123456789");
+                if(n == 0) {
+                    if(*p == '*') {
+                        minWidth = va_arg(arg, int);
+                        if(abs(minWidth) > 999999) goto ErrorReturn;
+                       /*
+                        * The following use of strlen rather than the
+                        * value returned from sprintf is because SUNOS4
+                        * returns a char * instead of an int count.
+                        */
+                       sprintf(fmtBuffPtr, "%d", minWidth);
+                        fmtBuffPtr += strlen(fmtBuffPtr);
+                        p++;
+                   } else {
+                        minWidth = 0;
+                   }
+               } else if(n <= 6) {
+                    minWidth = strtol(p, NULL, 10);
+                    CopyAndAdvance(&fmtBuffPtr, &p, n);
+                } else {
+                    goto ErrorReturn;
+                }
+                /*
+                 * Scan precision
+                 */
+               if(*p == '.') {
+                    CopyAndAdvance(&fmtBuffPtr, &p, 1);
+                    n = strspn(p, "0123456789");
+                    if(n == 0) {
+                        if(*p == '*') {
+                            precision = va_arg(arg, int);
+                            if(precision < 0) precision = 0;
+                            if(precision > 999999) goto ErrorReturn;
+                       /*
+                        * The following use of strlen rather than the
+                        * value returned from sprintf is because SUNOS4
+                        * returns a char * instead of an int count.
+                        */
+                           sprintf(fmtBuffPtr, "%d", precision);
+                           fmtBuffPtr += strlen(fmtBuffPtr);
+                            p++;
+                       } else {
+                            precision = 0;
+                       }
+                   } else if(n <= 6) {
+                        precision = strtol(p, NULL, 10);
+                        CopyAndAdvance(&fmtBuffPtr, &p, n);
+                    } else {
+                        goto ErrorReturn;
+                    }
+                } else {
+                    precision = -1;
+                }
+                /*
+                 * Scan size modifier and conversion operation
+                 */
+                switch(*p) {
+                   case 'l':
+                    case 'L':
+                    case 'h':
+                        sizeModifier = *p;
+                        CopyAndAdvance(&fmtBuffPtr, &p, 1);
+                        break;
+                   default:
+                        sizeModifier = ' ';
+                        break;
+                }
+                op = *p;
+                CopyAndAdvance(&fmtBuffPtr, &p, 1);
+                ASSERT(fmtBuffPtr - fmtBuff < FMT_BUFFLEN);
+                *fmtBuffPtr = '\0';
+                specifierLength = p - percentPtr;
+                /*
+                 * Bound the required buffer size.  For s and f
+                 * conversions this requires examining the argument.
+                 */
+                switch(op) {
+                   case 'd':
+                    case 'i':
+                    case 'u':
+                    case 'o':
+                    case 'x':
+                    case 'X':
+                    case 'c':
+                    case 'p':
+                        buffReqd = max(precision, 46);
+                        break;
+                   case 's':
+                        charPtrArg = va_arg(arg, char *);
+                        if(precision == -1) {
+                           buffReqd = strlen(charPtrArg);
+                       } else {
+                           p = memchr(charPtrArg, '\0', precision);
+                            buffReqd =
+                             (p == NULL) ? precision : p - charPtrArg;
+                       }
+                        break;
+                   case 'f':
+                        switch(sizeModifier) {
+                            case ' ':
+                                doubleArg = va_arg(arg, double);
+                               frexp(doubleArg, &exp);
+                                break;
+                            case 'L':
+                                lDoubleArg = va_arg(arg, LONG_DOUBLE);
+                               frexp(lDoubleArg, &exp);
+                                break;
+                            default:
+                                goto ErrorReturn;
+                        }
+                        if(precision == -1) precision = 6;
+                        buffReqd = precision + 3 + ((exp > 0) ? exp/3 : 0);
+                        break;
+                   case 'e':
+                   case 'E':
+                   case 'g':
+                   case 'G':
+                        if(precision == -1) precision = 6;
+                        buffReqd = precision + 8;
+                        break;
+                   case 'n':
+                   case '%':
+                   default:
+                        goto ErrorReturn;
+                        break;
+                }
+                buffReqd = max(buffReqd + 10, minWidth);
+                /*
+                 * Allocate the buffer
+                 */
+               if(buffReqd <= PRINTF_BUFFLEN) {
+                    buffPtr = buff;
+                   buffLen = PRINTF_BUFFLEN;
+               } else {
+                    if(auxBuffPtr == NULL || buffReqd > auxBuffLen) {
+                       if(auxBuffPtr != NULL) free(auxBuffPtr);
+                        auxBuffPtr = Malloc(buffReqd);
+                        auxBuffLen = buffReqd;
+                        if(auxBuffPtr == NULL) goto ErrorReturn;
+                   }
+                    buffPtr = auxBuffPtr;
+                   buffLen = auxBuffLen;
+               }
+           }
+            /*
+             * This giant switch statement requires the following variables
+             * to be set up: op, sizeModifier, arg, buffPtr, fmtBuff.
+             * When fastPath == FALSE and op == 's' or 'f', the argument
+             * has been read into charPtrArg, doubleArg, or lDoubleArg.
+             * The statement produces the boolean performedOp, TRUE iff
+             * the op/sizeModifier were executed and argument consumed;
+             * if performedOp, the characters written into buffPtr[]
+             * and the character count buffCount (== EOF meaning error).
+             *
+             * The switch cases are arranged in the same order as in the
+             * description of fprintf in section 15.11 of Harbison and Steele.
+             */
+            performedOp = TRUE;
+            switch(op) {
+               case 'd':
+               case 'i':
+                    switch(sizeModifier) {
+                        case ' ':
+                            intArg = va_arg(arg, int);
+                           sprintf(buffPtr, fmtBuff, intArg);
+                            buffCount = strlen(buffPtr);
+                            break;
+                       case 'l':
+                            longArg = va_arg(arg, long);
+                            sprintf(buffPtr, fmtBuff, longArg);
+                            buffCount = strlen(buffPtr);
+                            break;
+                       case 'h':
+                            shortArg = va_arg(arg, short);
+                           sprintf(buffPtr, fmtBuff, shortArg);
+                            buffCount = strlen(buffPtr);
+                            break;
+                       default:
+                            goto ErrorReturn;
+                   }
+                    break;
+               case 'u':
+                case 'o':
+                case 'x':
+                case 'X':
+                    switch(sizeModifier) {
+                        case ' ':
+                            unsignedArg = va_arg(arg, unsigned);
+                           sprintf(buffPtr, fmtBuff, unsignedArg);
+                            buffCount = strlen(buffPtr);
+                            break;
+                       case 'l':
+                            uLongArg = va_arg(arg, unsigned long);
+                           sprintf(buffPtr, fmtBuff, uLongArg);
+                            buffCount = strlen(buffPtr);
+                            break;
+                        case 'h':
+                            uShortArg = va_arg(arg, unsigned short);
+                           sprintf(buffPtr, fmtBuff, uShortArg);
+                            buffCount = strlen(buffPtr);
+                            break;
+                        default:
+                            goto ErrorReturn;
+                    }
+                    break;
+                case 'c':
+                    switch(sizeModifier) {
+                        case ' ':
+                            intArg = va_arg(arg, int);
+                           sprintf(buffPtr, fmtBuff, intArg);
+                            buffCount = strlen(buffPtr);
+                            break;
+                       case 'l':
+                            /*
+                             * XXX: Allowed by ISO C Amendment 1, but
+                             * many platforms don't yet support wint_t
+                             */
+                            goto ErrorReturn;
+                    default:
+                            goto ErrorReturn;
+                    }
+                    break;
+               case 's':
+                    switch(sizeModifier) {
+                        case ' ':
+                           if(fastPath) {
+                               buffPtr = va_arg(arg, char *);
+                                buffCount = strlen(buffPtr);
+                                buffLen = buffCount + 1;
+                           } else {
+                               sprintf(buffPtr, fmtBuff, charPtrArg);
+                               buffCount = strlen(buffPtr);
+                           }
+                           break;
+                       case 'l':
+                            /*
+                             * XXX: Don't know how to convert a sequence
+                             * of wide characters into a byte stream, or
+                             * even how to predict the buffering required.
+                             */
+                            goto ErrorReturn;
+                        default:
+                            goto ErrorReturn;
+                    }
+                    break;
+                case 'p':
+                    if(sizeModifier != ' ') goto ErrorReturn;
+                    voidPtrArg = va_arg(arg, void *);
+                   sprintf(buffPtr, fmtBuff, voidPtrArg);
+                    buffCount = strlen(buffPtr);
+                    break;
+                case 'n':
+                    switch(sizeModifier) {
+                        case ' ':
+                            intPtrArg = va_arg(arg, int *);
+                            *intPtrArg = streamCount;
+                            break;
+                        case 'l':
+                            longPtrArg = va_arg(arg, long *);
+                            *longPtrArg = streamCount;
+                            break;
+                        case 'h':
+                            shortPtrArg = va_arg(arg, short *);
+                            *shortPtrArg = streamCount;
+                            break;
+                       default:
+                            goto ErrorReturn;
+                   }
+                    buffCount = 0;
+                    break;
+                case 'f':
+                   if(fastPath) {
+                       performedOp = FALSE;
+                        break;
+                   }
+                    switch(sizeModifier) {
+                        case ' ':
+                           sprintf(buffPtr, fmtBuff, doubleArg);
+                            buffCount = strlen(buffPtr);
+                            break;
+                        case 'L':
+                           sprintf(buffPtr, fmtBuff, lDoubleArg);
+                            buffCount = strlen(buffPtr);
+                            break;
+                        default:
+                            goto ErrorReturn;
+                    }
+                    break;
+                case 'e':
+                case 'E':
+                case 'g':
+                case 'G':
+                    switch(sizeModifier) {
+                        case ' ':
+                            doubleArg = va_arg(arg, double);
+                           sprintf(buffPtr, fmtBuff, doubleArg);
+                            buffCount = strlen(buffPtr);
+                            break;
+                        case 'L':
+                            lDoubleArg = va_arg(arg, LONG_DOUBLE);
+                           sprintf(buffPtr, fmtBuff, lDoubleArg);
+                            buffCount = strlen(buffPtr);
+                            break;
+                        default:
+                            goto ErrorReturn;
+                    }
+                    break;
+                case '%':
+                    if(sizeModifier != ' ')
+                        goto ErrorReturn;
+                    buff[0] = '%';
+                    buffCount = 1;
+                    break;
+                case '\0':
+                    goto ErrorReturn;
+                default:
+                    performedOp = FALSE;
+                    break;
+            } /* switch(op) */
+            if(performedOp) break;
+            if(!fastPath) goto ErrorReturn;
+            fastPath = FALSE;
+        } /* for (;;) */
+        ASSERT(buffCount < buffLen);
+        if(buffCount > 0) {
+            if(FCGX_PutStr(buffPtr, buffCount, stream) < 0)
+                goto ErrorReturn;
+            streamCount += buffCount;
+        } else if(buffCount < 0) {
+            goto ErrorReturn;
+       }
+        f += specifierLength;
+    } /* while(f != fStop) */
+    goto NormalReturn;
+  ErrorReturn:
+    streamCount = -1;
+  NormalReturn:
+    if(auxBuffPtr != NULL) free(auxBuffPtr);
+    return streamCount;
+}
+
+/*
+ * Copy n characters from *srcPtr to *destPtr, then increment
+ * both *srcPtr and *destPtr by n.
+ */
+static void CopyAndAdvance(char **destPtr, char **srcPtr, int n)
+{
+    char *dest = *destPtr;
+    char *src = *srcPtr;
+    int i;
+    for (i = 0; i < n; i++)
+        *dest++ = *src++;
+    *destPtr = dest;
+    *srcPtr = src;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_FFlush --
+ *
+ *      Flushes any buffered output.
+ *
+ *      Server-push is a legitimate application of FCGX_FFlush.
+ *      Otherwise, FCGX_FFlush is not very useful, since FCGX_Accept
+ *      does it implicitly.  FCGX_FFlush may reduce performance
+ *      by increasing the total number of operating system calls
+ *      the application makes.
+ *
+ * Results:
+ *      EOF (-1) if an error occurred.
+ * 
+ *----------------------------------------------------------------------
+ */
+int FCGX_FFlush(FCGX_Stream *stream)
+{
+    if(stream->isClosed || stream->isReader)
+        return 0;
+    stream->emptyBuffProc(stream, FALSE);
+    return (stream->isClosed) ? -1 : 0;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_FClose --
+ *
+ *      Performs FCGX_FFlush and closes the stream.
+ *
+ *      This is not a very useful operation, since FCGX_Accept
+ *      does it implicitly.  Closing the out stream before the
+ *      err stream results in an extra write if there's nothing
+ *      in the err stream, and therefore reduces performance.
+ *
+ * Results:
+ *      EOF (-1) if an error occurred.
+ * 
+ *----------------------------------------------------------------------
+ */
+int FCGX_FClose(FCGX_Stream *stream)
+{
+    if(!stream->wasFCloseCalled) {
+        if(!stream->isReader) {
+            stream->emptyBuffProc(stream, TRUE);
+        }
+        stream->wasFCloseCalled = TRUE;
+        stream->isClosed = TRUE;
+        if(stream->isReader) {
+            stream->wrNext = stream->stop = stream->rdNext;
+        } else {
+            stream->rdNext = stream->stop = stream->wrNext;
+        }
+    }
+    return (stream->FCGI_errno == 0) ? 0 : EOF;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * SetError --
+ *
+ *      An error has occurred; save the error code in the stream
+ *      for diagnostic purposes and set the stream state so that
+ *      reads return EOF and writes have no effect.
+ *
+ *----------------------------------------------------------------------
+ */
+static void SetError(FCGX_Stream *stream, int FCGI_errno)
+{
+    /*
+     * Preserve only the first error.
+     */
+    if(stream->FCGI_errno == 0) {
+        stream->FCGI_errno = FCGI_errno;
+        stream->isClosed = TRUE;
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_GetError --
+ *
+ *      Return the stream error code.  0 means no error, > 0
+ *      is an errno(2) error, < 0 is an FCGX_errno error.
+ *
+ *----------------------------------------------------------------------
+ */
+int FCGX_GetError(FCGX_Stream *stream) {
+    return stream->FCGI_errno;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_ClearError --
+ *
+ *      Clear the stream error code and end-of-file indication.
+ *
+ *----------------------------------------------------------------------
+ */
+void FCGX_ClearError(FCGX_Stream *stream) {
+    stream->FCGI_errno = 0;
+    /*
+     * stream->isClosed = FALSE;
+     * XXX: should clear isClosed but work is needed to make it safe
+     * to do so.  For example, if an application calls FClose, gets
+     * an I/O error on the write, calls ClearError and retries
+     * the FClose, FClose (really EmptyBuffProc) will write a second
+     * EOF record.  If an application calls PutChar instead of FClose
+     * after the ClearError, the application will write more data.
+     * The stream's state must discriminate between various states
+     * of the stream that are now all lumped under isClosed.
+     */
+}
+\f
+/*
+ *======================================================================
+ * Parameters
+ *======================================================================
+ */
+
+/*
+ * A vector of pointers representing the parameters received
+ * by a FastCGI application server, with the vector's length
+ * and last valid element so adding new parameters is efficient.
+ */
+
+typedef struct Params {
+    FCGX_ParamArray vec;    /* vector of strings */
+    int length;                    /* number of string vec can hold */
+    char **cur;                    /* current item in vec; *cur == NULL */
+} Params;
+typedef Params *ParamsPtr;
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * NewParams --
+ *
+ *     Creates a new Params structure.
+ *
+ * Results:
+ *      Pointer to the new structure.
+ *
+ *----------------------------------------------------------------------
+ */
+static ParamsPtr NewParams(int length)
+{
+    ParamsPtr result;
+    result = Malloc(sizeof(Params));
+    result->vec = (char **) Malloc(length * sizeof(char *));
+    result->length = length;
+    result->cur = result->vec;
+    *result->cur = NULL;
+    return result;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeParams --
+ *
+ *     Frees a Params structure and all the parameters it contains.
+ *
+ * Side effects:
+ *      paramsPtr becomes invalid.
+ *
+ *----------------------------------------------------------------------
+ */
+static void FreeParams(ParamsPtr *paramsPtrPtr)
+{
+    ParamsPtr paramsPtr = *paramsPtrPtr;
+    char **p;
+    if(paramsPtr == NULL) {
+        return;
+    }
+    for (p = paramsPtr->vec; p < paramsPtr->cur; p++) {
+        free(*p);
+    }
+    free(paramsPtr->vec);
+    free(paramsPtr);
+    *paramsPtrPtr = NULL;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * PutParam --
+ *
+ *     Add a name/value pair to a Params structure.
+ *
+ * Results:
+ *      None.
+ *
+ * Side effects:  
+ *      Parameters structure updated.
+ *
+ *----------------------------------------------------------------------
+ */  
+static void PutParam(ParamsPtr paramsPtr, char *nameValue)
+{
+    int size;
+
+    *paramsPtr->cur++ = nameValue;
+    size = paramsPtr->cur - paramsPtr->vec;
+    if(size >= paramsPtr->length) {
+       paramsPtr->length *= 2;
+       paramsPtr->vec =
+         realloc(paramsPtr->vec, paramsPtr->length * sizeof(char *));
+       paramsPtr->cur = paramsPtr->vec + size;
+    }
+    *paramsPtr->cur = NULL;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_GetParam -- obtain value of FCGI parameter in environment
+ *
+ *
+ * Results:
+ *     Value bound to name, NULL if name not present in the
+ *      environment envp.  Caller must not mutate the result
+ *      or retain it past the end of this request.
+ *
+ *----------------------------------------------------------------------
+ */
+char *FCGX_GetParam(const char *name, FCGX_ParamArray envp)
+{
+    int len;
+    char **p;
+    len = strlen(name);
+    if(len == 0) return NULL;
+    for (p = envp; *p != NULL; p++) {
+        if((strncmp(name, *p, len) == 0) && ((*p)[len] == '=')) {
+            return *p+len+1;
+        }
+    }
+    return NULL;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Start of FastCGI-specific code
+ *
+ *----------------------------------------------------------------------
+ */
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ReadParams --
+ *
+ *      Reads FastCGI name-value pairs from stream until EOF.  Converts
+ *      each pair to name=value format and adds it to Params structure.
+ *
+ *----------------------------------------------------------------------
+ */
+static int ReadParams(Params *paramsPtr, FCGX_Stream *stream)
+{
+    int nameLen, valueLen;
+    unsigned char lenBuff[3];
+    char *nameValue;
+
+    while((nameLen = FCGX_GetChar(stream)) != EOF) {
+        /*
+         * Read name length (one or four bytes) and value length
+         * (one or four bytes) from stream.
+         */
+        if((nameLen & 0x80) != 0) {
+            if(FCGX_GetStr((char *) &lenBuff[0], 3, stream) != 3) {
+                SetError(stream, FCGX_PARAMS_ERROR);
+                return -1;
+           }
+            nameLen = ((nameLen & 0x7f) << 24) + (lenBuff[0] << 16)
+                    + (lenBuff[1] << 8) + lenBuff[2];
+        }
+        if((valueLen = FCGX_GetChar(stream)) == EOF) {
+            SetError(stream, FCGX_PARAMS_ERROR);
+            return -1;
+       }
+        if((valueLen & 0x80) != 0) {
+            if(FCGX_GetStr((char *) &lenBuff[0], 3, stream) != 3) {
+                SetError(stream, FCGX_PARAMS_ERROR);
+                return -1;
+           }
+            valueLen = ((valueLen & 0x7f) << 24) + (lenBuff[0] << 16)
+                    + (lenBuff[1] << 8) + lenBuff[2];
+        }
+        /*
+         * nameLen and valueLen are now valid; read the name and value
+         * from stream and construct a standard environment entry.
+         */
+        nameValue = Malloc(nameLen + valueLen + 2);
+        if(FCGX_GetStr(nameValue, nameLen, stream) != nameLen) {
+            SetError(stream, FCGX_PARAMS_ERROR);
+            free(nameValue);
+            return -1;
+       }
+        *(nameValue + nameLen) = '=';
+        if(FCGX_GetStr(nameValue + nameLen + 1, valueLen, stream)
+                != valueLen) {
+            SetError(stream, FCGX_PARAMS_ERROR);
+            free(nameValue);
+            return -1;
+       }
+        *(nameValue + nameLen + valueLen + 1) = '\0';
+        PutParam(paramsPtr, nameValue);
+    }
+    return 0;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * MakeHeader --
+ *
+ *      Constructs an FCGI_Header struct.
+ *
+ *----------------------------------------------------------------------
+ */
+static FCGI_Header MakeHeader(
+        int type,
+        int requestId,
+        int contentLength,
+        int paddingLength)
+{
+    FCGI_Header header;
+    ASSERT(contentLength >= 0 && contentLength <= FCGI_MAX_LENGTH);
+    ASSERT(paddingLength >= 0 && paddingLength <= 0xff);
+    header.version = FCGI_VERSION_1;
+    header.type             =  type;
+    header.requestIdB1      = (requestId      >> 8) & 0xff;
+    header.requestIdB0      = (requestId          ) & 0xff;
+    header.contentLengthB1  = (contentLength  >> 8) & 0xff;
+    header.contentLengthB0  = (contentLength      ) & 0xff;
+    header.paddingLength    =  paddingLength;
+    header.reserved         =  0;
+    return header;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * MakeEndRequestBody --
+ *
+ *      Constructs an FCGI_EndRequestBody struct.
+ *
+ *----------------------------------------------------------------------
+ */
+static FCGI_EndRequestBody MakeEndRequestBody(
+        int appStatus,
+        int protocolStatus)
+{
+    FCGI_EndRequestBody body;
+    body.appStatusB3 = (appStatus >> 24) & 0xff;
+    body.appStatusB2 = (appStatus >> 16) & 0xff;
+    body.appStatusB1 = (appStatus >>  8) & 0xff;
+    body.appStatusB0 = (appStatus      ) & 0xff;
+    body.protocolStatus = protocolStatus;
+    memset(body.reserved, 0, sizeof(body.reserved));
+    return body;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * MakeUnknownTypeBody --
+ *
+ *      Constructs an FCGI_MakeUnknownTypeBody struct.
+ *
+ *----------------------------------------------------------------------
+ */
+static FCGI_UnknownTypeBody MakeUnknownTypeBody(
+        int type)
+{
+    FCGI_UnknownTypeBody body;
+    body.type = type;
+    memset(body.reserved, 0, sizeof(body.reserved));
+    return body;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * AlignInt8 --
+ *
+ *      Returns the smallest integer greater than or equal to n
+ *      that's a multiple of 8.
+ *
+ *----------------------------------------------------------------------
+ */
+static int AlignInt8(unsigned n) {
+    return (n + 7) & (UINT_MAX - 7);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AlignPtr8 --
+ *
+ *      Returns the smallest pointer greater than or equal to p
+ *      that's a multiple of 8.
+ *
+ *----------------------------------------------------------------------
+ */
+static unsigned char *AlignPtr8(unsigned char *p) {
+    unsigned long u = (unsigned long) p;
+    u = ((u + 7) & (ULONG_MAX - 7)) - u;
+    return p + u;
+}
+\f
+/*
+ * State associated with a request
+ */
+typedef struct ReqData {
+    int ipcFd;               /* < 0 means no connection */
+    int isBeginProcessed;     /* FCGI_BEGIN_REQUEST seen */
+    int requestId;            /* valid if isBeginProcessed */
+    int keepConnection;       /* don't close ipcFd at end of request */
+    int role;
+    int appStatus;
+    int nWriters;             /* number of open writers (0..2) */
+    FCGX_Stream *inStream;
+    FCGX_Stream *outStream;
+    FCGX_Stream *errStream;
+    ParamsPtr paramsPtr;
+} ReqData;
+
+
+/*
+ * State associated with a stream
+ */
+typedef struct FCGX_Stream_Data {
+    unsigned char *buff;      /* buffer after alignment */
+    int bufflen;              /* number of bytes buff can store */
+    unsigned char *mBuff;     /* buffer as returned by Malloc */
+    unsigned char *buffStop;  /* reader: last valid byte + 1 of entire buffer.
+                               * stop generally differs from buffStop for
+                               * readers because of record structure.
+                               * writer: buff + bufflen */
+    int type;                 /* reader: FCGI_PARAMS or FCGI_STDIN
+                               * writer: FCGI_STDOUT or FCGI_STDERR */
+    int eorStop;              /* reader: stop stream at end-of-record */
+    int skip;                 /* reader: don't deliver content bytes */
+    int contentLen;           /* reader: bytes of unread content */
+    int paddingLen;           /* reader: bytes of unread padding */
+    int isAnythingWritten;    /* writer: data has been written to ipcFd */
+    int rawWrite;             /* writer: write data without stream headers */
+    ReqData *reqDataPtr;      /* request data not specific to one stream */
+} FCGX_Stream_Data;
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * WriteCloseRecords --
+ *
+ *      Writes an EOF record for the stream content if necessary.
+ *      If this is the last writer to close, writes an FCGI_END_REQUEST
+ *      record.
+ *
+ *----------------------------------------------------------------------
+ */
+static void WriteCloseRecords(struct FCGX_Stream *stream)
+{
+    FCGX_Stream_Data *data = stream->data;
+    /*
+     * Enter rawWrite mode so final records won't be encapsulated as
+     * stream data.
+     */
+    data->rawWrite = TRUE;
+    /*
+     * Generate EOF for stream content if needed.
+     */
+    if(!(data->type == FCGI_STDERR
+            && stream->wrNext == data->buff
+            && !data->isAnythingWritten)) {
+        FCGI_Header header;
+        header = MakeHeader(data->type, data->reqDataPtr->requestId, 0, 0);
+        FCGX_PutStr((char *) &header, sizeof(header), stream);
+    };
+    /*
+     * Generate FCGI_END_REQUEST record if needed.
+     */
+    if(data->reqDataPtr->nWriters == 1) {
+        FCGI_EndRequestRecord endRequestRecord;
+        endRequestRecord.header = MakeHeader(FCGI_END_REQUEST,
+                data->reqDataPtr->requestId,
+                sizeof(endRequestRecord.body), 0);
+        endRequestRecord.body = MakeEndRequestBody(
+                data->reqDataPtr->appStatus, FCGI_REQUEST_COMPLETE);
+        FCGX_PutStr((char *) &endRequestRecord,
+                sizeof(endRequestRecord), stream);
+    }
+    data->reqDataPtr->nWriters--;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * EmptyBuffProc --
+ *
+ *      Encapsulates any buffered stream content in a FastCGI
+ *      record.  Writes the data, making the buffer empty.
+ *
+ *----------------------------------------------------------------------
+ */
+static void EmptyBuffProc(struct FCGX_Stream *stream, int doClose)
+{
+    FCGX_Stream_Data *data = stream->data;
+    int cLen, eLen;
+    /*
+     * If the buffer contains stream data, fill in the header.
+     * Pad the record to a multiple of 8 bytes in length.  Padding
+     * can't overflow the buffer because the buffer is a multiple
+     * of 8 bytes in length.  If the buffer contains no stream
+     * data, reclaim the space reserved for the header.
+     */
+    if(!data->rawWrite) {
+        cLen = stream->wrNext - data->buff - sizeof(FCGI_Header);
+        if(cLen > 0) {
+            eLen = AlignInt8(cLen);
+            /*
+             * Giving the padding a well-defined value keeps Purify happy.
+             */
+            memset(stream->wrNext, 0, eLen - cLen);
+            stream->wrNext += eLen - cLen;
+            *((FCGI_Header *) data->buff)
+                    = MakeHeader(data->type,
+                            data->reqDataPtr->requestId, cLen, eLen - cLen);
+        } else {
+            stream->wrNext = data->buff;
+       }
+    }
+    if(doClose) {
+        WriteCloseRecords(stream);
+    };
+    if(stream->wrNext != data->buff) {
+        data->isAnythingWritten = TRUE;
+        if(OS_Write(data->reqDataPtr->ipcFd, (char *)data->buff,
+                stream->wrNext - data->buff) < 0) {
+            SetError(stream, OS_Errno);
+            return;
+        }
+        stream->wrNext = data->buff;
+    }
+    /*
+     * The buffer is empty.
+     */
+    if(!data->rawWrite) {
+        stream->wrNext += sizeof(FCGI_Header);
+    }
+}
+\f
+/*
+ * Return codes for Process* functions
+ */
+#define STREAM_RECORD 0
+#define SKIP          1
+#define BEGIN_RECORD  2
+#define MGMT_RECORD   3
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ProcessManagementRecord --
+ *
+ *      Reads and responds to a management record.  The only type of
+ *      management record this library understands is FCGI_GET_VALUES.
+ *      The only variables that this library's FCGI_GET_VALUES
+ *      understands are FCGI_MAX_CONNS, FCGI_MAX_REQS, and FCGI_MPXS_CONNS.
+ *      Ignore other FCGI_GET_VALUES variables; respond to other
+ *      management records with a FCGI_UNKNOWN_TYPE record.
+ *
+ *----------------------------------------------------------------------
+ */
+static int ProcessManagementRecord(int type, FCGX_Stream *stream)
+{
+    FCGX_Stream_Data *data = stream->data;
+    ParamsPtr paramsPtr = NewParams(3);
+    char **pPtr;
+    char response[64]; /* 64 = 8 + 3*(1+1+14+1)* + padding */
+    char *responseP = &response[FCGI_HEADER_LEN];
+    char *name, value;
+    int len, paddedLen;
+    if(type == FCGI_GET_VALUES) {
+        ReadParams(paramsPtr, stream);
+        if((FCGX_GetError(stream) != 0) || (data->contentLen != 0)) {
+            FreeParams(&paramsPtr);
+            return FCGX_PROTOCOL_ERROR;
+        }
+        for (pPtr = paramsPtr->vec; pPtr < paramsPtr->cur; pPtr++) {
+            name = *pPtr;
+            *(strchr(name, '=')) = '\0';
+            if(strcmp(name, FCGI_MAX_CONNS) == 0) {
+                value = '1';
+            } else if(strcmp(name, FCGI_MAX_REQS) == 0) {
+                value = '1';
+            } else if(strcmp(name, FCGI_MPXS_CONNS) == 0) {
+                value = '0';
+            } else {
+                name = NULL;
+            }
+            if(name != NULL) {
+                len = strlen(name);
+                sprintf(responseP, "%c%c%s%c", len, 1, name, value);
+                responseP += len + 3;
+           }
+        }
+        len = responseP - &response[FCGI_HEADER_LEN];
+        paddedLen = AlignInt8(len);
+        *((FCGI_Header *) response)
+            = MakeHeader(FCGI_GET_VALUES_RESULT, FCGI_NULL_REQUEST_ID,
+                         len, paddedLen - len);
+        FreeParams(&paramsPtr);
+    } else {
+        paddedLen = len = sizeof(FCGI_UnknownTypeBody);
+        ((FCGI_UnknownTypeRecord *) response)->header
+            = MakeHeader(FCGI_UNKNOWN_TYPE, FCGI_NULL_REQUEST_ID,
+                         len, 0);
+        ((FCGI_UnknownTypeRecord *) response)->body
+            = MakeUnknownTypeBody(type);
+    }
+    if(OS_Write(data->reqDataPtr->ipcFd,
+            response, FCGI_HEADER_LEN + paddedLen) < 0) {
+        SetError(stream, OS_Errno);
+        return -1;
+    }
+    return MGMT_RECORD;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ProcessBeginRecord --
+ *
+ *      Reads an FCGI_BEGIN_REQUEST record.
+ *
+ * Results:
+ *      BEGIN_RECORD for normal return.  FCGX_PROTOCOL_ERROR for
+ *      protocol error.  SKIP for attempt to multiplex
+ *      connection.  -1 for error from write (errno in stream).
+ *
+ * Side effects:
+ *      In case of BEGIN_RECORD return, stores requestId, role,
+ *      keepConnection values, and sets isBeginProcessed = TRUE.
+ *
+ *----------------------------------------------------------------------
+ */
+static int ProcessBeginRecord(int requestId, FCGX_Stream *stream)
+{
+    FCGX_Stream_Data *data = stream->data;
+    FCGI_BeginRequestBody body;
+    if(requestId == 0 || data->contentLen != sizeof(body)) {
+        return FCGX_PROTOCOL_ERROR;
+    }
+    if(data->reqDataPtr->isBeginProcessed) {
+        /*
+         * The Web server is multiplexing the connection.  This library
+         * doesn't know how to handle multiplexing, so respond with
+         * FCGI_END_REQUEST{protocolStatus = FCGI_CANT_MPX_CONN}
+         */
+        FCGI_EndRequestRecord endRequestRecord;
+        endRequestRecord.header = MakeHeader(FCGI_END_REQUEST,
+                requestId, sizeof(endRequestRecord.body), 0);
+        endRequestRecord.body
+                = MakeEndRequestBody(0, FCGI_CANT_MPX_CONN);
+        if(OS_Write(data->reqDataPtr->ipcFd, (char *) &endRequestRecord,
+                sizeof(endRequestRecord)) < 0) {
+            SetError(stream, OS_Errno);
+            return -1;
+        }
+        return SKIP;
+    }
+    /*
+     * Accept this new request.  Read the record body.
+     */
+    data->reqDataPtr->requestId = requestId;
+    if(FCGX_GetStr((char *) &body, sizeof(body), stream)
+            != sizeof(body)) {
+        return FCGX_PROTOCOL_ERROR;
+    }
+    data->reqDataPtr->keepConnection = (body.flags & FCGI_KEEP_CONN);
+    data->reqDataPtr->role = (body.roleB1 << 8) + body.roleB0;
+    data->reqDataPtr->isBeginProcessed = TRUE;
+    return BEGIN_RECORD;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ProcessHeader --
+ *
+ *      Interprets FCGI_Header.  Processes FCGI_BEGIN_REQUEST and
+ *      management records here; extracts information from stream
+ *      records (FCGI_PARAMS, FCGI_STDIN) into stream.
+ *
+ * Results:
+ *      >= 0 for a normal return, < 0 for error
+ *
+ * Side effects:
+ *      XXX: Many (more than there used to be).
+ *      If !stream->isRequestIdSet, ProcessHeader initializes
+ *      stream->requestId from header and sets stream->isRequestIdSet
+ *      to TRUE.  ProcessHeader also sets stream->contentLen to header's
+ *      contentLength, and sets stream->paddingLen to the header's
+ *      paddingLength.
+ *
+ *----------------------------------------------------------------------
+ */
+static int ProcessHeader(FCGI_Header header, FCGX_Stream *stream)
+{
+    FCGX_Stream_Data *data = stream->data;
+    int requestId;
+    if(header.version != FCGI_VERSION_1) {
+        return FCGX_UNSUPPORTED_VERSION;
+    }
+    requestId =        (header.requestIdB1 << 8)
+                         + header.requestIdB0;
+    data->contentLen = (header.contentLengthB1 << 8)
+                         + header.contentLengthB0;
+    data->paddingLen = header.paddingLength;
+    if(header.type == FCGI_BEGIN_REQUEST) {
+        return ProcessBeginRecord(requestId, stream);
+    }
+    if(requestId  == FCGI_NULL_REQUEST_ID) {
+        return ProcessManagementRecord(header.type, stream);
+    }
+    if(requestId != data->reqDataPtr->requestId) {
+        return SKIP;
+    }
+    if(header.type != data->type) {
+        return FCGX_PROTOCOL_ERROR;
+    }
+    return STREAM_RECORD;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FillBuffProc --
+ *
+ *      Reads bytes from the ipcFd, supplies bytes to a stream client.
+ *
+ *----------------------------------------------------------------------
+ */
+static void FillBuffProc(FCGX_Stream *stream)
+{
+    FCGX_Stream_Data *data = stream->data;
+    FCGI_Header header;
+    int headerLen = 0;
+    int status, count;
+
+    for (;;) {
+        /*
+         * If data->buff is empty, do a read.
+         */
+        if(stream->rdNext == data->buffStop) {
+            count = OS_Read(data->reqDataPtr->ipcFd, (char *)data->buff,
+                            data->bufflen);
+            if(count <= 0) {
+                SetError(stream, (count == 0 ? FCGX_PROTOCOL_ERROR : OS_Errno));
+                return;
+            }
+            stream->rdNext = data->buff;
+            data->buffStop = data->buff + count;
+       }
+        /*
+         * Now data->buff is not empty.  If the current record contains
+         * more content bytes, deliver all that are present in data->buff.
+         */
+        if(data->contentLen > 0) {
+            count = min(data->contentLen, data->buffStop - stream->rdNext);
+            data->contentLen -= count;
+            if(!data->skip) {
+                stream->wrNext = stream->stop = stream->rdNext + count;
+                return;
+           } else {
+                stream->rdNext += count;
+                if(data->contentLen > 0) {
+                    continue;
+               } else {
+                    data->skip = FALSE;
+               }
+           }
+       }
+        /*
+         * If the current record (whose content has been fully consumed by
+         * the client) was padded, skip over the padding bytes.
+         */
+        if(data->paddingLen > 0) {
+            count = min(data->paddingLen, data->buffStop - stream->rdNext);
+            data->paddingLen -= count;
+            stream->rdNext += count;
+            if(data->paddingLen > 0) {
+                continue;
+           }
+       }
+        /*
+         * All done with the current record, including the padding.
+         * If we're in a recursive call from ProcessHeader, deliver EOF.
+         */
+        if(data->eorStop) {
+            stream->stop = stream->rdNext;
+            stream->isClosed = TRUE;
+            return;
+        }
+        /*
+         * Fill header with bytes from the input buffer.
+         */
+        count = min((int)sizeof(header) - headerLen,
+                        data->buffStop - stream->rdNext);
+        memcpy(((char *)(&header)) + headerLen, stream->rdNext, count);
+        headerLen += count;
+        stream->rdNext += count;
+        if(headerLen < sizeof(header)) {
+            continue;
+       };
+        headerLen = 0;
+        /*
+         * Interpret header.  eorStop prevents ProcessHeader from reading
+         * past the end-of-record when using stream to read content.
+         */
+        data->eorStop = TRUE;
+        stream->stop = stream->rdNext;
+        status = ProcessHeader(header, stream);
+        data->eorStop = FALSE;
+        stream->isClosed = FALSE;
+        switch(status) {
+            case STREAM_RECORD:
+                /*
+                 * If this stream record header marked the end of stream
+                 * data deliver EOF to the stream client, otherwise loop
+                 * and deliver data.
+                 *
+                 * XXX: If this is final stream and
+                 * stream->rdNext != data->buffStop, buffered
+                 * data is next request (server pipelining)?
+                 */
+                if(data->contentLen == 0) {
+                    stream->wrNext = stream->stop = stream->rdNext;
+                    stream->isClosed = TRUE;
+                    return;
+               }
+                break;
+           case SKIP:
+                data->skip = TRUE;
+                break;
+            case BEGIN_RECORD:
+                /*
+                 * If this header marked the beginning of a new
+                 * request, return role information to caller.
+                 */
+                return;
+                break;
+            case MGMT_RECORD:
+                break;
+            default:
+                ASSERT(status < 0);
+                SetError(stream, status);
+                return;
+                break;
+       }
+    }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * NewStream --
+ *
+ *      Creates a stream to read or write from an open ipcFd.
+ *      The stream performs reads/writes of up to bufflen bytes.
+ *
+ *----------------------------------------------------------------------
+ */
+static FCGX_Stream *NewStream(
+        ReqData *reqDataPtr, int bufflen, int isReader, int streamType)
+{
+    /*
+     * XXX: It would be a lot cleaner to have a NewStream that only
+     * knows about the type FCGX_Stream, with all other
+     * necessary data passed in.  It appears that not just
+     * data and the two procs are needed for initializing stream,
+     * but also data->buff and data->buffStop.  This has implications
+     * for procs that want to swap buffers, too.
+     */
+    FCGX_Stream *stream = Malloc(sizeof(FCGX_Stream));
+    FCGX_Stream_Data *data = Malloc(sizeof(FCGX_Stream_Data));
+    data->reqDataPtr = reqDataPtr;
+    bufflen = AlignInt8(min(max(bufflen, 32), FCGI_MAX_LENGTH + 1));
+    data->bufflen = bufflen;
+    data->mBuff = Malloc(bufflen);
+    data->buff = AlignPtr8(data->mBuff);
+    if(data->buff != data->mBuff) {
+        data->bufflen -= 8;
+    }
+    if(isReader) {
+        data->buffStop = data->buff;
+    } else {
+        data->buffStop = data->buff + data->bufflen;
+    }
+    data->type = streamType;
+    data->eorStop = FALSE;
+    data->skip = FALSE;
+    data->contentLen = 0;
+    data->paddingLen = 0;
+    data->isAnythingWritten = FALSE;
+    data->rawWrite = FALSE;
+
+    stream->data = data;
+    stream->isReader = isReader;
+    stream->isClosed = FALSE;
+    stream->wasFCloseCalled = FALSE;
+    stream->FCGI_errno = 0;
+    if(isReader) {
+        stream->fillBuffProc = FillBuffProc;
+        stream->emptyBuffProc = NULL;
+        stream->rdNext = data->buff;
+        stream->stop = stream->rdNext;
+        stream->stopUnget = data->buff;
+        stream->wrNext = stream->stop;
+    } else {
+        stream->fillBuffProc = NULL;
+        stream->emptyBuffProc = EmptyBuffProc;
+        stream->wrNext = data->buff + sizeof(FCGI_Header);
+        stream->stop = data->buffStop;
+        stream->stopUnget = NULL;
+        stream->rdNext = stream->stop;
+    }
+    return stream;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeStream --
+ *
+ *      Frees all storage allocated when *streamPtr was created,
+ *      and nulls out *streamPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+void FreeStream(FCGX_Stream **streamPtr)
+{
+    FCGX_Stream *stream = *streamPtr;
+    FCGX_Stream_Data *data;
+    if(stream == NULL) {
+        return;
+    }
+    data = stream->data;
+    data->reqDataPtr = NULL;
+    free(data->mBuff);
+    free(data);
+    free(stream);
+    *streamPtr = NULL;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * SetReaderType --
+ *
+ *      Re-initializes the stream to read data of the specified type.
+ *
+ *----------------------------------------------------------------------
+ */
+static FCGX_Stream *SetReaderType(FCGX_Stream *stream, int streamType)
+{
+    FCGX_Stream_Data *data = stream->data;
+    ASSERT(stream->isReader);
+    data->type = streamType;
+    data->eorStop = FALSE;
+    data->skip = FALSE;
+    data->contentLen = 0;
+    data->paddingLen = 0;
+    stream->wrNext = stream->stop = stream->rdNext;
+    stream->isClosed = FALSE;
+    return stream;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * NewReader --
+ *
+ *      Creates a stream to read streamType records for the given
+ *      request.  The stream performs OS reads of up to bufflen bytes.
+ *
+ *----------------------------------------------------------------------
+ */
+static FCGX_Stream *NewReader(ReqData *reqDataPtr, int bufflen, int streamType)
+{
+    return NewStream(reqDataPtr, bufflen, TRUE, streamType);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NewWriter --
+ *
+ *      Creates a stream to write streamType FastCGI records, using
+ *      the ipcFd and RequestId contained in *reqDataPtr.
+ *      The stream performs OS writes of up to bufflen bytes.
+ *
+ *----------------------------------------------------------------------
+ */
+static FCGX_Stream *NewWriter(ReqData *reqDataPtr, int bufflen, int streamType)
+{
+    return NewStream(reqDataPtr, bufflen, FALSE, streamType);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateWriter --
+ *
+ *      Creates a stream to write streamType FastCGI records, using
+ *      the given ipcFd and request Id.  This function is provided
+ *      for use by cgi-fcgi.  In order to be defensive against misuse,
+ *      this function leaks a little storage; cgi-fcgi doesn't care.
+ *
+ *----------------------------------------------------------------------
+ */
+FCGX_Stream *CreateWriter(
+        int ipcFd,
+        int requestId,
+        int bufflen,
+        int streamType)
+{
+    ReqData *reqDataPtr = Malloc(sizeof(ReqData));
+    reqDataPtr->ipcFd = ipcFd;
+    reqDataPtr->requestId = requestId;
+    /*
+     * Suppress writing an FCGI_END_REQUEST record.
+     */
+    reqDataPtr->nWriters = 2;
+    return NewWriter(reqDataPtr, bufflen, streamType);
+}
+\f
+/*
+ *======================================================================
+ * Control
+ *======================================================================
+ */
+
+static int isCGI = -1;
+static int isFastCGI = -1;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_IsCGI --
+ *
+ *      This routine determines if the process is running as a CGI or
+ *      FastCGI process.  The distinction is made by determining whether
+ *      FCGI_LISTENSOCK_FILENO is a listener ipcFd or the end of a
+ *      pipe (ie. standard in).
+ *
+ * Results:
+ *      TRUE if the process is a CGI process, FALSE if FastCGI.
+ *
+ * Side effects:
+ *      If this is a FastCGI process there's a chance that a connection
+ *      will be accepted while performing the test.  If this occurs,
+ *      the connection is saved and used later by the FCGX_Accept logic.
+ *
+ *----------------------------------------------------------------------
+ */
+int FCGX_IsCGI(void)
+{
+    /*
+     * Already been here, no need to test again.
+     */
+    if(isCGI != -1) {
+        return isCGI;
+    }
+    
+    if(!osLibInitialized) {
+        if(OS_LibInit(NULL) == -1) {
+           exit(OS_Errno);
+       }
+       osLibInitialized = 1;
+    }
+
+    isFastCGI = OS_IsFcgi();
+    isCGI = !isFastCGI;
+    return isCGI;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_Finish --
+ *
+ *      Finishes the current request from the HTTP server.
+ *
+ * Side effects:
+ *
+ *      Finishes the request accepted by (and frees any
+ *      storage allocated by) the previous call to FCGX_Accept.
+ *
+ *      DO NOT retain pointers to the envp array or any strings
+ *      contained in it (e.g. to the result of calling FCGX_GetParam),
+ *      since these will be freed by the next call to FCGX_Finish
+ *      or FCGX_Accept.
+ *
+ *----------------------------------------------------------------------
+ */
+static ReqData *reqDataPtr = NULL;
+
+void FCGX_Finish(void)
+{
+    if(reqDataPtr != NULL && reqDataPtr->inStream != NULL) {
+        /*
+         * Complete the previous request.
+         */
+        int errStatus = FCGX_FClose(reqDataPtr->errStream);
+        int outStatus = FCGX_FClose(reqDataPtr->outStream);
+        int prevRequestFailed = (errStatus != 0)
+                || (outStatus != 0)
+                || (FCGX_GetError(reqDataPtr->inStream) != 0);
+        ASSERT(reqDataPtr->nWriters == 0);
+        FreeStream(&reqDataPtr->inStream);
+        FreeStream(&reqDataPtr->outStream);
+        FreeStream(&reqDataPtr->errStream);
+        FreeParams(&reqDataPtr->paramsPtr);
+        if(prevRequestFailed || !reqDataPtr->keepConnection) {
+            OS_IpcClose(reqDataPtr->ipcFd);
+            reqDataPtr->ipcFd = -1;
+        }
+    }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_Accept --
+ *
+ *      Accepts a new request from the HTTP server.
+ *
+ * Results:
+ *     0 for successful call, -1 for error.
+ *
+ * Side effects:
+ *
+ *      Finishes the request accepted by (and frees any
+ *      storage allocated by) the previous call to FCGX_Accept.
+ *      Creates input, output, and error streams and
+ *      assigns them to *in, *out, and *err respectively.
+ *      Creates a parameters data structure to be accessed
+ *      via getenv(3) (if assigned to environ) or by FCGX_GetParam
+ *      and assigns it to *envp.
+ *
+ *      DO NOT retain pointers to the envp array or any strings
+ *      contained in it (e.g. to the result of calling FCGX_GetParam),
+ *      since these will be freed by the next call to FCGX_Finish
+ *      or FCGX_Accept.
+ *
+ *----------------------------------------------------------------------
+ */
+static ReqData reqData;
+static char *webServerAddressList = NULL;
+
+int FCGX_Accept(
+        FCGX_Stream **in,
+        FCGX_Stream **out,
+        FCGX_Stream **err,
+        FCGX_ParamArray *envp)
+{
+    /*
+     * If our compiler doesn't play by the ISO rules for struct
+     * layout, halt.
+     */
+    ASSERT(sizeof(FCGI_Header) == FCGI_HEADER_LEN);
+
+    if(!osLibInitialized) {
+        if(OS_LibInit(NULL) == -1) {
+           exit(OS_Errno);
+       }
+       osLibInitialized = 1;
+    }
+
+    /*
+     * If our compiler doesn't play by the ISO rules for struct
+     * layout, halt.
+     */
+    ASSERT(sizeof(FCGI_Header) == FCGI_HEADER_LEN);
+    
+    if(reqDataPtr == NULL) {
+       /*
+        * Very first call, so capture FCGI_WEB_SERVER_ADDRS from
+         * the initial environment, and initialize reqDataPtr
+         * and parts of reqData.
+        */
+        char *p = getenv("FCGI_WEB_SERVER_ADDRS");
+       if (p != NULL) {
+            webServerAddressList = StringCopy(p);
+       }
+        reqDataPtr = &reqData;
+        reqDataPtr->ipcFd = -1;
+        reqDataPtr->inStream = NULL;
+        reqDataPtr->outStream = NULL;
+        reqDataPtr->errStream = NULL;
+    } else {
+        /*
+         * Not the first call.  Finish the current request, if any.
+         */
+        FCGX_Finish();
+    }
+    for(;;) {
+        /*
+         * If a connection isn't open, accept a new connection (blocking).
+         * If an OS error occurs in accepting the connection,
+         * return -1 to the caller, who should exit.
+         */
+        if(reqDataPtr->ipcFd < 0) {
+           reqDataPtr->ipcFd = OS_FcgiIpcAccept(webServerAddressList);
+           if(reqDataPtr->ipcFd < 0) {
+                reqDataPtr = NULL;
+               return -1;
+           }
+       }
+        /*
+         * A connection is open.  Read from the connection in order to
+         * get the request's role and environment.  If protocol or other
+         * errors occur, close the connection and try again.
+         */
+        reqDataPtr->isBeginProcessed = FALSE;
+        reqDataPtr->inStream = NewReader(reqDataPtr, 8192, 0);
+        FillBuffProc(reqDataPtr->inStream);
+        if(!reqDataPtr->isBeginProcessed) {
+            goto TryAgain;
+        }
+        {
+            char *roleStr;
+            switch(reqDataPtr->role) {
+                case FCGI_RESPONDER:
+                    roleStr = "FCGI_ROLE=RESPONDER";
+                    break;
+                case FCGI_AUTHORIZER:
+                    roleStr = "FCGI_ROLE=AUTHORIZER";
+                    break;
+                case FCGI_FILTER:
+                    roleStr = "FCGI_ROLE=FILTER";
+                    break;
+                default:
+                    goto TryAgain;
+            }
+            reqDataPtr->paramsPtr = NewParams(30);
+            PutParam(reqDataPtr->paramsPtr, StringCopy(roleStr));
+        }
+        SetReaderType(reqDataPtr->inStream, FCGI_PARAMS);
+        if(ReadParams(reqDataPtr->paramsPtr, reqDataPtr->inStream) >= 0) {
+            /*
+             * Finished reading the environment.  No errors occurred, so
+             * leave the connection-retry loop.
+             */
+            break;
+        }
+        /*
+         * Close the connection and try again.
+         */
+      TryAgain:
+        FreeParams(&reqDataPtr->paramsPtr);
+        FreeStream(&reqDataPtr->inStream);
+        OS_Close(reqDataPtr->ipcFd);
+        reqDataPtr->ipcFd = -1;
+    } /* for (;;) */
+    /*
+     * Build the remaining data structures representing the new
+     * request and return successfully to the caller.
+     */
+    SetReaderType(reqDataPtr->inStream, FCGI_STDIN);
+    reqDataPtr->outStream = NewWriter(reqDataPtr, 8192, FCGI_STDOUT);
+    reqDataPtr->errStream = NewWriter(reqDataPtr, 512, FCGI_STDERR);
+    reqDataPtr->nWriters = 2;
+    *in = reqDataPtr->inStream;
+    *out = reqDataPtr->outStream;
+    *err = reqDataPtr->errStream;
+    *envp = reqDataPtr->paramsPtr->vec;
+    return 0;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_StartFilterData --
+ *
+ *      stream is an input stream for a FCGI_FILTER request.
+ *      stream is positioned at EOF on FCGI_STDIN.
+ *      Repositions stream to the start of FCGI_DATA.
+ *      If the preconditions are not met (e.g. FCGI_STDIN has not
+ *      been read to EOF) sets the stream error code to
+ *      FCGX_CALL_SEQ_ERROR.
+ *
+ * Results:
+ *      0 for a normal return, < 0 for error 
+ *
+ *----------------------------------------------------------------------
+ */
+
+int FCGX_StartFilterData(FCGX_Stream *stream)
+{
+    FCGX_Stream_Data *data = stream->data;
+    if(data->reqDataPtr->role != FCGI_FILTER
+            || !stream->isReader
+            || !stream->isClosed
+            || data->type != FCGI_STDIN) {
+        SetError(stream, FCGX_CALL_SEQ_ERROR);
+        return -1;
+    }
+    SetReaderType(reqDataPtr->inStream, FCGI_DATA);
+    return 0;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * FCGX_SetExitStatus --
+ *
+ *      Sets the exit status for stream's request. The exit status
+ *      is the status code the request would have exited with, had
+ *      the request been run as a CGI program.  You can call
+ *      SetExitStatus several times during a request; the last call
+ *      before the request ends determines the value.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void FCGX_SetExitStatus(int status, FCGX_Stream *stream)
+{
+    FCGX_Stream_Data *data = stream->data;
+    data->reqDataPtr->appStatus = status;
+}
diff --git a/libfcgi/libfcgi.mak b/libfcgi/libfcgi.mak
new file mode 100644 (file)
index 0000000..d4d41f2
--- /dev/null
@@ -0,0 +1,252 @@
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+!IF "$(CFG)" == ""
+CFG=libfcgi - Win32 Debug
+!MESSAGE No configuration specified.  Defaulting to libfcgi - Win32 Debug.
+!ENDIF 
+
+!IF "$(CFG)" != "libfcgi - Win32 Release" && "$(CFG)" !=\
+ "libfcgi - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line.  For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "libfcgi.mak" CFG="libfcgi - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "libfcgi - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "libfcgi - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+!ERROR An invalid configuration is specified.
+!ENDIF 
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE 
+NULL=nul
+!ENDIF 
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "libfcgi - Win32 Debug"
+CPP=cl.exe
+RSC=rc.exe
+MTL=mktyplib.exe
+
+!IF  "$(CFG)" == "libfcgi - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : "$(OUTDIR)\libfcgi.dll"
+
+CLEAN : 
+       -@erase "$(INTDIR)\fcgi_stdio.obj"
+       -@erase "$(INTDIR)\fcgiapp.obj"
+       -@erase "$(INTDIR)\os_win32.obj"
+       -@erase "$(OUTDIR)\libfcgi.dll"
+       -@erase "$(OUTDIR)\libfcgi.exp"
+       -@erase "$(OUTDIR)\libfcgi.lib"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+CPP_PROJ=/nologo /MD /W3 /GX /O2 /I "..\include" /D "WIN32" /D "NDEBUG" /D\
+ "_WINDOWS" /Fp"$(INTDIR)/libfcgi.pch" /YX /Fo"$(INTDIR)/" /c 
+CPP_OBJS=.\Release/
+CPP_SBRS=.\.
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /win32
+MTL_PROJ=/nologo /D "NDEBUG" /win32 
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/libfcgi.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:windows /dll /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib wsock32.lib /nologo /subsystem:windows /dll /incremental:no\
+ /pdb:"$(OUTDIR)/libfcgi.pdb" /machine:I386 /out:"$(OUTDIR)/libfcgi.dll"\
+ /implib:"$(OUTDIR)/libfcgi.lib" 
+LINK32_OBJS= \
+       "$(INTDIR)\fcgi_stdio.obj" \
+       "$(INTDIR)\fcgiapp.obj" \
+       "$(INTDIR)\os_win32.obj"
+
+"$(OUTDIR)\libfcgi.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF  "$(CFG)" == "libfcgi - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : "$(OUTDIR)\libfcgi.dll"
+
+CLEAN : 
+       -@erase "$(INTDIR)\fcgi_stdio.obj"
+       -@erase "$(INTDIR)\fcgiapp.obj"
+       -@erase "$(INTDIR)\os_win32.obj"
+       -@erase "$(INTDIR)\vc40.idb"
+       -@erase "$(INTDIR)\vc40.pdb"
+       -@erase "$(OUTDIR)\libfcgi.dll"
+       -@erase "$(OUTDIR)\libfcgi.exp"
+       -@erase "$(OUTDIR)\libfcgi.ilk"
+       -@erase "$(OUTDIR)\libfcgi.lib"
+       -@erase "$(OUTDIR)\libfcgi.pdb"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /Gm /GX /Zi /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+CPP_PROJ=/nologo /MD /W3 /Gm /GX /Zi /Od /I "..\include" /D "WIN32" /D "_DEBUG"\
+ /D "_WINDOWS" /Fp"$(INTDIR)/libfcgi.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c\
+CPP_OBJS=.\Debug/
+CPP_SBRS=.\.
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /win32
+MTL_PROJ=/nologo /D "_DEBUG" /win32 
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/libfcgi.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib wsock32.lib /nologo /subsystem:windows /dll /incremental:yes\
+ /pdb:"$(OUTDIR)/libfcgi.pdb" /debug /machine:I386 /out:"$(OUTDIR)/libfcgi.dll"\
+ /implib:"$(OUTDIR)/libfcgi.lib" 
+LINK32_OBJS= \
+       "$(INTDIR)\fcgi_stdio.obj" \
+       "$(INTDIR)\fcgiapp.obj" \
+       "$(INTDIR)\os_win32.obj"
+
+"$(OUTDIR)\libfcgi.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF 
+
+.c{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.c{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+################################################################################
+# Begin Target
+
+# Name "libfcgi - Win32 Release"
+# Name "libfcgi - Win32 Debug"
+
+!IF  "$(CFG)" == "libfcgi - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "libfcgi - Win32 Debug"
+
+!ENDIF 
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\fcgi_stdio.c
+DEP_CPP_FCGI_=\
+       "..\include\fcgi_config.h"\
+       "..\include\fcgi_stdio.h"\
+       "..\include\fcgiapp.h"\
+       "..\include\fcgios.h"\
+       {$(INCLUDE)}"\sys\types.h"\
+       
+
+"$(INTDIR)\fcgi_stdio.obj" : $(SOURCE) $(DEP_CPP_FCGI_) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\fcgiapp.c
+DEP_CPP_FCGIA=\
+       "..\include\fastcgi.h"\
+       "..\include\fcgi_config.h"\
+       "..\include\fcgiapp.h"\
+       "..\include\fcgiappmisc.h"\
+       "..\include\fcgimisc.h"\
+       "..\include\fcgios.h"\
+       {$(INCLUDE)}"\sys\types.h"\
+       
+
+"$(INTDIR)\fcgiapp.obj" : $(SOURCE) $(DEP_CPP_FCGIA) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\os_win32.c
+DEP_CPP_OS_WI=\
+       "..\include\fcgios.h"\
+       {$(INCLUDE)}"\sys\timeb.h"\
+       
+
+"$(INTDIR)\os_win32.obj" : $(SOURCE) $(DEP_CPP_OS_WI) "$(INTDIR)"
+
+
+# End Source File
+# End Target
+# End Project
+################################################################################
diff --git a/libfcgi/libfcgi.mak.in b/libfcgi/libfcgi.mak.in
new file mode 100644 (file)
index 0000000..4832293
--- /dev/null
@@ -0,0 +1,376 @@
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+# Another service of Ace Wrecking and Software
+#
+# xmb Generated NMAKE File for NT and VC++ 
+
+!IF "$(CFG)" == ""
+CFG=libfcgi - Win32 Debug
+!MESSAGE No configuration specified.  Defaulting to libfcgi - Win32 Debug.
+!ENDIF 
+
+!IF "$(CFG)" != "libfcgi - Win32 Release" && "$(CFG)" != "libfcgi - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line.  For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "libfcgi.mak" CFG="libfcgi - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "libfcgi - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "libfcgi - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+!ERROR An invalid configuration is specified.
+!ENDIF 
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE 
+NULL=nul
+!ENDIF 
+#
+#
+#  Makefile for @SUBSYSTEM@ version @VERSION@(@PLATFORM@)
+#  Auto-generated, do not edit this Makefile
+#
+
+#
+# Default top-level directories in which to install architecture-
+# specific files (exec_prefix) and machine-independent files such
+# as scripts (prefix).  The values specified here may be overridden
+# at configure-time with the --exec-prefix and --prefix options
+# to the "configure" script.
+
+#-----------------------------------------------------------------------------
+# Normally do not edit this section. It is setup by configure
+#
+# the shell MUST BE /bin/sh
+#
+SHELL  = @SHELL@
+PLATFORM_CLASS = @PLATFORM_CLASS@
+O = @O@
+L = @L@
+X = @X@
+#
+exec_prefix =   @exec_prefix@
+prefix =        @prefix@
+common_prefix = @common_prefix@
+CVS_TAG =       @CVS_TAG@
+SRC_DIR =      @srcdir@
+BIN_DIR =       $(exec_prefix)\bin
+LIB_DIR =       $(exec_prefix)\lib
+ETC_DIR =       $(exec_prefix)\etc
+BINCLUDE_DIR = $(exec_prefix)\include
+INCLUDE_DIR =  $(common_prefix)\include
+CBIN_DIR =      $(common_prefix)\bin
+CLIB_DIR =      $(common_prefix)\lib
+CETC_DIR =      $(common_prefix)\etc
+CONTRIB_DIR =   $(common_prefix)\contrib
+MAN_DIR =       $(common_prefix)\man
+MAN1_DIR =      $(MAN_DIR)\man1
+MAN2_DIR =      $(MAN_DIR)\man2
+MAN3_DIR =      $(MAN_DIR)\man3
+MAN5_DIR =      $(MAN_DIR)\man5
+MAN8_DIR =      $(MAN_DIR)\man8
+INFO_DIR =      $(common_prefix)\info
+INSTALL                = @INSTALL@
+INSTALL_PROGRAM        = @INSTALL@
+INSTALL_DATA   = @INSTALL_DATA@
+CC              = @CC@ @CCDEFS@
+CFLAGS          = @CFLAGS@ @INCLUDE_PATH@ -I. @DEFS@
+RANLIB         = @RANLIB@
+AR             = @AR@
+GENMSGC         = @GENMSGC@
+GENMSGH         = @GENMSGH@
+#
+#---------------------------------------------------------------------------
+#
+#
+# All OMI makefiles will have the following make targets:
+#
+#      all - first rule, builds everything
+#      export - installs everything
+#      test - runs unit tests. This can be a null rule.
+#      clean - cleans up after a make
+#      realclean - cleans up after a configure and make
+#
+
+
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "libfcgi - Win32 Debug"
+CPP=cl.exe
+MTL=mktyplib.exe
+RSC=rc.exe
+MC=mc.exe
+
+!IF  "$(CFG)" == "libfcgi - Win32 Release"
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+
+ALL : "$(OUTDIR)\libfcgi.dll"
+
+
+CLEAN : 
+       -@erase "$(INTDIR)\fcgi_stdio.obj"
+       -@erase "$(INTDIR)\fcgiapp.obj"
+       -@erase "$(INTDIR)\strerror.obj"
+       -@erase "$(INTDIR)\os_win32.obj"
+       -@erase "$(OUTDIR)\libfcgi.dll"
+       -@erase "$(OUTDIR)\libfcgi.exp"
+       -@erase "$(OUTDIR)\libfcgi.lib"
+
+
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+
+!IF "$(INTDIR)" != "$(OUTDIR)"
+"$(INTDIR)" :
+    if not exist "$(INTDIR)/$(NULL)" mkdir "$(INTDIR)"
+!ENDIF
+
+
+EXPORT : 
+       if not exist "$(LIB_DIR)" mkdir "$(LIB_DIR)"
+       $(INSTALL_DATA) $(OUTDIR)\libfcgi.dll $(LIB_DIR)
+       $(INSTALL_DATA) $(OUTDIR)\libfcgi.lib $(LIB_DIR)
+
+
+TEST : "$(OUTDIR)\libfcgi.dll"
+
+
+
+
+# ADD BASE CPP /nologo /MT /W3 /GX /O /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O  /I ../include /I "..\include" /I "\omi\exports\Tcl\V7.4\common\include"  /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+CPP_PROJ=/nologo /MD /W3 /GX /O  /I ../include /I "..\include" /I "\omi\exports\Tcl\V7.4\common\include"  /D "WIN32" /D "NDEBUG" /D "_WINDOWS"\
+ /YX /Fo"$(INTDIR)/" /c 
+CPP_OBJS=.\Release/
+CPP_SBRS=.\.
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /win32
+MTL_PROJ=/nologo /D "NDEBUG" /win32 
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)/libfcgi.res" /d "NDEBUG" 
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/libfcgi.bsc" 
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib  /nologo /subsystem:windows /dll /machine:I386 /nodefaultlib:"LIBCD" /out:"$(OUTDIR)\libfcgi.dll"
+
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib wsock32.lib  /nologo /subsystem:windows /dll /incremental:no\
+ /pdb:"$(OUTDIR)/libfcgi.pdb" /machine:I386 /nodefaultlib:"LIBCD"\
+  /out:"$(OUTDIR)\libfcgi.dll"\
+ /implib:"$(OUTDIR)/libfcgi.lib" 
+
+LINK32_OBJS= \
+       "$(INTDIR)\fcgi_stdio.obj" \
+       "$(INTDIR)\fcgiapp.obj" \
+       "$(INTDIR)\strerror.obj" \
+       "$(INTDIR)\os_win32.obj" \
+
+
+"$(OUTDIR)\libfcgi.dll" : "$(OUTDIR)" "$(INTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF  "$(CFG)" == "libfcgi - Win32 Debug"
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=\local\Debug
+INTDIR=.\Debug
+
+ALL : "$(OUTDIR)\libfcgi.dll"
+
+
+CLEAN : 
+       -@erase "$(INTDIR)\fcgi_stdio.obj"
+       -@erase "$(INTDIR)\fcgiapp.obj"
+       -@erase "$(INTDIR)\strerror.obj"
+       -@erase "$(INTDIR)\os_win32.obj"
+       -@erase "$(OUTDIR)\libfcgi.dll"
+       -@erase "$(OUTDIR)\libfcgi.exp"
+       -@erase "$(OUTDIR)\libfcgi.lib"
+
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+
+!IF "$(INTDIR)" != "$(OUTDIR)"
+"$(INTDIR)" :
+    if not exist "$(INTDIR)/$(NULL)" mkdir "$(INTDIR)"
+!ENDIF
+
+
+EXPORT : 
+       if not exist "$(LIB_DIR)" mkdir "$(LIB_DIR)"
+       $(INSTALL_DATA) $(OUTDIR)\libfcgi.dll $(LIB_DIR)
+       $(INSTALL_DATA) $(OUTDIR)\libfcgi.lib $(LIB_DIR)
+
+
+TEST : "$(OUTDIR)\libfcgi.dll"
+
+
+
+# ADD BASE CPP /nologo /W3 /GX /Z7 /Od /I "..\include" /I "\omi\exports\Tcl\V7.4\common\include"  /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+CPP_PROJ=/nologo /MD /W3 /GX /Z7 /Od /I "..\include" /I "\omi\exports\Tcl\V7.4\common\include"  /D "WIN32" /D "_DEBUG" /D "_WINDOWS"\
+ /YX /Fo"$(INTDIR)/" /c 
+CPP_OBJS=.\Debug/
+CPP_SBRS=.\.
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /win32
+MTL_PROJ=/nologo /D "_DEBUG" /win32 
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)/libfcgi.res" /d "_DEBUG" 
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/libfcgi.bsc" 
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib  /nologo /subsystem:windows /dll /machine:I386 /nodefaultlib:"LIBCD" /out:"$(OUTDIR)\libfcgi.dll"
+
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib wsock32.lib  /nologo /subsystem:windows /dll /incremental:no\
+ /pdb:"$(OUTDIR)/libfcgi.pdb" /machine:I386 /nodefaultlib:"LIBCD"\
+  /out:"$(OUTDIR)\libfcgi.dll"\
+ /implib:"$(OUTDIR)/libfcgi.lib" 
+
+LINK32_OBJS= \
+       "$(INTDIR)\fcgi_stdio.obj" \
+       "$(INTDIR)\fcgiapp.obj" \
+       "$(INTDIR)\strerror.obj" \
+       "$(INTDIR)\os_win32.obj" \
+
+
+"$(OUTDIR)\libfcgi.dll" : "$(OUTDIR)" "$(INTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF 
+
+.c{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.c{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+################################################################################
+# Begin Target
+
+# Name "libfcgi - Win32 Release"
+# Name "libfcgi - Win32 Debug"
+
+!IF  "$(CFG)" == "libfcgi - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "libfcgi - Win32 Debug"
+
+!ENDIF 
+
+################################################################################
+# Begin Source File
+SOURCE=fcgi_stdio.c
+DEF_CPP_FCGI_=\
+       "..\include\fcgi_stdio.h"\
+       "..\include\fcgiapp.h"\
+       "..\include\fcgios.h"
+
+
+"$(INTDIR)\fcgi_stdio.obj" : $(SOURCE) $(DEF_CPP_FCGI_) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+# End Source File
+################################################################################
+# Begin Source File
+SOURCE=fcgiapp.c
+DEF_CPP_FCGIA=\
+       "..\include\fcgi_config.h"\
+       "..\include\fcgimisc.h"\
+       "..\include\fcgiapp.h"\
+       "..\include\fcgiappmisc.h"\
+       "..\include\fastcgi.h"\
+       "..\include\fcgios.h"
+
+
+"$(INTDIR)\fcgiapp.obj" : $(SOURCE) $(DEF_CPP_FCGIA) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+# End Source File
+################################################################################
+# Begin Source File
+SOURCE=strerror.c
+DEF_CPP_STRER=\
+       "..\include\fcgi_config.h"
+
+
+"$(INTDIR)\strerror.obj" : $(SOURCE) $(DEF_CPP_STRER) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+# End Source File
+################################################################################
+# Begin Source File
+SOURCE=os_win32.c
+DEF_CPP_OS_WI=\
+       "..\include\fcgios.h"
+
+
+"$(INTDIR)\os_win32.obj" : $(SOURCE) $(DEF_CPP_OS_WI) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+# End Source File
+
+# End Target
+# End Project
+################################################################################
+
diff --git a/libfcgi/os_unix.c b/libfcgi/os_unix.c
new file mode 100755 (executable)
index 0000000..2e3567c
--- /dev/null
@@ -0,0 +1,1159 @@
+/* 
+ * os_unix.c --
+ *
+ *      Description of file.
+ *
+ *
+ *  Copyright (c) 1995 Open Market, Inc.
+ *  All rights reserved.
+ *
+ *  This file contains proprietary and confidential information and
+ *  remains the unpublished property of Open Market, Inc. Use, 
+ *  disclosure, or reproduction is prohibited except as permitted by 
+ *  express written license agreement with Open Market, Inc. 
+ *
+ *  Bill Snapper
+ *  snapper@openmarket.com
+ */
+
+#ifndef lint
+static const char rcsid[] = "$Id: os_unix.c,v 1.1 1997/09/16 15:36:33 stanleyg Exp $";
+#endif /* not lint */
+
+#include "fcgimisc.h"
+#include "fcgiapp.h"
+#include "fcgiappmisc.h"
+#include "fastcgi.h"
+
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>     /* for memchr() */
+#include <errno.h>
+#include <stdarg.h>
+#include <math.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h> /* for getpeername */
+#endif
+#include <sys/un.h>
+#include <fcntl.h>      /* for fcntl */
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#include <sys/time.h>
+
+#include <sys/types.h>
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#include <arpa/inet.h>
+
+#include "fcgios.h"
+
+#ifndef _CLIENTDATA
+#   if defined(__STDC__) || defined(__cplusplus)
+    typedef void *ClientData;
+#   else
+    typedef int *ClientData;
+#   endif /* __STDC__ */
+#define _CLIENTDATA
+#endif
+
+/*
+ * This structure holds an entry for each oustanding async I/O operation.
+ */
+typedef struct {
+    OS_AsyncProc procPtr;          /* callout completion procedure */
+    ClientData clientData;         /* caller private data */
+    int fd;
+    int len;
+    int offset;
+    void *buf;
+    int inUse;
+} AioInfo;
+
+/*
+ * Entries in the async I/O table are allocated 2 per file descriptor.
+ *
+ * Read Entry Index  = fd * 2
+ * Write Entry Index = (fd * 2) + 1
+ */
+#define AIO_RD_IX(fd) (fd * 2)
+#define AIO_WR_IX(fd) ((fd * 2) + 1)
+
+static int asyncIoTableSize = 16;
+static AioInfo *asyncIoTable = NULL;
+#define STDIN_FILENO  0
+#define STDOUT_FILENO 1
+#define STDERR_FILENO 2
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+static int isFastCGI = FALSE;
+static int libInitialized = FALSE;
+
+static fd_set readFdSet;
+static fd_set writeFdSet;
+
+static fd_set readFdSetPost;
+static int numRdPosted = 0;
+static fd_set writeFdSetPost;
+static int numWrPosted = 0;
+static int volatile maxFd = -1;
+
+/*
+ * fcgiSocket will hold the socket file descriptor if the call to
+ * accept below results in a connection being accepted.  This socket
+ * will be used by FCGX_Accept and then set back to -1.
+ */
+static int fcgiSocket = -1;
+union u_sockaddr {
+    struct sockaddr_un un;
+    struct sockaddr_in in;
+} static fcgiSa;
+static int fcgiClilen;
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * OS_LibInit --
+ *
+ *      Set up the OS library for use.
+ *
+ *      NOTE: This function is really only needed for application
+ *            asynchronous I/O.  It will most likely change in the
+ *            future to setup the multi-threaded environment.
+ *
+ * Results:
+ *     Returns 0 if success, -1 if not.
+ *
+ * Side effects:
+ *     Async I/O table allocated and initialized.
+ *
+ *--------------------------------------------------------------
+ */
+int OS_LibInit(int stdioFds[3])
+{
+    if(libInitialized)
+        return 0;
+    
+    asyncIoTable = malloc(asyncIoTableSize * sizeof(AioInfo));
+    if(asyncIoTable == NULL) {
+        errno = ENOMEM;
+        return -1;
+    }
+    memset((char *) asyncIoTable, 0,
+           asyncIoTableSize * sizeof(AioInfo));
+
+    FD_ZERO(&readFdSet);
+    FD_ZERO(&writeFdSet);
+    FD_ZERO(&readFdSetPost);
+    FD_ZERO(&writeFdSetPost);
+    libInitialized = TRUE;
+    return 0;
+}
+
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * OS_LibShutdown --
+ *
+ *     Shutdown the OS library.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Memory freed, fds closed.
+ *
+ *--------------------------------------------------------------
+ */
+void OS_LibShutdown()
+{
+    if(!libInitialized)
+        return;
+    
+    free(asyncIoTable);
+    asyncIoTable = NULL;
+    libInitialized = FALSE;
+    return;
+}
+
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * OS_BuildSockAddrUn --
+ *
+ *      Using the pathname bindPath, fill in the sockaddr_un structure
+ *      *servAddrPtr and the length of this structure *servAddrLen.
+ *
+ *      The format of the sockaddr_un structure changed incompatibly in
+ *      4.3BSD Reno.  Digital UNIX supports both formats, other systems
+ *      support one or the other.
+ *
+ * Results:
+ *      0 for normal return, -1 for failure (bindPath too long).
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int OS_BuildSockAddrUn(char *bindPath,
+                              struct sockaddr_un *servAddrPtr,
+                              int *servAddrLen)
+{
+    int bindPathLen = strlen(bindPath);
+
+#ifdef HAVE_SOCKADDR_UN_SUN_LEN /* 4.3BSD Reno and later: BSDI, DEC */
+    if(bindPathLen >= sizeof(servAddrPtr->sun_path)) {
+        return -1;
+    }
+#else                           /* 4.3 BSD Tahoe: Solaris, HPUX, DEC, ... */
+    if(bindPathLen > sizeof(servAddrPtr->sun_path)) {
+        return -1;
+    }
+#endif
+    memset((char *) servAddrPtr, 0, sizeof(*servAddrPtr));
+    servAddrPtr->sun_family = AF_UNIX;
+    memcpy(servAddrPtr->sun_path, bindPath, bindPathLen);
+#ifdef HAVE_SOCKADDR_UN_SUN_LEN /* 4.3BSD Reno and later: BSDI, DEC */
+    *servAddrLen = sizeof(servAddrPtr->sun_len)
+            + sizeof(servAddrPtr->sun_family)
+            + bindPathLen + 1;
+    servAddrPtr->sun_len = *servAddrLen;
+#else                           /* 4.3 BSD Tahoe: Solaris, HPUX, DEC, ... */
+    *servAddrLen = sizeof(servAddrPtr->sun_family) + bindPathLen;
+#endif
+    return 0;
+}
+\f
+union SockAddrUnion {
+    struct  sockaddr_un        unixVariant;
+    struct  sockaddr_in        inetVariant;
+};
+
+\f
+/*
+ * OS_CreateLocalIpcFd --
+ *
+ *   This procedure is responsible for creating the listener socket
+ *   on Unix for local process communication.  It will create a
+ *   domain socket or a TCP/IP socket bound to "localhost" and return
+ *   a file descriptor to it to the caller.
+ *
+ * Results:
+ *      Listener socket created.  This call returns either a valid
+ *      file descriptor or -1 on error.
+ *
+ * Side effects:
+ *      None.
+ *
+ *----------------------------------------------------------------------
+ */
+int OS_CreateLocalIpcFd(char *bindPath)
+{
+    int listenSock, servLen;
+    union   SockAddrUnion sa;
+    int            tcp = FALSE;
+    char    *tp;
+    short   port;
+    char    host[MAXPATHLEN];
+
+    strcpy(host, bindPath);
+    if((tp = strchr(host, ':')) != 0) {
+       *tp++ = 0;
+       if((port = atoi(tp)) == 0) {
+           *--tp = ':';
+        } else {
+           tcp = TRUE;
+        }
+    }
+    if(tcp && (*host && strcmp(host, "localhost") != 0)) {
+       fprintf(stderr, "To start a service on a TCP port can not "
+                       "specify a host name.\n"
+                       "You should either use \"localhost:<port>\" or "
+                       " just use \":<port>.\"\n");
+       exit(1);
+    }
+
+    if(tcp) {
+       listenSock = socket(AF_INET, SOCK_STREAM, 0);
+        if(listenSock >= 0) {
+            int flag = 1;
+            if(setsockopt(listenSock, SOL_SOCKET, SO_REUSEADDR,
+                          (char *) &flag, sizeof(flag)) < 0) {
+                fprintf(stderr, "Can't set SO_REUSEADDR.\n");
+               exit(1001);
+           }
+       }
+    } else {
+       listenSock = socket(AF_UNIX, SOCK_STREAM, 0);
+    }
+    if(listenSock < 0) {
+        return -1;
+    }
+
+    /*
+     * Bind the listening socket.
+     */
+    if(tcp) {
+       memset((char *) &sa.inetVariant, 0, sizeof(sa.inetVariant));
+       sa.inetVariant.sin_family = AF_INET;
+       sa.inetVariant.sin_addr.s_addr = htonl(INADDR_ANY);
+       sa.inetVariant.sin_port = htons(port);
+       servLen = sizeof(sa.inetVariant);
+    } else {
+       unlink(bindPath);
+       if(OS_BuildSockAddrUn(bindPath, &sa.unixVariant, &servLen)) {
+           fprintf(stderr, "Listening socket's path name is too long.\n");
+           exit(1000);
+       }
+    }
+    if(bind(listenSock, (struct sockaddr *) &sa.unixVariant, servLen) < 0
+       || listen(listenSock, 5) < 0) {
+       perror("bind/listen");
+        exit(errno);
+    }
+
+    return listenSock;
+}
+
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * OS_FcgiConnect --
+ *
+ *     Create the socket and connect to the remote application if
+ *      possible.
+ *
+ *      This was lifted from the cgi-fcgi application and was abstracted
+ *      out because Windows NT does not have a domain socket and must
+ *      use a named pipe which has a different API altogether.
+ *
+ * Results:
+ *      -1 if fail or a valid file descriptor if connection succeeds.
+ *
+ * Side effects:
+ *      Remote connection established.
+ *
+ *----------------------------------------------------------------------
+ */
+int OS_FcgiConnect(char *bindPath)
+{
+    union   SockAddrUnion sa;
+    int servLen, resultSock;
+    int connectStatus;
+    char    *tp;
+    char    host[MAXPATHLEN];
+    short   port;
+    int            tcp = FALSE;
+
+    strcpy(host, bindPath);
+    if((tp = strchr(host, ':')) != 0) {
+       *tp++ = 0;
+       if((port = atoi(tp)) == 0) {
+           *--tp = ':';
+        } else {
+           tcp = TRUE;
+        }
+    }
+    if(tcp == TRUE) {
+       struct  hostent *hp;
+       if((hp = gethostbyname((*host ? host : "localhost"))) == NULL) {
+           fprintf(stderr, "Unknown host: %s\n", bindPath);
+           exit(1000);
+       }
+       sa.inetVariant.sin_family = AF_INET;
+       memcpy((caddr_t)&sa.inetVariant.sin_addr, hp->h_addr, hp->h_length);
+       sa.inetVariant.sin_port = htons(port);
+       servLen = sizeof(sa.inetVariant);
+       resultSock = socket(AF_INET, SOCK_STREAM, 0);
+    } else {
+       if(OS_BuildSockAddrUn(bindPath, &sa.unixVariant, &servLen)) {
+           fprintf(stderr, "Listening socket's path name is too long.\n");
+           exit(1000);
+       }
+       resultSock = socket(AF_UNIX, SOCK_STREAM, 0);
+    }
+
+    assert(resultSock >= 0);
+    connectStatus = connect(resultSock, (struct sockaddr *) &sa.unixVariant,
+                             servLen);
+    if(connectStatus >= 0) {
+        return resultSock;
+    } else {
+        /*
+         * Most likely (errno == ENOENT || errno == ECONNREFUSED)
+         * and no FCGI application server is running.
+         */
+        close(resultSock);
+        return -1;
+    }
+}
+     
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * OS_Read --
+ *
+ *     Pass through to the unix read function.
+ *
+ * Results:
+ *     Returns number of byes read, 0, or -1 failure: errno
+ *      contains actual error.
+ *
+ * Side effects:
+ *     None.
+ *
+ *--------------------------------------------------------------
+ */
+int OS_Read(int fd, char * buf, size_t len)
+{
+    return(read(fd, buf, len));
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * OS_Write --
+ *
+ *     Pass through to unix write function.
+ *
+ * Results:
+ *     Returns number of byes read, 0, or -1 failure: errno
+ *      contains actual error.
+ *
+ * Side effects:
+ *     none.
+ *
+ *--------------------------------------------------------------
+ */
+int OS_Write(int fd, char * buf, size_t len)
+{
+    return(write(fd, buf, len));
+}
+
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * OS_SpawnChild --
+ *
+ *     Spawns a new FastCGI listener process.
+ *
+ * Results:
+ *      0 if success, -1 if error.
+ *
+ * Side effects:
+ *      Child process spawned.
+ *
+ *----------------------------------------------------------------------
+ */
+int OS_SpawnChild(char *appPath, int listenFd)
+{
+    int forkResult;
+
+    forkResult = fork();
+    if(forkResult < 0) {
+        exit(errno);
+    }
+
+    if(forkResult == 0) {
+        /*
+         * Close STDIN unconditionally.  It's used by the parent
+         * process for CGI communication.  The FastCGI applciation
+         * will be replacing this with the FastCGI listenFd IF
+         * STDIN_FILENO is the same as FCGI_LISTENSOCK_FILENO
+         * (which it is on Unix).  Regardless, STDIN, STDOUT, and
+         * STDERR will be closed as the FastCGI process uses a
+         * multiplexed socket in their place.
+         */
+        close(STDIN_FILENO);
+
+        /*
+         * If the listenFd is already the value of FCGI_LISTENSOCK_FILENO
+         * we're set.  If not, change it so the child knows where to
+         * get the listen socket from.
+         */
+        if(listenFd != FCGI_LISTENSOCK_FILENO) {
+            dup2(listenFd, FCGI_LISTENSOCK_FILENO);
+            close(listenFd);
+        }
+
+       close(STDOUT_FILENO);
+       close(STDERR_FILENO);
+
+        /*
+        * We're a child.  Exec the application.
+         *
+         * XXX: entire environment passes through
+        */
+       execl(appPath, appPath, NULL);
+       /*
+        * XXX: Can't do this as we've already closed STDERR!!!
+        *
+        * perror("exec");
+        */
+       exit(errno);
+    }
+    return 0;
+}
+
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * OS_AsyncReadStdin --
+ *
+ *     This initiates an asynchronous read on the standard
+ *     input handle.
+ *
+ *      The abstraction is necessary because Windows NT does not
+ *      have a clean way of "select"ing a file descriptor for
+ *      I/O.
+ *
+ * Results:
+ *     -1 if error, 0 otherwise.
+ *
+ * Side effects:
+ *     Asynchronous bit is set in the readfd variable and
+ *      request is enqueued.
+ *
+ *--------------------------------------------------------------
+ */
+int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr, 
+                      ClientData clientData)
+{
+    int index = AIO_RD_IX(STDIN_FILENO);
+
+    ASSERT(asyncIoTable[index].inUse == 0);
+    asyncIoTable[index].procPtr = procPtr;
+    asyncIoTable[index].clientData = clientData;
+    asyncIoTable[index].fd = STDIN_FILENO;
+    asyncIoTable[index].len = len;
+    asyncIoTable[index].offset = 0;
+    asyncIoTable[index].buf = buf;
+    asyncIoTable[index].inUse = 1;
+    FD_SET(STDIN_FILENO, &readFdSet);
+    if(STDIN_FILENO > maxFd)
+        maxFd = STDIN_FILENO;
+    return 0;
+}
+
+static void GrowAsyncTable(void)
+{
+    int oldTableSize = asyncIoTableSize;
+    
+    asyncIoTableSize = asyncIoTableSize * 2;
+    asyncIoTable = realloc(asyncIoTable, asyncIoTableSize * sizeof(AioInfo));
+    if(asyncIoTable == NULL) {
+        errno = ENOMEM;
+        exit(errno);
+    }
+    memset((char *) &asyncIoTable[oldTableSize], 0,
+           oldTableSize * sizeof(AioInfo));
+
+}
+
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * OS_AsyncRead --
+ *
+ *     This initiates an asynchronous read on the file
+ *     handle which may be a socket or named pipe.
+ *
+ *     We also must save the ProcPtr and ClientData, so later
+ *     when the io completes, we know who to call.
+ *
+ *     We don't look at any results here (the ReadFile may
+ *     return data if it is cached) but do all completion
+ *     processing in OS_Select when we get the io completion
+ *     port done notifications.  Then we call the callback.
+ *
+ * Results:
+ *     -1 if error, 0 otherwise.
+ *
+ * Side effects:
+ *     Asynchronous I/O operation is queued for completion.
+ *
+ *--------------------------------------------------------------
+ */
+int OS_AsyncRead(int fd, int offset, void *buf, int len,
+                OS_AsyncProc procPtr, ClientData clientData)
+{
+    int index = AIO_RD_IX(fd);
+    
+    ASSERT(asyncIoTable != NULL);
+
+    if(fd > maxFd)
+        maxFd = fd;
+
+    if(index >= asyncIoTableSize) {
+        GrowAsyncTable();
+    }
+
+    ASSERT(asyncIoTable[index].inUse == 0);
+    asyncIoTable[index].procPtr = procPtr;
+    asyncIoTable[index].clientData = clientData;
+    asyncIoTable[index].fd = fd;
+    asyncIoTable[index].len = len;
+    asyncIoTable[index].offset = offset;
+    asyncIoTable[index].buf = buf;
+    asyncIoTable[index].inUse = 1;
+    FD_SET(fd, &readFdSet);
+    return 0;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * OS_AsyncWrite --
+ *
+ *     This initiates an asynchronous write on the "fake" file
+ *     descriptor (which may be a file, socket, or named pipe).
+ *     We also must save the ProcPtr and ClientData, so later
+ *     when the io completes, we know who to call.
+ *
+ *     We don't look at any results here (the WriteFile generally
+ *     completes immediately) but do all completion processing
+ *     in OS_DoIo when we get the io completion port done
+ *     notifications.  Then we call the callback.
+ *
+ * Results:
+ *     -1 if error, 0 otherwise.
+ *
+ * Side effects:
+ *     Asynchronous I/O operation is queued for completion.
+ *
+ *--------------------------------------------------------------
+ */
+int OS_AsyncWrite(int fd, int offset, void *buf, int len, 
+                 OS_AsyncProc procPtr, ClientData clientData)
+{
+    int index = AIO_WR_IX(fd);
+
+    if(fd > maxFd)
+        maxFd = fd;
+
+    if(index >= asyncIoTableSize) {
+        GrowAsyncTable();
+    }
+
+    ASSERT(asyncIoTable[index].inUse == 0);
+    asyncIoTable[index].procPtr = procPtr;
+    asyncIoTable[index].clientData = clientData;
+    asyncIoTable[index].fd = fd;
+    asyncIoTable[index].len = len;
+    asyncIoTable[index].offset = offset;
+    asyncIoTable[index].buf = buf;
+    asyncIoTable[index].inUse = 1;
+    FD_SET(fd, &writeFdSet);
+    return 0;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * OS_Close --
+ *
+ *     Closes the descriptor.  This is a pass through to the
+ *      Unix close.
+ *
+ * Results:
+ *     0 for success, -1 on failure
+ *
+ * Side effects:
+ *     None.
+ *
+ *--------------------------------------------------------------
+ */
+int OS_Close(int fd)
+{
+    int index = AIO_RD_IX(fd);
+    
+    FD_CLR(fd, &readFdSet);
+    FD_CLR(fd, &readFdSetPost);
+    if(asyncIoTable[index].inUse != 0) {
+        asyncIoTable[index].inUse = 0;
+    }
+    
+    FD_CLR(fd, &writeFdSet);
+    FD_CLR(fd, &writeFdSetPost);
+    index = AIO_WR_IX(fd);
+    if(asyncIoTable[index].inUse != 0) {
+        asyncIoTable[index].inUse = 0;
+    }
+    if(maxFd == fd)
+        maxFd--;
+    return close(fd);
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * OS_CloseRead --
+ *
+ *     Cancel outstanding asynchronous reads and prevent subsequent
+ *      reads from completing.
+ *
+ * Results:
+ *     Socket or file is shutdown. Return values mimic Unix shutdown:
+ *             0 success, -1 failure
+ *
+ *--------------------------------------------------------------
+ */
+int OS_CloseRead(int fd)
+{
+    if(asyncIoTable[AIO_RD_IX(fd)].inUse != 0) {
+        asyncIoTable[AIO_RD_IX(fd)].inUse = 0;
+        FD_CLR(fd, &readFdSet);
+    }
+    
+    return shutdown(fd, 0);
+}
+
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * OS_DoIo --
+ *
+ *     This function was formerly OS_Select.  It's purpose is
+ *      to pull I/O completion events off the queue and dispatch
+ *      them to the appropriate place.
+ *
+ * Results:
+ *     Returns 0.
+ *
+ * Side effects:
+ *     Handlers are called.
+ *
+ *--------------------------------------------------------------
+ */
+int OS_DoIo(struct timeval *tmo)
+{
+    int fd, len, selectStatus;
+    OS_AsyncProc procPtr;
+    ClientData clientData;
+    AioInfo *aioPtr;
+    fd_set readFdSetCpy;
+    fd_set writeFdSetCpy;
+
+    FD_ZERO(&readFdSetCpy);
+    FD_ZERO(&writeFdSetCpy);
+
+    for(fd = 0; fd <= maxFd; fd++) {
+        if(FD_ISSET(fd, &readFdSet)) {
+            FD_SET(fd, &readFdSetCpy);
+        }
+        if(FD_ISSET(fd, &writeFdSet)) {
+            FD_SET(fd, &writeFdSetCpy);
+        }
+    }
+    
+    /*
+     * If there were no completed events from a prior call, see if there's
+     * any work to do.
+     */
+    if(numRdPosted == 0 && numWrPosted == 0) {
+        selectStatus = select((maxFd+1), &readFdSetCpy, &writeFdSetCpy,
+                              NULL, tmo);
+        if(selectStatus < 0) {
+            exit(errno);
+       }
+
+        for(fd = 0; fd <= maxFd; fd++) {
+           /*
+            * Build up a list of completed events.  We'll work off of
+            * this list as opposed to looping through the read and write
+            * fd sets since they can be affected by a callbacl routine.
+            */
+           if(FD_ISSET(fd, &readFdSetCpy)) {
+               numRdPosted++;
+               FD_SET(fd, &readFdSetPost);
+               FD_CLR(fd, &readFdSet);
+           }
+
+            if(FD_ISSET(fd, &writeFdSetCpy)) {
+               numWrPosted++;
+               FD_SET(fd, &writeFdSetPost);
+               FD_CLR(fd, &writeFdSet);
+           }
+        }
+    }
+
+    if(numRdPosted == 0 && numWrPosted == 0)
+        return 0;
+           
+    for(fd = 0; fd <= maxFd; fd++) {
+        /*
+        * Do reads and dispatch callback.
+        */
+        if(FD_ISSET(fd, &readFdSetPost) 
+          && asyncIoTable[AIO_RD_IX(fd)].inUse) {
+
+           numRdPosted--;
+           FD_CLR(fd, &readFdSetPost);
+           aioPtr = &asyncIoTable[AIO_RD_IX(fd)];
+           
+           len = read(aioPtr->fd, aioPtr->buf, aioPtr->len);
+
+           procPtr = aioPtr->procPtr;
+           aioPtr->procPtr = NULL;
+           clientData = aioPtr->clientData;
+           aioPtr->inUse = 0;
+
+           (*procPtr)(clientData, len);
+       }
+
+        /*
+        * Do writes and dispatch callback.
+        */
+        if(FD_ISSET(fd, &writeFdSetPost) &&
+           asyncIoTable[AIO_WR_IX(fd)].inUse) {
+
+           numWrPosted--;
+           FD_CLR(fd, &writeFdSetPost);
+           aioPtr = &asyncIoTable[AIO_WR_IX(fd)];
+           
+           len = write(aioPtr->fd, aioPtr->buf, aioPtr->len);
+
+           procPtr = aioPtr->procPtr;
+           aioPtr->procPtr = NULL;
+           clientData = aioPtr->clientData;
+           aioPtr->inUse = 0;
+           (*procPtr)(clientData, len);
+       }
+    }
+    return 0;
+}
+
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ClientAddrOK --
+ *
+ *      Checks if a client address is in a list of allowed addresses
+ *
+ * Results:
+ *     TRUE if address list is empty or client address is present
+ *      in the list, FALSE otherwise.
+ *
+ *----------------------------------------------------------------------
+ */
+static int ClientAddrOK(struct sockaddr_in *saPtr, char *clientList)
+{
+    int result = FALSE;
+    char *clientListCopy, *cur, *next;
+    char *newString = NULL;
+    int strLen;
+
+    if(clientList == NULL || *clientList == '\0') {
+        return TRUE;
+    }
+
+    strLen = strlen(clientList);
+    clientListCopy = malloc(strLen + 1);
+    assert(newString != NULL);
+    memcpy(newString, clientList, strLen);
+    newString[strLen] = '\000';
+    
+    for(cur = clientListCopy; cur != NULL; cur = next) {
+        next = strchr(cur, ',');
+        if(next != NULL) {
+            *next++ = '\0';
+       }
+        if(inet_addr(cur) == saPtr->sin_addr.s_addr) {
+            result = TRUE;
+            break;
+        }
+    }
+    free(clientListCopy);
+    return result;
+}
+
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * AcquireLock --
+ *
+ *      On platforms that implement concurrent calls to accept
+ *      on a shared listening ipcFd, returns 0.  On other platforms,
+ *     acquires an exclusive lock across all processes sharing a
+ *      listening ipcFd, blocking until the lock has been acquired.
+ *
+ * Results:
+ *      0 for successful call, -1 in case of system error (fatal).
+ *
+ * Side effects:
+ *      This process now has the exclusive lock.
+ *
+ *----------------------------------------------------------------------
+ */
+static int AcquireLock(void)
+{
+#ifdef USE_LOCKING
+    struct flock lock;
+    lock.l_type = F_WRLCK;
+    lock.l_start = 0;
+    lock.l_whence = SEEK_SET;
+    lock.l_len = 0;
+
+    if(fcntl(FCGI_LISTENSOCK_FILENO, F_SETLKW, &lock) < 0) {
+        return -1;
+    }
+#endif /* USE_LOCKING */
+    return 0;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * ReleaseLock --
+ *
+ *      On platforms that implement concurrent calls to accept
+ *      on a shared listening ipcFd, does nothing.  On other platforms,
+ *     releases an exclusive lock acquired by AcquireLock.
+ *
+ * Results:
+ *      0 for successful call, -1 in case of system error (fatal).
+ *
+ * Side effects:
+ *      This process no longer holds the lock.
+ *
+ *----------------------------------------------------------------------
+ */
+static int ReleaseLock(void)
+{
+#ifdef USE_LOCKING
+    struct flock lock;
+    lock.l_type = F_UNLCK;
+    lock.l_start = 0;
+    lock.l_whence = SEEK_SET;
+    lock.l_len = 0;
+
+    if(fcntl(FCGI_LISTENSOCK_FILENO, F_SETLK, &lock) < 0) {
+        return -1;
+    }
+#endif /* USE_LOCKING */
+    return 0;
+}
+
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * OS_FcgiIpcAccept --
+ *
+ *     Accepts a new FastCGI connection.  This routine knows whether
+ *      we're dealing with TCP based sockets or NT Named Pipes for IPC.
+ *
+ * Results:
+ *      -1 if the operation fails, otherwise this is a valid IPC fd.
+ *
+ * Side effects:
+ *      New IPC connection is accepted.
+ *
+ *----------------------------------------------------------------------
+ */
+int OS_FcgiIpcAccept(char *clientAddrList)
+{
+    int socket;
+    union u_sockaddr {
+        struct sockaddr_un un;
+       struct sockaddr_in in;
+    } sa;
+    int clilen = sizeof(sa);
+    
+    for(;;) {
+        if(AcquireLock() < 0) {
+           return -1;
+       }
+       /*
+        * If there was a connection accepted from a prior FCGX_IsCGI
+        * test, use it.
+        */
+       if(fcgiSocket != -1) {
+           socket = fcgiSocket;
+           memcpy(&sa, &fcgiSa, fcgiClilen);
+           clilen = fcgiClilen;
+           /*
+            * Clear out the fcgiSocket as we will not be needing
+            * it later.
+            */
+           fcgiSocket = -1;
+       } else {
+            do {
+                socket = accept(FCGI_LISTENSOCK_FILENO,
+                                (struct sockaddr *) &sa.un,
+                                &clilen);
+            } while ((socket < 0) && (errno==EINTR));
+       }
+       if(ReleaseLock() < 0) {
+           return -1;
+       }
+       if(socket < 0) {
+           return -1;
+       }
+       /*
+        * If the new connection uses TCP/IP, check the IP address;
+        * if the address isn't valid, close the connection and
+        * try again.
+        */
+       if(sa.in.sin_family == AF_INET
+          && !ClientAddrOK(&sa.in, clientAddrList)) {
+           close(socket);
+           socket = -1;
+       } else {
+           return socket;
+       }
+    }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * OS_IpcClose
+ *
+ *     OS IPC routine to close an IPC connection.
+ *
+ * Results:
+ *
+ *
+ * Side effects:
+ *      IPC connection is closed.
+ *
+ *----------------------------------------------------------------------
+ */
+int OS_IpcClose(int ipcFd)
+{
+    return OS_Close(ipcFd);
+}
+
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * OS_IsFcgi --
+ *
+ *     Determines whether this process is a FastCGI process or not.
+ *
+ * Results:
+ *      Returns 1 if FastCGI, 0 if not.
+ *
+ * Side effects:
+ *      None.
+ *
+ *----------------------------------------------------------------------
+ */
+int OS_IsFcgi()
+{
+    int flags, flags1 ;
+  
+    fcgiClilen = sizeof(fcgiSa);
+    
+    /*
+     * Put the file descriptor into non-blocking mode.
+     */
+    flags = fcntl(FCGI_LISTENSOCK_FILENO, F_GETFL, 0);
+    flags |= O_NONBLOCK;
+    if( (fcntl(FCGI_LISTENSOCK_FILENO, F_SETFL, flags)) == -1 ) {
+        /*
+         * XXX: The reason for the assert is that this call is not
+         *      supposed to return an error unless the 
+         *      FCGI_LISTENSOCK_FILENO is closed.  If it is closed
+         *      then we have an unexpected error which should cause 
+         *      the assert to pop.  The same is true for the following
+         *      asserts in this function.
+         */
+        assert(errno == 0);
+    }
+
+    /*
+     * Perform an accept() on the file descriptor.  If this is not a
+     * listener socket we will get an error.  Typically this will be
+     * ENOTSOCK but it differs from platform to platform.
+     */
+    fcgiSocket = accept(FCGI_LISTENSOCK_FILENO, (struct sockaddr *) &fcgiSa.un,
+                        &fcgiClilen);
+    if(fcgiSocket >= 0) {
+        /*
+         * This is a FastCGI listener socket because we accepted a
+         * connection.  fcgiSocket will be tested in FCGX_Accept before
+         * performing another accept so that we don't lose this state.
+         *
+         * The new connection is put into blocking mode as we're not
+         * doing asynchronous I/O.
+         */
+        flags1 = fcntl(fcgiSocket, F_GETFL, 0);
+        flags1 &= ~(O_NONBLOCK);
+        if( (fcntl(fcgiSocket, F_SETFL, flags1)) == -1 ) {
+            assert(errno == 0);
+        }
+       isFastCGI = TRUE;
+    } else {
+        /*
+         * If errno == EWOULDBLOCK then this is a valid FastCGI listener 
+         * socket without any connection pending at this time.
+        *
+        * NOTE: hp-ux can also return EAGAIN for listener sockets in
+        * non-blocking mode when no connections are present.
+         */
+#if (EAGAIN != EWOULDBLOCK)
+        if((errno == EWOULDBLOCK) || (errno == EAGAIN)) {
+#else
+        if(errno == EWOULDBLOCK) {
+#endif
+           isFastCGI = TRUE;
+        } else {
+           isFastCGI = FALSE;
+        }
+    }
+
+    /*
+     * Put the file descriptor back in blocking mode.
+     */
+    flags &= ~(O_NONBLOCK);
+    if( (fcntl(FCGI_LISTENSOCK_FILENO, F_SETFL, flags)) == -1 ) {
+        assert(errno == 0);
+    }
+    return isFastCGI;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * OS_SetFlags --
+ *
+ *      Sets selected flag bits in an open file descriptor.
+ *
+ *----------------------------------------------------------------------
+ */
+void OS_SetFlags(int fd, int flags)
+{
+    int val;
+    if((val = fcntl(fd, F_GETFL, 0)) < 0) {
+        exit(errno);
+    }
+    val |= flags;
+    if(fcntl(fd, F_SETFL, val) < 0) {
+        exit(errno);
+    }
+}
diff --git a/libfcgi/os_win32.c b/libfcgi/os_win32.c
new file mode 100755 (executable)
index 0000000..082fe84
--- /dev/null
@@ -0,0 +1,1627 @@
+/* 
+ * os_win32.c --
+ *
+ *
+ *  Copyright (c) 1995 Open Market, Inc.
+ *  All rights reserved.
+ *
+ *  This file contains proprietary and confidential information and
+ *  remains the unpublished property of Open Market, Inc. Use, 
+ *  disclosure, or reproduction is prohibited except as permitted by 
+ *  express written license agreement with Open Market, Inc. 
+ *
+ *  Bill Snapper
+ *  snapper@openmarket.com
+ *
+ * (Special thanks to Karen and Bill.  They made my job much easier and
+ *  significantly more enjoyable.)
+ */
+#ifdef _WIN32
+#define DLLAPI  __declspec(dllexport)
+#endif
+
+#ifndef lint
+static const char rcsid[] = "$Id: os_win32.c,v 1.1 1997/09/16 15:36:33 stanleyg Exp $";
+#endif /* not lint */
+
+#include <windows.h>
+#include <stdio.h>
+#include "fcgios.h"
+
+/*
+ * io.c will instantiate globals; all other
+ * modules access these variables as externs.
+ */
+#ifndef EXTRN
+#define EXTRN extern
+#endif
+
+#include <assert.h>
+#include <sys/timeb.h>
+
+#define WIN32_OPEN_MAX 32 /* XXX: Small hack */
+
+#define ASSERT assert
+
+static HANDLE hIoCompPort = INVALID_HANDLE_VALUE;
+static HANDLE hStdinCompPort = INVALID_HANDLE_VALUE;
+static HANDLE hStdinThread = INVALID_HANDLE_VALUE;
+
+static HANDLE stdioHandles[3] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
+                                INVALID_HANDLE_VALUE};
+
+static HANDLE hPipeMutex = INVALID_HANDLE_VALUE;;
+static char pipeMutexEnv[80] = "";
+
+#define MUTEX_VARNAME "_FCGI_MUTEX_"
+
+/* 
+ * An enumeration of the file types
+ * supported by the FD_TABLE structure.
+ *
+ * XXX: Not all currently supported.  This allows for future
+ *      functionality.
+ */
+typedef enum {
+    FD_UNUSED,
+    FD_FILE_SYNC,
+    FD_FILE_ASYNC,
+    FD_SOCKET_SYNC,
+    FD_SOCKET_ASYNC,
+    FD_PIPE_SYNC,
+    FD_PIPE_ASYNC
+} FILE_TYPE;
+
+typedef union {
+    HANDLE fileHandle;
+    SOCKET sock;
+    unsigned int value;
+} DESCRIPTOR;
+
+/* 
+ * Structure used to map file handle and socket handle
+ * values into values that can be used to create unix-like
+ * select bitmaps, read/write for both sockets/files.
+ */
+struct FD_TABLE {
+    DESCRIPTOR fid;
+    FILE_TYPE type;
+    char *path;
+    DWORD Errno;
+    unsigned long instance;
+    int status;
+    int offset;                        /* only valid for async file writes */
+    LPDWORD offsetHighPtr;     /* pointers to offset high and low words */
+    LPDWORD offsetLowPtr;      /* only valid for async file writes (logs) */
+    HANDLE  hMapMutex;         /* mutex handle for multi-proc offset update */
+    LPVOID  ovList;            /* List of associated OVERLAPPED_REQUESTs */
+};
+
+typedef struct FD_TABLE *PFD_TABLE;
+
+static struct FD_TABLE fdTable[WIN32_OPEN_MAX];
+
+struct OVERLAPPED_REQUEST {
+    OVERLAPPED overlapped;
+    unsigned long instance;    /* file instance (won't match after a close) */
+    OS_AsyncProc procPtr;      /* callback routine */
+    ClientData clientData;     /* callback argument */
+    ClientData clientData1;    /* additional clientData */
+};
+typedef struct OVERLAPPED_REQUEST *POVERLAPPED_REQUEST;
+
+static const char *bindPathPrefix = "\\\\.\\pipe\\FastCGI\\";
+
+static int isFastCGI = FALSE;
+static int isCGI = FALSE;
+static int listenType = FD_UNUSED;
+static HANDLE hListen = INVALID_HANDLE_VALUE;
+static int libInitialized = 0;
+
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Win32NewDescriptor --
+ *
+ *     Set up for I/O descriptor masquerading.
+ *
+ * Results:
+ *     Returns "fake id" which masquerades as a UNIX-style "small
+ *     non-negative integer" file/socket descriptor.
+ *     Win32_* routine below will "do the right thing" based on the
+ *     descriptor's actual type. -1 indicates failure.
+ *
+ * Side effects:
+ *     Entry in fdTable is reserved to represent the socket/file.
+ *
+ *--------------------------------------------------------------
+ */
+static int Win32NewDescriptor(FILE_TYPE type, int fd, int desiredFd)
+{
+    int index;
+
+    /*
+     * If the "desiredFd" is not -1, try to get this entry for our
+     * pseudo file descriptor.  If this is not available, return -1
+     * as the caller wanted to get this mapping.  This is typically
+     * only used for mapping stdio handles.
+     */
+    if ((desiredFd >= 0) &&
+       (desiredFd < WIN32_OPEN_MAX)) {
+
+        if(fdTable[desiredFd].type == FD_UNUSED) {
+           index = desiredFd;
+           goto found_entry;
+        } else {
+           return -1;
+       }
+
+    }
+
+    /*
+     * Next see if the entry that matches "fd" is available.
+     */
+    if ((fd > 0) &&
+       (fd < WIN32_OPEN_MAX) && (fdTable[fd].type == FD_UNUSED)) {
+       index = fd;
+       goto found_entry;
+    }
+
+    /*
+     * Scan entries for one we can use. Start at 1 (0 fake id fails
+     * in some cases). -K*
+     */
+    for (index = 1; index < WIN32_OPEN_MAX; index++)
+       if (fdTable[index].type == FD_UNUSED)
+           break;
+
+    /* If no table entries are available, return error. */
+    if (index == WIN32_OPEN_MAX) {
+       SetLastError(WSAEMFILE);
+       DebugBreak();
+       return -1;
+    }
+
+found_entry:
+    fdTable[index].fid.value = fd;
+    fdTable[index].type = type;
+    fdTable[index].path = NULL;
+    fdTable[index].Errno = NO_ERROR;
+    fdTable[index].status = 0;
+    fdTable[index].offset = -1;
+    fdTable[index].offsetHighPtr = fdTable[index].offsetLowPtr = NULL;
+    fdTable[index].hMapMutex = NULL;
+    fdTable[index].ovList = NULL;
+
+    return index;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * StdinThread--
+ *
+ *     This thread performs I/O on stadard input.  It is needed
+ *      because you can't guarantee that all applications will
+ *      create standard input with sufficient access to perform
+ *      asynchronous I/O.  Since we don't want to block the app
+ *      reading from stdin we make it look like it's using I/O 
+ *      completion ports to perform async I/O.
+ *
+ * Results:
+ *     Data is read from stdin and posted to the io completion
+ *      port.
+ *
+ * Side effects:
+ *     None.
+ *
+ *--------------------------------------------------------------
+ */
+static void StdinThread(LPDWORD startup){
+
+    int doIo = TRUE;
+    int fd;
+    int bytesRead;
+    POVERLAPPED_REQUEST pOv;
+    
+    while(doIo) {
+        /*
+         * Block until a request to read from stdin comes in or a
+         * request to terminate the thread arrives (fd = -1).
+         */
+        if (!GetQueuedCompletionStatus(hStdinCompPort, &bytesRead, &fd,
+           (LPOVERLAPPED *)&pOv, (DWORD)-1) && !pOv) {
+            doIo = 0;
+            break;
+        }
+       
+       ASSERT((fd == STDIN_FILENO) || (fd == -1));
+        if(fd == -1) {
+            doIo = 0;
+            break;
+        }
+        ASSERT(pOv->clientData1 != NULL);
+
+        if(ReadFile(stdioHandles[STDIN_FILENO], pOv->clientData1, bytesRead,
+                    &bytesRead, NULL)) {
+            PostQueuedCompletionStatus(hIoCompPort, bytesRead, 
+                                       STDIN_FILENO, (LPOVERLAPPED)pOv);
+        } else {
+            doIo = 0;
+            break;
+        }
+    }
+
+    ExitThread(0);
+}
+
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * OS_LibInit --
+ *
+ *     Set up the OS library for use.
+ *
+ * Results:
+ *     Returns 0 if success, -1 if not.
+ *
+ * Side effects:
+ *     Sockets initialized, pseudo file descriptors setup, etc.
+ *
+ *--------------------------------------------------------------
+ */
+int OS_LibInit(int stdioFds[3])
+{
+    WORD  wVersion;
+    WSADATA wsaData;
+    int err;
+    int fakeFd;
+    DWORD pipeMode;
+    DWORD threadId;
+    char *cLenPtr = NULL;
+    char *mutexPtr = NULL;
+    
+    if(libInitialized)
+        return 0;
+
+    /*
+     * Initialize windows sockets library.
+     */
+    wVersion = MAKEWORD(1,1);
+    err = WSAStartup( wVersion, &wsaData );
+    if (err) {
+        fprintf(stderr, "Error starting Windows Sockets.  Error: %d",
+               WSAGetLastError());
+       exit(111);
+    }
+
+    /*
+     * Create the I/O completion port to be used for our I/O queue.
+     */
+    if (hIoCompPort == INVALID_HANDLE_VALUE) {
+       hIoCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL,
+                                             0, 1);
+       if(hIoCompPort == INVALID_HANDLE_VALUE) {
+           printf("<H2>OS_LibInit Failed CreateIoCompletionPort!  ERROR: %d</H2>\r\n\r\n",
+              GetLastError());
+           return -1;
+       }
+    }
+
+    /*
+     * Determine if this library is being used to listen for FastCGI
+     * connections.  This is communicated by STDIN containing a
+     * valid handle to a listener object.  In this case, both the
+     * "stdout" and "stderr" handles will be INVALID (ie. closed) by
+     * the starting process.
+     *
+     * The trick is determining if this is a pipe or a socket...
+     *
+     * XXX: Add the async accept test to determine socket or handle to a
+     *      pipe!!!
+     */
+    if((GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) &&
+       (GetStdHandle(STD_ERROR_HANDLE)  == INVALID_HANDLE_VALUE) &&
+       (GetStdHandle(STD_INPUT_HANDLE)  != INVALID_HANDLE_VALUE) ) {
+
+        hListen = GetStdHandle(STD_INPUT_HANDLE);
+        isFastCGI = TRUE;
+
+       /*
+        * Set the pipe handle state so that it operates in wait mode.
+        *
+        * NOTE: The listenFd is not mapped to a pseudo file descriptor
+        *       as all work done on it is contained to the OS library.
+        *
+        * XXX: Initial assumption is that SetNamedPipeHandleState will
+        *      fail if this is an IP socket...
+        */
+        pipeMode = PIPE_READMODE_BYTE | PIPE_WAIT;
+        if(SetNamedPipeHandleState(hListen, &pipeMode, NULL, NULL)) {
+            listenType = FD_PIPE_SYNC;
+            /*
+             * Lookup the mutex.  If one is found, save it and 
+             * remove it from the env table if it's not already
+             * been done.
+             */
+            mutexPtr = getenv(MUTEX_VARNAME);
+            if(mutexPtr != NULL) {
+                hPipeMutex = (HANDLE)atoi(mutexPtr);
+                putenv(MUTEX_VARNAME"=");
+            }
+        } else {
+            listenType = FD_SOCKET_SYNC;
+        }
+    }
+
+    /*
+     * If there are no stdioFds passed in, we're done.
+     */
+    if(stdioFds == NULL) {
+        libInitialized = 1;
+        return 0;
+    }
+    
+    /*
+     * Setup standard input asynchronous I/O.  There is actually a separate
+     * thread spawned for this purpose.  The reason for this is that some
+     * web servers use anonymous pipes for the connection between itself
+     * and a CGI application.  Anonymous pipes can't perform asynchronous
+     * I/O or use I/O completion ports.  Therefore in order to present a
+     * consistent I/O dispatch model to an application we emulate I/O
+     * completion port behavior by having the standard input thread posting
+     * messages to the hIoCompPort which look like a complete overlapped
+     * I/O structure.  This keeps the event dispatching simple from the
+     * application perspective.
+     */
+    stdioHandles[STDIN_FILENO] = GetStdHandle(STD_INPUT_HANDLE);
+
+    if(!SetHandleInformation(stdioHandles[STDIN_FILENO],
+                            HANDLE_FLAG_INHERIT, 0)) {
+/*
+ * XXX: Causes error when run from command line.  Check KB
+        err = GetLastError();
+        DebugBreak();
+       exit(99);
+ */
+    }
+
+    if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
+                                    (int)stdioHandles[STDIN_FILENO],
+                                    STDIN_FILENO)) == -1) {
+        return -1;
+    } else {
+        /*
+        * Set stdin equal to our pseudo FD and create the I/O completion
+        * port to be used for async I/O.
+        */
+       stdioFds[STDIN_FILENO] = fakeFd;
+    }
+
+    /*
+     * Create the I/O completion port to be used for communicating with
+     * the thread doing I/O on standard in.  This port will carry read
+     * and possibly thread termination requests to the StdinThread.
+     */
+    if (hStdinCompPort == INVALID_HANDLE_VALUE) {
+       hStdinCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL,
+                                             0, 1);
+       if(hStdinCompPort == INVALID_HANDLE_VALUE) {
+           printf("<H2>OS_LibInit Failed CreateIoCompletionPort: STDIN!  ERROR: %d</H2>\r\n\r\n",
+              GetLastError());
+           return -1;
+       }
+    }
+
+    /* 
+     * Create the thread that will read stdin if the CONTENT_LENGTH
+     * is non-zero.
+     */
+    if((cLenPtr = getenv("CONTENT_LENGTH")) != NULL &&
+       atoi(cLenPtr) > 0) {
+        hStdinThread = CreateThread(NULL, 8192,
+                                   (LPTHREAD_START_ROUTINE)&StdinThread,
+                                   NULL, 0, &threadId);
+       if (hStdinThread == NULL) {
+           printf("<H2>OS_LibInit Failed to create STDIN thread!  ERROR: %d</H2>\r\n\r\n",
+                  GetLastError());
+           return -1;
+        }
+    }
+
+    /*
+     * STDOUT will be used synchronously.
+     *
+     * XXX: May want to convert this so that it could be used for OVERLAPPED
+     *      I/O later.  If so, model it after the Stdin I/O as stdout is
+     *      also incapable of async I/O on some servers.
+     */
+    stdioHandles[STDOUT_FILENO] = GetStdHandle(STD_OUTPUT_HANDLE);
+    if(!SetHandleInformation(stdioHandles[STDOUT_FILENO],
+                            HANDLE_FLAG_INHERIT, FALSE)) {
+        DebugBreak();
+       exit(99);
+    }
+
+    if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
+                                    (int)stdioHandles[STDOUT_FILENO],
+                                    STDOUT_FILENO)) == -1) {
+        return -1;
+    } else {
+        /*
+        * Set stdout equal to our pseudo FD
+        */
+       stdioFds[STDOUT_FILENO] = fakeFd;
+    }
+
+    stdioHandles[STDERR_FILENO] = GetStdHandle(STD_ERROR_HANDLE);
+    if(!SetHandleInformation(stdioHandles[STDERR_FILENO],
+                            HANDLE_FLAG_INHERIT, FALSE)) {
+        DebugBreak();
+       exit(99);
+    }
+    if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
+                                    (int)stdioHandles[STDERR_FILENO],
+                                    STDERR_FILENO)) == -1) {
+        return -1;
+    } else {
+        /*
+        * Set stderr equal to our pseudo FD
+        */
+       stdioFds[STDERR_FILENO] = fakeFd;
+    }
+
+    return 0;
+}
+
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * OS_LibShutdown --
+ *
+ *     Shutdown the OS library.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Memory freed, handles closed.
+ *
+ *--------------------------------------------------------------
+ */
+void OS_LibShutdown()
+{
+
+    if(hIoCompPort != INVALID_HANDLE_VALUE) {
+        CloseHandle(hIoCompPort);
+       hIoCompPort = INVALID_HANDLE_VALUE;
+    }
+
+    if(hStdinCompPort != INVALID_HANDLE_VALUE) {
+        CloseHandle(hStdinCompPort);
+       hStdinCompPort = INVALID_HANDLE_VALUE;
+    }
+
+    /*
+     * Shutdown the socket library.
+     */
+    WSACleanup();
+    return;
+}
+
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Win32FreeDescriptor --
+ *
+ *     Free I/O descriptor entry in fdTable.
+ *
+ * Results:
+ *     Frees I/O descriptor entry in fdTable.
+ *
+ * Side effects:
+ *     None.
+ *
+ *--------------------------------------------------------------
+ */
+static void Win32FreeDescriptor(int fd)
+{
+    /* Catch it if fd is a bogus value */
+    ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
+    ASSERT(fdTable[fd].type != FD_UNUSED);
+
+    switch (fdTable[fd].type) {
+       case FD_FILE_SYNC:
+       case FD_FILE_ASYNC:
+           /* Free file path string */
+           ASSERT(fdTable[fd].path != NULL);
+           free(fdTable[fd].path);
+           fdTable[fd].path = NULL;
+           break;
+       default:
+           /*
+            * Break through to generic fdTable free-descriptor code
+            */
+           break;
+
+    }
+    ASSERT(fdTable[fd].path == NULL);
+    fdTable[fd].type = FD_UNUSED;
+    fdTable[fd].path = NULL;
+    fdTable[fd].Errno = NO_ERROR;
+    fdTable[fd].offsetHighPtr = fdTable[fd].offsetLowPtr = NULL;
+    if (fdTable[fd].hMapMutex != NULL) {
+        CloseHandle(fdTable[fd].hMapMutex);
+        fdTable[fd].hMapMutex = NULL;
+    }
+    return;
+}
+
+\f
+/*
+ * OS_CreateLocalIpcFd --
+ *
+ *   This procedure is responsible for creating the listener pipe
+ *   on Windows NT for local process communication.  It will create a
+ *   named pipe and return a file descriptor to it to the caller.
+ *
+ * Results:
+ *      Listener pipe created.  This call returns either a valid
+ *      pseudo file descriptor or -1 on error.
+ *
+ * Side effects:
+ *      Listener pipe and IPC address are stored in the FCGI info
+ *         structure.
+ *      'errno' will set on errors (-1 is returned).
+ *
+ *----------------------------------------------------------------------
+ */
+int OS_CreateLocalIpcFd(char *bindPath)
+{
+    int retFd = -1;
+    SECURITY_ATTRIBUTES     sa;
+    HANDLE hListenPipe = INVALID_HANDLE_VALUE;
+    char *localPath;
+    SOCKET listenSock;
+    int bpLen;
+    int servLen;
+    struct  sockaddr_in        sockAddr;
+    char    host[1024];
+    short   port;
+    int            tcp = FALSE;
+    int flag = 1;
+    char    *tp;
+    
+    strcpy(host, bindPath);
+    if((tp = strchr(host, ':')) != 0) {
+       *tp++ = 0;
+       if((port = atoi(tp)) == 0) {
+           *--tp = ':';
+        } else {
+           tcp = TRUE;
+        }
+    }
+    if(tcp && (*host && strcmp(host, "localhost") != 0)) {
+       fprintf(stderr, "To start a service on a TCP port can not "
+                       "specify a host name.\n"
+                       "You should either use \"localhost:<port>\" or "
+                       " just use \":<port>.\"\n");
+       exit(1);
+    }
+
+    if(tcp) {
+       listenSock = socket(AF_INET, SOCK_STREAM, 0);
+        if(listenSock == SOCKET_ERROR) {
+           return -1;
+       }
+       /*
+        * Bind the listening socket.
+        */
+       memset((char *) &sockAddr, 0, sizeof(sockAddr));
+       sockAddr.sin_family = AF_INET;
+       sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
+       sockAddr.sin_port = htons(port);
+       servLen = sizeof(sockAddr);
+
+       if(bind(listenSock, (struct sockaddr *) &sockAddr, servLen) < 0
+          || listen(listenSock, 5) < 0) {
+           perror("bind/listen");
+           exit(errno);
+       }
+
+       retFd = Win32NewDescriptor(FD_SOCKET_SYNC, (int)listenSock, -1);
+       return retFd;
+    }
+
+  
+    /*
+     * Initialize the SECURITY_ATTRIUBTES structure.
+     */
+    sa.nLength = sizeof(sa);
+    sa.lpSecurityDescriptor = NULL;
+    sa.bInheritHandle = TRUE;       /* This will be inherited by the
+                                     * FastCGI process
+                                     */
+
+    /*
+     * Create a mutex to be used to synchronize access to accepting a
+     * connection on a named pipe.  We don't want to own this at creation
+     * time but would rather let the first process that goes for it
+     * be able to acquire it.
+     */
+    hPipeMutex = CreateMutex(NULL, FALSE, NULL);
+    if(hPipeMutex == NULL) {
+        return -1;
+    }
+    if(!SetHandleInformation(hPipeMutex, HANDLE_FLAG_INHERIT,
+                                 TRUE)) {
+        return -1;
+    }
+    sprintf(pipeMutexEnv, "%s=%d", MUTEX_VARNAME, (int)hPipeMutex);
+    putenv(pipeMutexEnv);
+
+    /*
+     * Create a unique name to be used for the socket bind path.
+     * Make sure that this name is unique and that there's no process
+     * bound to it.
+     *
+     * Named Pipe Pathname: \\.\pipe\FastCGI\OM_WS.pid.N
+     * Where: N is the pipe instance on the machine.
+     *
+     */
+    bpLen = (int)strlen(bindPathPrefix);
+    bpLen += strlen(bindPath);
+    localPath = malloc(bpLen+2);
+    strcpy(localPath, bindPathPrefix);
+    strcat(localPath, bindPath);
+    
+    /*
+     * Create and setup the named pipe to be used by the fcgi server.
+     */
+    hListenPipe = CreateNamedPipe(localPath, /* name of pipe */
+                   PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+                   PIPE_TYPE_BYTE | PIPE_WAIT |
+                   PIPE_READMODE_BYTE,         /* pipe IO type */
+                   PIPE_UNLIMITED_INSTANCES,   /* number of instances */
+                   4096,                       /* size of outbuf (0 == allocate as necessary) */
+                   4096,                               /* size of inbuf */
+                   0, /*1000,*/                /* default time-out value */
+                   &sa);                       /* security attributes */
+    free(localPath);
+    /*
+     * Can't create an instance of the pipe, fail...
+     */
+    if (hListenPipe == INVALID_HANDLE_VALUE) {
+       return -1;
+    }
+
+    retFd = Win32NewDescriptor(FD_PIPE_SYNC, (int)hListenPipe, -1);
+    return retFd;
+}
+
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * OS_FcgiConnect --
+ *
+ *     Create the pipe pathname connect to the remote application if
+ *      possible.
+ *
+ * Results:
+ *      -1 if fail or a valid handle if connection succeeds.
+ *
+ * Side effects:
+ *      Remote connection established.
+ *
+ *----------------------------------------------------------------------
+ */
+int OS_FcgiConnect(char *bindPath)
+{
+    char *pipePath = NULL;
+    HANDLE hPipe;
+    int pseudoFd, err;
+
+    struct  sockaddr_in        sockAddr;
+    int servLen, resultSock;
+    int connectStatus;
+    char    *tp;
+    char    host[1024];
+    short   port;
+    int            tcp = FALSE;
+
+    strcpy(host, bindPath);
+    if((tp = strchr(host, ':')) != 0) {
+       *tp++ = 0;
+       if((port = atoi(tp)) == 0) {
+           *--tp = ':';
+        } else {
+           tcp = TRUE;
+        }
+    }
+    if(tcp == TRUE) {
+       struct  hostent *hp;
+       if((hp = gethostbyname((*host ? host : "localhost"))) == NULL) {
+           fprintf(stderr, "Unknown host: %s\n", bindPath);
+           exit(1000);
+       }
+       sockAddr.sin_family = AF_INET;
+       memcpy(&sockAddr.sin_addr, hp->h_addr, hp->h_length);
+       sockAddr.sin_port = htons(port);
+       servLen = sizeof(sockAddr);
+       resultSock = socket(AF_INET, SOCK_STREAM, 0);
+
+       assert(resultSock >= 0);
+       connectStatus = connect(resultSock, (struct sockaddr *)
+                               &sockAddr, servLen);
+       if(connectStatus < 0) {
+           /*
+            * Most likely (errno == ENOENT || errno == ECONNREFUSED)
+            * and no FCGI application server is running.
+            */
+           closesocket(resultSock);
+           return -1;
+       }
+       pseudoFd = Win32NewDescriptor(FD_SOCKET_SYNC, resultSock, -1);
+       if(pseudoFd == -1) {
+           closesocket(resultSock);
+       }
+       return pseudoFd;
+    }
+    
+    /*
+     * Not a TCP connection, create and connect to a named pipe.
+     */
+    pipePath = malloc((size_t)(strlen(bindPathPrefix) + strlen(bindPath) + 2));
+    if(pipePath == NULL) {
+        return -1;
+    }
+    strcpy(pipePath, bindPathPrefix);
+    strcat(pipePath, bindPath);
+
+    hPipe = CreateFile (pipePath, 
+                       /* Generic access, read/write. */
+                       GENERIC_WRITE | GENERIC_READ,
+                       /* Share both read and write. */
+                       FILE_SHARE_READ | FILE_SHARE_WRITE ,
+                       NULL,                  /* No security.*/
+                       OPEN_EXISTING,         /* Fail if not existing. */
+                       FILE_FLAG_OVERLAPPED,  /* Use overlap. */
+                       NULL);                 /* No template. */
+
+    free(pipePath);
+    if(hPipe == INVALID_HANDLE_VALUE) {
+        return -1;
+    }
+
+    if ((pseudoFd = Win32NewDescriptor(FD_PIPE_ASYNC, (int)hPipe, -1)) == -1) {
+        CloseHandle(hPipe);
+        return -1;
+    } else {
+        /*
+        * Set stdin equal to our pseudo FD and create the I/O completion
+        * port to be used for async I/O.
+        */
+        if (!CreateIoCompletionPort(hPipe, hIoCompPort, pseudoFd, 1)) {
+           err = GetLastError();
+           Win32FreeDescriptor(pseudoFd);
+           CloseHandle(hPipe);
+           return -1;
+       }
+    }
+    return pseudoFd;
+}
+     
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * OS_Read --
+ *
+ *     Pass through to the appropriate NT read function.
+ *
+ * Results:
+ *     Returns number of byes read. Mimics unix read:.
+ *             n bytes read, 0 or -1 failure: errno contains actual error
+ *
+ * Side effects:
+ *     None.
+ *
+ *--------------------------------------------------------------
+ */
+int OS_Read(int fd, char * buf, size_t len)
+{
+    DWORD bytesRead;
+    int ret;
+
+    /*
+     * Catch any bogus fd values
+     */
+    ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
+
+    switch (fdTable[fd].type) {
+       case FD_FILE_SYNC:
+       case FD_FILE_ASYNC:
+       case FD_PIPE_SYNC:
+       case FD_PIPE_ASYNC:
+           bytesRead = fd;
+           /*
+            * ReadFile returns: TRUE success, FALSE failure
+            */
+           if (!ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead,
+               NULL)) {
+               fdTable[fd].Errno = GetLastError();
+               return -1;
+           }
+           return bytesRead;
+
+       case FD_SOCKET_SYNC:
+       case FD_SOCKET_ASYNC:
+           /* winsock recv returns n bytes recv'ed, SOCKET_ERROR failure */
+           /*
+            * XXX: Test this with ReadFile.  If it works, remove this code
+            *      to simplify the routine.
+            */
+           if ((ret = recv(fdTable[fd].fid.sock, buf, len, 0)) ==
+               SOCKET_ERROR) {
+               fdTable[fd].Errno = WSAGetLastError();
+               return -1;
+           }
+           return ret;
+       default:
+               return -1;
+    }
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * OS_Write --
+ *
+ *     Perform a synchronous OS write.
+ *
+ * Results:
+ *     Returns number of bytes written. Mimics unix write:
+ *             n bytes written, 0 or -1 failure (??? couldn't find man page).
+ *
+ * Side effects:
+ *     none.
+ *
+ *--------------------------------------------------------------
+ */
+int OS_Write(int fd, char * buf, size_t len)
+{
+    DWORD bytesWritten;
+    int ret;
+
+    /*
+     * Catch any bogus fd values
+     */
+    ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
+    ASSERT((fdTable[fd].type > FD_UNUSED) &&
+          (fdTable[fd].type <= FD_PIPE_ASYNC));
+
+    switch (fdTable[fd].type) {
+       case FD_FILE_SYNC:
+       case FD_FILE_ASYNC:
+       case FD_PIPE_SYNC:
+       case FD_PIPE_ASYNC:
+           bytesWritten = fd;
+           /*
+            * WriteFile returns: TRUE success, FALSE failure
+            */
+           if (!WriteFile(fdTable[fd].fid.fileHandle, buf, len,
+               &bytesWritten, NULL)) {
+               fdTable[fd].Errno = GetLastError();
+               return -1;
+           }
+           return bytesWritten;
+       case FD_SOCKET_SYNC:
+       case FD_SOCKET_ASYNC:
+           /* winsock send returns n bytes written, SOCKET_ERROR failure */
+           /*
+            * XXX: Test this with WriteFile.  If it works, remove this code
+            *      to simplify the routine.
+            */
+           if ((ret = send(fdTable[fd].fid.sock, buf, len, 0)) ==
+               SOCKET_ERROR) {
+               fdTable[fd].Errno = WSAGetLastError();
+               return -1;
+           }
+           return ret;
+       default:
+           return -1;
+    }
+}
+
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * OS_SpawnChild --
+ *
+ *     Spawns a new server listener process, and stores the information
+ *      relating to the child in the supplied record.  A wait handler is
+ *     registered on the child's completion.  This involves creating
+ *        a process on NT and preparing a command line with the required
+ *        state (currently a -childproc flag and the server socket to use
+ *        for accepting connections).
+ *
+ * Results:
+ *      0 if success, -1 if error.
+ *
+ * Side effects:
+ *      Child process spawned.
+ *
+ *----------------------------------------------------------------------
+ */
+int OS_SpawnChild(char *execPath, int listenFd)
+{
+    STARTUPINFO StartupInfo;
+    PROCESS_INFORMATION pInfo;
+    BOOL success;
+
+    memset((void *)&StartupInfo, 0, sizeof(STARTUPINFO));
+    StartupInfo.cb = sizeof (STARTUPINFO);
+    StartupInfo.lpReserved = NULL;
+    StartupInfo.lpReserved2 = NULL;
+    StartupInfo.cbReserved2 = 0;
+    StartupInfo.lpDesktop = NULL;
+
+    /*
+     * FastCGI on NT will set the listener pipe HANDLE in the stdin of
+     * the new process.  The fact that there is a stdin and NULL handles
+     * for stdout and stderr tells the FastCGI process that this is a
+     * FastCGI process and not a CGI process.
+     */
+    StartupInfo.dwFlags = STARTF_USESTDHANDLES;
+    /*
+     * XXX: Do I have to dup the handle before spawning the process or is
+     *      it sufficient to use the handle as it's reference counted
+     *      by NT anyway?
+     */
+    StartupInfo.hStdInput  = fdTable[listenFd].fid.fileHandle;
+    StartupInfo.hStdOutput = INVALID_HANDLE_VALUE;
+    StartupInfo.hStdError  = INVALID_HANDLE_VALUE;
+    
+    /*
+     * Make the listener socket inheritable.
+     */
+    success = SetHandleInformation(StartupInfo.hStdInput, HANDLE_FLAG_INHERIT,
+                                  TRUE);
+    if(!success) {
+        exit(99);
+    }
+
+    /*
+     * XXX: Might want to apply some specific security attributes to the
+     *      processes.
+     */
+    success = CreateProcess(execPath,  /* LPCSTR address of module name */
+                       NULL,           /* LPCSTR address of command line */
+                       NULL,           /* Process security attributes */ 
+                       NULL,           /* Thread security attributes */
+                       TRUE,           /* Inheritable Handes inherited. */
+                       0,              /* DWORD creation flags  */
+                       NULL,           /* Use parent environment block */
+                       NULL,           /* Address of current directory name */
+                       &StartupInfo,   /* Address of STARTUPINFO  */
+                       &pInfo);        /* Address of PROCESS_INFORMATION   */
+    if(success) {
+        return 0;
+    } else {
+        return -1;
+    }
+}
+
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * OS_AsyncReadStdin --
+ *
+ *     This initiates an asynchronous read on the standard
+ *     input handle.  This handle is not guaranteed to be
+ *      capable of performing asynchronous I/O so we send a 
+ *      message to the StdinThread to do the synchronous read.
+ *
+ * Results:
+ *     -1 if error, 0 otherwise.
+ *
+ * Side effects:
+ *     Asynchronous message is queued to the StdinThread and an
+ *      overlapped structure is allocated/initialized.
+ *
+ *--------------------------------------------------------------
+ */
+int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr, 
+                      ClientData clientData)
+{
+    POVERLAPPED_REQUEST pOv;
+
+    ASSERT(fdTable[STDIN_FILENO].type != FD_UNUSED);
+
+    pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
+    ASSERT(pOv);
+    memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
+    pOv->clientData1 = (ClientData)buf;
+    pOv->instance = fdTable[STDIN_FILENO].instance;
+    pOv->procPtr = procPtr;
+    pOv->clientData = clientData;
+
+    PostQueuedCompletionStatus(hStdinCompPort, len, STDIN_FILENO,
+                               (LPOVERLAPPED)pOv);
+    return 0;
+}
+
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * OS_AsyncRead --
+ *
+ *     This initiates an asynchronous read on the file
+ *     handle which may be a socket or named pipe.
+ *
+ *     We also must save the ProcPtr and ClientData, so later
+ *     when the io completes, we know who to call.
+ *
+ *     We don't look at any results here (the ReadFile may
+ *     return data if it is cached) but do all completion
+ *     processing in OS_Select when we get the io completion
+ *     port done notifications.  Then we call the callback.
+ *
+ * Results:
+ *     -1 if error, 0 otherwise.
+ *
+ * Side effects:
+ *     Asynchronous I/O operation is queued for completion.
+ *
+ *--------------------------------------------------------------
+ */
+int OS_AsyncRead(int fd, int offset, void *buf, int len,
+                OS_AsyncProc procPtr, ClientData clientData)
+{
+    DWORD bytesRead;
+    POVERLAPPED_REQUEST pOv;
+
+    /*
+     * Catch any bogus fd values
+     */
+    ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
+    /*
+     * Confirm that this is an async fd
+     */
+    ASSERT(fdTable[fd].type != FD_UNUSED);
+    ASSERT(fdTable[fd].type != FD_FILE_SYNC);
+    ASSERT(fdTable[fd].type != FD_PIPE_SYNC);
+    ASSERT(fdTable[fd].type != FD_SOCKET_SYNC);
+
+    pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
+    ASSERT(pOv);
+    memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
+    /*
+     * Only file offsets should be non-zero, but make sure.
+     */
+    if (fdTable[fd].type == FD_FILE_ASYNC)
+       if (fdTable[fd].offset >= 0)
+           pOv->overlapped.Offset = fdTable[fd].offset;
+       else
+           pOv->overlapped.Offset = offset;
+    pOv->instance = fdTable[fd].instance;
+    pOv->procPtr = procPtr;
+    pOv->clientData = clientData;
+    bytesRead = fd;
+    /*
+     * ReadFile returns: TRUE success, FALSE failure
+     */
+    if (!ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead,
+       (LPOVERLAPPED)pOv)) {
+       fdTable[fd].Errno = GetLastError();
+       if(fdTable[fd].Errno == ERROR_NO_DATA ||
+          fdTable[fd].Errno == ERROR_PIPE_NOT_CONNECTED) {
+           PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
+           return 0;
+       }
+       if(fdTable[fd].Errno != ERROR_IO_PENDING) {
+           PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
+           return -1;
+       }
+       fdTable[fd].Errno = 0;
+    }
+    return 0;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * OS_AsyncWrite --
+ *
+ *     This initiates an asynchronous write on the "fake" file
+ *     descriptor (which may be a file, socket, or named pipe).
+ *     We also must save the ProcPtr and ClientData, so later
+ *     when the io completes, we know who to call.
+ *
+ *     We don't look at any results here (the WriteFile generally
+ *     completes immediately) but do all completion processing
+ *     in OS_DoIo when we get the io completion port done
+ *     notifications.  Then we call the callback.
+ *
+ * Results:
+ *     -1 if error, 0 otherwise.
+ *
+ * Side effects:
+ *     Asynchronous I/O operation is queued for completion.
+ *
+ *--------------------------------------------------------------
+ */
+int OS_AsyncWrite(int fd, int offset, void *buf, int len, 
+                 OS_AsyncProc procPtr, ClientData clientData)
+{
+    DWORD bytesWritten;
+    POVERLAPPED_REQUEST pOv;
+
+    /*
+     * Catch any bogus fd values
+     */
+    ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
+    /*
+     * Confirm that this is an async fd
+     */
+    ASSERT(fdTable[fd].type != FD_UNUSED);
+    ASSERT(fdTable[fd].type != FD_FILE_SYNC);
+    ASSERT(fdTable[fd].type != FD_PIPE_SYNC);
+    ASSERT(fdTable[fd].type != FD_SOCKET_SYNC);
+
+    pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
+    ASSERT(pOv);
+    memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
+    /*
+     * Only file offsets should be non-zero, but make sure.
+     */
+    if (fdTable[fd].type == FD_FILE_ASYNC)
+       /* 
+        * Only file opened via OS_AsyncWrite with
+        * O_APPEND will have an offset != -1.
+        */
+       if (fdTable[fd].offset >= 0)
+           /* 
+            * If the descriptor has a memory mapped file
+            * handle, take the offsets from there.
+            */
+           if (fdTable[fd].hMapMutex != NULL) {
+               /*
+                * Wait infinitely; this *should* not cause problems.
+                */ 
+               WaitForSingleObject(fdTable[fd].hMapMutex, INFINITE);
+               
+               /*
+                * Retrieve the shared offset values.
+                */
+               pOv->overlapped.OffsetHigh = *(fdTable[fd].offsetHighPtr);
+               pOv->overlapped.Offset = *(fdTable[fd].offsetLowPtr);
+               
+               /*
+                * Update the shared offset values for the next write
+                */
+               *(fdTable[fd].offsetHighPtr) += 0;      /* XXX How do I handle overflow */
+               *(fdTable[fd].offsetLowPtr) += len;
+               
+               ReleaseMutex(fdTable[fd].hMapMutex);
+           } else 
+               pOv->overlapped.Offset = fdTable[fd].offset;
+       else
+           pOv->overlapped.Offset = offset;
+    pOv->instance = fdTable[fd].instance;
+    pOv->procPtr = procPtr;
+    pOv->clientData = clientData;
+    bytesWritten = fd;
+    /*
+     * WriteFile returns: TRUE success, FALSE failure
+     */
+    if (!WriteFile(fdTable[fd].fid.fileHandle, buf, len, &bytesWritten,
+       (LPOVERLAPPED)pOv)) {
+       fdTable[fd].Errno = GetLastError();
+       if(fdTable[fd].Errno != ERROR_IO_PENDING) {
+           PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
+           return -1;
+       }
+       fdTable[fd].Errno = 0;
+    }
+    if (fdTable[fd].offset >= 0)
+       fdTable[fd].offset += len;
+    return 0;
+}
+
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * OS_Close --
+ *
+ *     Closes the descriptor with routine appropriate for
+ *      descriptor's type.
+ *
+ * Results:
+ *     Socket or file is closed. Return values mimic Unix close:
+ *             0 success, -1 failure
+ *
+ * Side effects:
+ *     Entry in fdTable is marked as free.
+ *
+ *--------------------------------------------------------------
+ */
+int OS_Close(int fd)
+{
+    int ret = 0;
+
+    /*
+     * Catch it if fd is a bogus value
+     */
+    ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
+    ASSERT(fdTable[fd].type != FD_UNUSED);
+
+    switch (fdTable[fd].type) {
+       case FD_PIPE_SYNC:
+       case FD_PIPE_ASYNC:
+       case FD_FILE_SYNC:
+       case FD_FILE_ASYNC:
+           /*
+            * CloseHandle returns: TRUE success, 0 failure
+            */
+           if (CloseHandle(fdTable[fd].fid.fileHandle) == FALSE)
+               ret = -1;
+           break;
+       case FD_SOCKET_SYNC:
+       case FD_SOCKET_ASYNC:
+           /*
+            * Closing a socket that has an async read outstanding causes a
+            * tcp reset and possible data loss.  The shutdown call seems to
+            * prevent this.
+            */
+           shutdown(fdTable[fd].fid.sock, 2);
+           /*
+            * closesocket returns: 0 success, SOCKET_ERROR failure
+            */
+           if (closesocket(fdTable[fd].fid.sock) == SOCKET_ERROR)
+               ret = -1;
+           break;
+       default:
+           return -1;          /* fake failure */
+    }
+
+    Win32FreeDescriptor(fd);
+    return ret;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * OS_CloseRead --
+ *
+ *     Cancel outstanding asynchronous reads and prevent subsequent
+ *      reads from completing.
+ *
+ * Results:
+ *     Socket or file is shutdown. Return values mimic Unix shutdown:
+ *             0 success, -1 failure
+ *
+ *--------------------------------------------------------------
+ */
+int OS_CloseRead(int fd)
+{
+    int ret = 0;
+
+    /*
+     * Catch it if fd is a bogus value
+     */
+    ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
+    ASSERT(fdTable[fd].type == FD_SOCKET_ASYNC
+       || fdTable[fd].type == FD_SOCKET_SYNC);
+
+    if (shutdown(fdTable[fd].fid.sock,0) == SOCKET_ERROR)
+       ret = -1;
+    return ret;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * OS_DoIo --
+ *
+ *     This function was formerly OS_Select.  It's purpose is
+ *      to pull I/O completion events off the queue and dispatch
+ *      them to the appropriate place.
+ *
+ * Results:
+ *     Returns 0.
+ *
+ * Side effects:
+ *     Handlers are called.
+ *
+ *--------------------------------------------------------------
+ */
+int OS_DoIo(struct timeval *tmo)
+{
+    int fd;
+    int bytes;
+    POVERLAPPED_REQUEST pOv;
+    struct timeb tb;
+    int ms;
+    int ms_last;
+    int err;
+    
+    /* XXX
+     * We can loop in here, but not too long, as wait handlers
+     * must run.
+     * For cgi stdin, apparently select returns when io completion
+     * ports don't, so don't wait the full timeout.
+     */
+    if(tmo)
+       ms = (tmo->tv_sec*1000 + tmo->tv_usec/1000) / 2;
+    else
+       ms = 1000;
+    ftime(&tb);
+    ms_last = tb.time*1000 + tb.millitm;
+    while (ms >= 0) {
+       if(tmo && (ms = tmo->tv_sec*1000 + tmo->tv_usec/1000)> 100)
+           ms = 100;
+       if (!GetQueuedCompletionStatus(hIoCompPort, &bytes, &fd,
+           (LPOVERLAPPED *)&pOv, ms) && !pOv) {
+           err = WSAGetLastError();
+           return 0; /* timeout */
+        }
+       
+       ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
+       /* call callback if descriptor still valid */
+       ASSERT(pOv);
+       if(pOv->instance == fdTable[fd].instance)
+         (*pOv->procPtr)(pOv->clientData, bytes);
+       free(pOv);
+
+       ftime(&tb);
+       ms -= (tb.time*1000 + tb.millitm - ms_last);
+       ms_last = tb.time*1000 + tb.millitm;
+    }
+    return 0;
+}
+
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * OS_FcgiIpcAccept --
+ *
+ *     Accepts a new FastCGI connection.  This routine knows whether
+ *      we're dealing with TCP based sockets or NT Named Pipes for IPC.
+ *
+ * Results:
+ *      -1 if the operation fails, otherwise this is a valid IPC fd.
+ *
+ * Side effects:
+ *      New IPC connection is accepted.
+ *
+ *----------------------------------------------------------------------
+ */
+int OS_FcgiIpcAccept(char *serverHostList)
+{
+    struct sockaddr_in sa;
+    int isNewConnection;
+    int ipcFd = -1;
+    BOOL pConnected;
+    HANDLE hDup;
+    SOCKET hSock;
+    int clilen = sizeof(sa);
+    DWORD waitForStatus;
+    
+    switch(listenType) {
+
+    case FD_PIPE_SYNC:
+        waitForStatus = WaitForSingleObject(hPipeMutex,INFINITE);
+        switch(waitForStatus) {
+        case WAIT_OBJECT_0:
+        case WAIT_ABANDONED:
+            break;
+
+        case WAIT_FAILED:
+        default:
+            return -1;
+        }
+
+        /*
+         * We have the mutex, go for the connection.
+         */
+       pConnected = ConnectNamedPipe(hListen, NULL) ?
+                             TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
+                                   
+        ReleaseMutex(hPipeMutex);
+           if(pConnected) {
+               /*
+                * Success...
+                */
+               if (!DuplicateHandle(GetCurrentProcess(), hListen,
+                                GetCurrentProcess(), &hDup, 0,
+                                TRUE,          /* allow inheritance */
+                                DUPLICATE_SAME_ACCESS)) {
+                   return -1;
+               }
+               ipcFd = Win32NewDescriptor(FD_PIPE_SYNC, (int)hDup, -1);
+               if(ipcFd == -1) {
+                DisconnectNamedPipe(hListen);
+                CloseHandle(hDup);
+            }
+            return ipcFd;
+        } else {
+            return -1;
+    }
+       break;
+
+    case FD_SOCKET_SYNC:
+       hSock = accept((int)hListen, (struct sockaddr *) &sa, &clilen);
+       if(hSock == -1) {
+           return -1;
+       } else if (sa.sin_family != AF_INET) { /* What are we? */
+           closesocket(hSock);
+           hSock = (SOCKET)-1;
+           return -1;
+       } else {
+           char        *tp1, *tp2;
+           int match = 0;
+           if (serverHostList == NULL) 
+               isNewConnection = TRUE;
+           else {
+               tp1 = (char *) malloc(strlen(serverHostList)+1);
+            ASSERT(tp1 != NULL);
+               strcpy(tp1, serverHostList);
+               while(tp1) {
+                   if ((tp2 = strchr(tp1, ',')) != NULL)
+                       *tp2++ = 0;
+                       
+                   if (inet_addr(tp1) == sa.sin_addr.s_addr) {
+                       match = 1;
+                       break;
+                   }
+                   tp1 = tp2;
+               }
+               free(tp1);
+               if (match)
+                   isNewConnection = TRUE;
+               else {
+                   closesocket(hSock);
+                   hSock = (SOCKET)-1;
+                   return -1;
+               }
+           }
+       }
+
+       ipcFd = Win32NewDescriptor(FD_SOCKET_SYNC, hSock, -1);
+       if(ipcFd == -1) {
+           closesocket(hSock);
+       }
+       return ipcFd;
+       break;
+
+    case FD_UNUSED:
+      default:
+        exit(101);
+       break;
+       
+    }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * OS_IpcClose
+ *
+ *     OS IPC routine to close an IPC connection.
+ *
+ * Results:
+ *
+ *
+ * Side effects:
+ *      IPC connection is closed.
+ *
+ *----------------------------------------------------------------------
+ */
+int OS_IpcClose(int ipcFd)
+{
+
+    /*
+     * Catch it if fd is a bogus value
+     */
+    ASSERT((ipcFd >= 0) && (ipcFd < WIN32_OPEN_MAX));
+    ASSERT(fdTable[ipcFd].type != FD_UNUSED);
+
+    switch(listenType) {
+
+    case FD_PIPE_SYNC:
+       /*
+        * Make sure that the client (ie. a Web Server in this case) has
+        * read all data from the pipe before we disconnect.
+        */
+       if(!FlushFileBuffers(fdTable[ipcFd].fid.fileHandle))
+           return -1;
+       if(DisconnectNamedPipe(fdTable[ipcFd].fid.fileHandle)) {
+           OS_Close(ipcFd);
+           return 0;
+       } else {
+           return -1;
+       }
+       break;
+
+    case FD_SOCKET_SYNC:
+       OS_Close(ipcFd);
+       break;
+
+    case FD_UNUSED:
+    default:
+       exit(106);
+       break;
+    }
+
+}
+
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * OS_IsFcgi --
+ *
+ *     Determines whether this process is a FastCGI process or not.
+ *
+ * Results:
+ *      Returns 1 if FastCGI, 0 if not.
+ *
+ * Side effects:
+ *      None.
+ *
+ *----------------------------------------------------------------------
+ */
+int OS_IsFcgi()
+{
+    if(listenType == FD_UNUSED) {
+        isCGI = TRUE;
+        return 0;
+    } else {
+        isCGI = FALSE;
+        return 1;
+    }
+}
+
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * OS_SetFlags --
+ *
+ *      Sets selected flag bits in an open file descriptor.  Currently
+ *      this is only to put a SOCKET into non-blocking mode.
+ *
+ *----------------------------------------------------------------------
+ */
+void OS_SetFlags(int fd, int flags)
+{
+    long int pLong = 1L;
+    int err;
+    
+    if(fdTable[fd].type == FD_SOCKET_SYNC && flags == O_NONBLOCK) {
+        if (ioctlsocket(fdTable[fd].fid.sock, FIONBIO, &pLong) ==
+           SOCKET_ERROR) {
+           exit(WSAGetLastError());
+        }
+        if (!CreateIoCompletionPort((HANDLE)fdTable[fd].fid.sock,
+                                   hIoCompPort, fd, 1)) {
+           err = GetLastError();
+           exit(err);
+       }
+
+        fdTable[fd].type = FD_SOCKET_ASYNC;
+    }
+    return;
+}
+
diff --git a/libfcgi/strerror.c b/libfcgi/strerror.c
new file mode 100644 (file)
index 0000000..ee54b36
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * The terms in the file "LICENSE.TERMS" do not apply to this file.
+ * See terms below.
+ *
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)strerror.c  5.6 (Berkeley) 5/4/91";*/
+static char *rcsid = "$Id: strerror.c,v 1.1 1997/09/16 15:36:33 stanleyg Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include "fcgi_config.h"
+
+#if ! defined (HAVE_STRERROR)
+#include <string.h>
+
+/*
+ * Since perror() is not allowed to change the contents of strerror()'s
+ * static buffer, both functions supply their own buffers to the
+ * internal function __strerror().
+ */
+
+char *
+__strerror(num, buf)
+       int num;
+       char *buf;
+{
+#define        UPREFIX "Unknown error: "
+       extern char *sys_errlist[];
+       extern int sys_nerr;
+       register unsigned int errnum;
+       register char *p, *t;
+       char tmp[40];
+
+       errnum = num;                           /* convert to unsigned */
+       if (errnum < sys_nerr)
+               return(sys_errlist[errnum]);
+
+       /* Do this by hand, so we don't include stdio(3). */
+       t = tmp;
+       do {
+               *t++ = "0123456789"[errnum % 10];
+       } while (errnum /= 10);
+
+       strcpy (buf, UPREFIX);
+       for (p = buf + sizeof(UPREFIX) -1;;) {
+               *p++ = *--t;
+               if (t <= tmp)
+                       break;
+       }
+
+       return buf;
+}
+
+
+char *
+strerror(num)
+       int num;
+{
+       static char buf[40];                    /* 64-bit number + slop */
+       return __strerror(num, buf);
+}
+
+#endif
diff --git a/tcl/common/tclAppInit.c b/tcl/common/tclAppInit.c
new file mode 100644 (file)
index 0000000..600b395
--- /dev/null
@@ -0,0 +1,109 @@
+/* 
+ * tclAppInit.c --
+ *
+ *     Provides a default version of the main program and Tcl_AppInit
+ *     procedure for Tcl applications (without Tk).
+ *
+ * Copyright (c) 1993 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#ifndef lint
+static const char rcsid[] = "$Id: tclAppInit.c,v 1.1 1997/09/16 15:36:36 stanleyg Exp $";
+#endif /* not lint */
+
+#include "tcl.h"
+
+/*
+ * The following variable is a special hack that is needed in order for
+ * Sun shared libraries to be used for Tcl.
+ */
+
+extern int matherr();
+int *tclDummyMathPtr = (int *) matherr;
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * main --
+ *
+ *     This is the main program for the application.
+ *
+ * Results:
+ *     None: Tcl_Main never returns here, so this procedure never
+ *     returns either.
+ *
+ * Side effects:
+ *     Whatever the application does.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+main(argc, argv)
+    int argc;                  /* Number of command-line arguments. */
+    char **argv;               /* Values of command-line arguments. */
+{
+    Tcl_Main(argc, argv, Tcl_AppInit);
+    return 0;                  /* Needed only to prevent compiler warning. */
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_AppInit --
+ *
+ *     This procedure performs application-specific initialization.
+ *     Most applications, especially those that incorporate additional
+ *     packages, will have their own version of this procedure.
+ *
+ * Results:
+ *     Returns a standard Tcl completion code, and leaves an error
+ *     message in interp->result if an error occurs.
+ *
+ * Side effects:
+ *     Depends on the startup script.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_AppInit(interp)
+    Tcl_Interp *interp;                /* Interpreter for application. */
+{
+    if (Tcl_Init(interp) == TCL_ERROR) {
+       return TCL_ERROR;
+    }
+
+    /*
+     * Call the init procedures for included packages.  Each call should
+     * look like this:
+     *
+     * if (Mod_Init(interp) == TCL_ERROR) {
+     *     return TCL_ERROR;
+     * }
+     *
+     * where "Mod" is the name of the module.
+     */
+    if (FCGI_Init(interp) == TCL_ERROR) {
+        return TCL_ERROR;
+    }
+
+    /*
+     * Call Tcl_CreateCommand for application-specific commands, if
+     * they weren't already created by the init procedures called above.
+     */
+
+    /*
+     * Specify a user-specific startup file to invoke if the application
+     * is run interactively.  Typically the startup file is "~/.apprc"
+     * where "app" is the name of the application.  If this line is deleted
+     * then no user-specific startup file will be run under any conditions.
+     */
+
+    tcl_RcFileName = "~/.tclshrc";
+    return TCL_OK;
+}
diff --git a/tcl/common/tclFCGI.c b/tcl/common/tclFCGI.c
new file mode 100644 (file)
index 0000000..e87fd5e
--- /dev/null
@@ -0,0 +1,163 @@
+/* 
+ * tclFCGI.c --
+ *
+ *     TCL functions needed to set up FastCGI commands
+ *
+ * Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] = "$Id: tclFCGI.c,v 1.1 1997/09/16 15:36:36 stanleyg Exp $";
+#endif /* not lint */
+
+#include <tcl.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/fcntl.h>
+#include "fcgiapp.h"
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#ifndef TRUE
+#define TRUE  (1)
+#endif
+
+extern char **environ;
+static char **requestEnviron = NULL;
+
+
+/*
+ * For each variable in the array envp, either set or unset it
+ * in the interpreter interp.
+ */
+static void DoTclEnv(Tcl_Interp *interp, char **envp, int set)
+{
+    int i;
+    char *p, *p1;
+    for (i = 0; ; i++) {
+       if ((p = envp[i]) == NULL) {
+           break;
+       }
+        p1 = strchr(p, '=');
+       *p1 = '\0';
+        if(set) {
+           Tcl_SetVar2(interp, "env", p, p1 + 1, TCL_GLOBAL_ONLY);
+        } else {
+           Tcl_UnsetVar2(interp, "env", p, TCL_GLOBAL_ONLY);
+       }
+       *p1 = '=';
+    }
+}
+
+
+/*
+ * If running as FastCGI, grab FDs 1 and 2 so the Tcl open
+ * command won't see them and think it has discovered stdout/stderr.
+ * Grab the FDs using /dev/null so that attempts to write
+ * to stdout/stderr before calling FCGI_Accept are a no-op rather
+ * than crashing Tcl.
+ */
+static void GrabFDs(void)
+{
+    if(FCGX_IsCGI()) {
+        return;
+    }
+    for (;;) {
+        int fd = open("/dev/null", O_WRONLY, 0);
+        if(fd >= 2) {
+            break;
+        }
+    }
+}
+
+
+static int FcgiAcceptCmd(
+        ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
+{
+    char **savedEnviron;
+    int acceptStatus;
+    /*
+     * Unmake Tcl variable settings for the request just completed.
+     */
+    if(requestEnviron != NULL) {
+        DoTclEnv(interp, requestEnviron, FALSE);
+        requestEnviron = NULL;
+    }
+    /*
+     * Call FCGI_Accept but preserve environ.
+     */
+    savedEnviron = environ;
+    acceptStatus = FCGI_Accept();
+    requestEnviron = environ;
+    environ = savedEnviron;
+    /*
+     * Make Tcl variable settings for the new request.
+     */
+    if(acceptStatus >= 0 && !FCGX_IsCGI()) {
+        DoTclEnv(interp, requestEnviron, TRUE);
+    } else {
+        requestEnviron = NULL;
+    }
+    sprintf(interp->result, "%d", acceptStatus);
+    return TCL_OK;
+}
+
+
+static int FcgiFinishCmd(
+        ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
+{
+    /*
+     * Unmake Tcl variable settings for the completed request.
+     */
+    if(requestEnviron != NULL) {
+        DoTclEnv(interp, requestEnviron, FALSE);
+        requestEnviron = NULL;
+    }
+    /*
+     * Call FCGI_Finish.
+     */
+    FCGI_Finish();
+    sprintf(interp->result, "%d", 0);
+    return TCL_OK;
+}
+
+
+static int FcgiSetExitStatusCmd(
+        ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
+{
+    if (argc != 2) {
+       sprintf(interp->result, "wrong # args");
+       return TCL_ERROR;
+    }
+    FCGI_SetExitStatus(atoi(argv[1]));
+    sprintf(interp->result, "%d", 0);
+    return TCL_OK;
+}
+
+
+static int FcgiStartFilterDataCmd(
+        ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
+{
+    sprintf(interp->result, "%d", FCGI_StartFilterData());
+    return TCL_OK;
+}
+
+
+int FCGI_Init(Tcl_Interp *interp) {
+    GrabFDs();
+    Tcl_CreateCommand(
+            interp, "FCGI_Accept", FcgiAcceptCmd, 0, NULL);
+    Tcl_CreateCommand(
+            interp, "FCGI_Finish", FcgiFinishCmd, 0, NULL);
+    Tcl_CreateCommand(
+            interp, "FCGI_SetExitStatus", FcgiSetExitStatusCmd, 0, NULL);
+    Tcl_CreateCommand(
+            interp, "FCGI_StartFilterData", FcgiStartFilterDataCmd, 0, NULL);
+    return TCL_OK;
+}
diff --git a/tcl/tcl7.4/Makefile.in b/tcl/tcl7.4/Makefile.in
new file mode 100644 (file)
index 0000000..9ec8c2a
--- /dev/null
@@ -0,0 +1,351 @@
+#
+# This file is a Makefile for Tcl.  If it has the name "Makefile.in"
+# then it is a template for a Makefile;  to generate the actual Makefile,
+# run "./configure", which is a configuration script generated by the
+# "autoconf" program (constructs like "@foo@" will get replaced in the
+# actual Makefile.
+#
+# @(#) Makefile.in 1.24 95/07/27 12:40:13
+
+# Current Tcl version;  used in various names.
+
+VERSION = 7.4
+
+#----------------------------------------------------------------
+# Things you can change to personalize the Makefile for your own
+# site (you can make these changes in either Makefile.in or
+# Makefile, but changes to Makefile will get lost if you re-run
+# the configuration script).
+#----------------------------------------------------------------
+
+# Default top-level directories in which to install architecture-
+# specific files (exec_prefix) and machine-independent files such
+# as scripts (prefix).  The values specified here may be overridden
+# at configure-time with the --exec-prefix and --prefix options
+# to the "configure" script.
+
+prefix =       @prefix@
+exec_prefix =  @exec_prefix@
+
+# The following definition can be set to non-null for special systems
+# like AFS with replication.  It allows the pathnames used for installation
+# to be different than those used for actually reference files at
+# run-time.  INSTALL_ROOT is prepended to $prefix and $exec_prefix
+# when installing files.
+INSTALL_ROOT =
+
+# Directory from which applications will reference the library of Tcl
+# scripts (note: you can set the TCL_LIBRARY environment variable at
+# run-time to override this value):
+TCL_LIBRARY =  $(prefix)/lib/tcl$(VERSION)
+
+# Path name to use when installing library scripts:
+SCRIPT_INSTALL_DIR =   $(INSTALL_ROOT)$(TCL_LIBRARY)
+
+# Directory in which to install the archive libtcl.a:
+LIB_INSTALL_DIR =      $(INSTALL_ROOT)$(exec_prefix)/lib
+
+# Directory in which to install the program tclsh:
+BIN_INSTALL_DIR =      $(INSTALL_ROOT)$(exec_prefix)/bin
+
+# Directory in which to install the include file tcl.h:
+INCLUDE_INSTALL_DIR =  $(INSTALL_ROOT)$(prefix)/include
+
+# Top-level directory in which to install manual entries:
+MAN_INSTALL_DIR =      $(INSTALL_ROOT)$(prefix)/man
+
+# Directory in which to install manual entry for tclsh:
+MAN1_INSTALL_DIR =     $(MAN_INSTALL_DIR)/man1
+
+# Directory in which to install manual entries for Tcl's C library
+# procedures:
+MAN3_INSTALL_DIR =     $(MAN_INSTALL_DIR)/man3
+
+# Directory in which to install manual entries for the built-in
+# Tcl commands:
+MANN_INSTALL_DIR =     $(MAN_INSTALL_DIR)/mann
+
+# Additional libraries to use when linking.  The "LIBS" part will be
+# replaced (or has already been replaced) with relevant libraries as
+# determined by the configure script.
+LIBS = ../fcgi-devel-kit/libfcgi/libfcgi.a @LIBS@
+
+# To change the compiler switches, for example to change from -O
+# to -g, change the following line:
+CFLAGS = -g -I../fcgi-devel-kit/include -include ../fcgi-devel-kit/include/fcgi_stdio.h
+
+# To disable ANSI-C procedure prototypes reverse the comment characters
+# on the following lines:
+PROTO_FLAGS =
+#PROTO_FLAGS = -DNO_PROTOTYPE
+
+# Mathematical functions like sin and atan2 are enabled for expressions
+# by default.  To disable them, reverse the comment characters on the
+# following pairs of lines:
+MATH_FLAGS =
+#MATH_FLAGS = -DTCL_NO_MATH
+MATH_LIBS = @MATH_LIBS@
+#MATH_LIBS =
+
+# To compile for non-UNIX systems (so that only the non-UNIX-specific
+# commands are available), reverse the comment characters on the
+# following pairs of lines.  In addition, you'll have to provide your
+# own replacement for the "panic" procedure (see panic.c for what
+# the current one does).
+GENERIC_FLAGS =
+#GENERIC_FLAGS = -DTCL_GENERIC_ONLY
+UNIX_OBJS = panic.o tclEnv.o tclGlob.o tclMain.o tclMtherr.o \
+       tclUnixAZ.o tclUnixStr.o tclUnixUtil.o
+#UNIX_OBJS =
+
+# To enable memory debugging reverse the comment characters on the following
+# lines.  Warning:  if you enable memory debugging, you must do it
+# *everywhere*, including all the code that calls Tcl, and you must use
+# ckalloc and ckfree everywhere instead of malloc and free.
+MEM_DEBUG_FLAGS =
+#MEM_DEBUG_FLAGS = -DTCL_MEM_DEBUG
+
+# Some versions of make, like SGI's, use the following variable to
+# determine which shell to use for executing commands:
+SHELL =                /bin/sh
+
+# Tcl used to let the configure script choose which program to use
+# for installing, but there are just too many different versions of
+# "install" around;  better to use the install-sh script that comes
+# with the distribution, which is slower but guaranteed to work.
+
+INSTALL = @srcdir@/install-sh -c
+
+#----------------------------------------------------------------
+# The information below is modified by the configure script when
+# Makefile is generated from Makefile.in.  You shouldn't normally
+# modify any of this stuff by hand.
+#----------------------------------------------------------------
+
+COMPAT_OBJS =          @LIBOBJS@
+AC_FLAGS =             @DEFS@
+INSTALL_PROGRAM =      @INSTALL_PROGRAM@
+INSTALL_DATA =         @INSTALL_DATA@
+RANLIB =               @RANLIB@
+SRC_DIR =              @srcdir@
+VPATH =                        @srcdir@
+
+#----------------------------------------------------------------
+# The information below should be usable as is.  The configure
+# script won't modify it and you shouldn't need to modify it
+# either.
+#----------------------------------------------------------------
+
+
+CC =           @CC@
+CC_SWITCHES =  ${CFLAGS} -I. -I${SRC_DIR} ${AC_FLAGS} ${MATH_FLAGS} \
+${GENERIC_FLAGS} ${PROTO_FLAGS} ${MEM_DEBUG_FLAGS} \
+-DTCL_LIBRARY=\"${TCL_LIBRARY}\"
+
+GENERIC_OBJS = regexp.o tclAsync.o tclBasic.o tclCkalloc.o \
+       tclCmdAH.o tclCmdIL.o tclCmdMZ.o tclExpr.o tclGet.o \
+       tclHash.o tclHistory.o tclLink.o tclParse.o tclProc.o \
+       tclUtil.o tclVar.o tclFCGI.o
+
+OBJS = ${GENERIC_OBJS} ${UNIX_OBJS} ${COMPAT_OBJS}
+
+SRCS= regexp.c tclAsync.c tclBasic.c tclCkalloc.c \
+       tclCmdAH.c tclCmdIL.c tclCmdMZ.c tclExpr.c tclGet.c \
+       tclHash.c tclHistory.c tclLink.c tclParse.c tclProc.c \
+       tclUtil.c tclVar.c panic.c tclEnv.c tclGlob.c tclMain.c \
+       tclMtherr.c tclUnixAZ.c tclUnixStr.c tclUnixUtil.c \
+       tclTest.c tclAppInit.c tclFCGI.c
+
+all: libtcl.a tclsh
+
+libtcl.a: ${OBJS}
+       rm -f libtcl.a
+       ar cr libtcl.a ${OBJS}
+       $(RANLIB) libtcl.a
+
+tclsh: tclAppInit.o libtcl.a
+       ${CC} ${CC_SWITCHES} tclAppInit.o libtcl.a ${LIBS} ${MATH_LIBS} -o tclsh
+
+tcltest: tclTest.o libtcl.a
+       ${CC} ${CC_SWITCHES} tclTest.o libtcl.a ${LIBS} ${MATH_LIBS} -o tcltest
+
+test: tcltest
+       @cwd=`pwd`; \
+       cd $(SRC_DIR); TCL_LIBRARY=`pwd`/library; export TCL_LIBRARY; \
+       cd $$cwd; ( echo cd $(SRC_DIR)/tests\; source all ) | ./tcltest
+
+configInfo: Makefile
+       @rm -f configInfo
+       @echo "# Definitions and libraries needed to build Tcl applications" >> configInfo
+       @echo "# (generated by the configure script):" >> configInfo
+       @echo "TCL_CC_SWITCHES = ${AC_FLAGS} ${MEM_DEBUG_FLAGS}" >> configInfo
+       @echo "TCL_LIBS = ${LIBS} ${MATH_LIBS}" >> configInfo
+
+install: install-binaries install-libraries install-man
+
+install-binaries: libtcl.a tclsh
+       @for i in $(LIB_INSTALL_DIR) $(BIN_INSTALL_DIR) ; \
+           do \
+           if [ ! -d $$i ] ; then \
+               echo "Making directory $$i"; \
+               mkdir $$i; \
+               chmod 755 $$i; \
+               else true; \
+               fi; \
+           done;
+       @echo "Installing libtcl.a"
+       @$(INSTALL_DATA) libtcl.a $(LIB_INSTALL_DIR)/libtcl$(VERSION).a
+       @$(RANLIB) $(LIB_INSTALL_DIR)/libtcl$(VERSION).a
+       @echo "Installing tclsh"
+       @$(INSTALL_PROGRAM) tclsh $(BIN_INSTALL_DIR)/tclsh$(VERSION)
+
+install-libraries:
+       @for i in $(INSTALL_ROOT)$(prefix)/lib $(INCLUDE_INSTALL_DIR) \
+               $(SCRIPT_INSTALL_DIR) ; \
+           do \
+           if [ ! -d $$i ] ; then \
+               echo "Making directory $$i"; \
+               mkdir $$i; \
+               chmod 755 $$i; \
+               else true; \
+               fi; \
+           done;
+       @echo "Installing tcl.h"
+       @$(INSTALL_DATA) $(SRC_DIR)/tcl.h $(INCLUDE_INSTALL_DIR)
+       @for i in $(SRC_DIR)/library/*.tcl $(SRC_DIR)/library/tclIndex $(SRC_DIR)/tclAppInit.c; \
+           do \
+           echo "Installing $$i"; \
+           $(INSTALL_DATA) $$i $(SCRIPT_INSTALL_DIR); \
+           done;
+
+install-man:
+       @for i in $(MAN_INSTALL_DIR) $(MAN1_INSTALL_DIR) $(MAN3_INSTALL_DIR) $(MANN_INSTALL_DIR) ; \
+           do \
+           if [ ! -d $$i ] ; then \
+               echo "Making directory $$i"; \
+               mkdir $$i; \
+               chmod 755 $$i; \
+               else true; \
+               fi; \
+           done;
+       @cd $(SRC_DIR)/doc; for i in *.1; \
+           do \
+           echo "Installing doc/$$i"; \
+           rm -f $(MAN1_INSTALL_DIR)/$$i; \
+           sed -e '/man\.macros/r man.macros' -e '/man\.macros/d' \
+                   $$i > $(MAN1_INSTALL_DIR)/$$i; \
+           chmod 444 $(MAN1_INSTALL_DIR)/$$i; \
+           done;
+       @cd $(SRC_DIR)/doc; for i in *.3; \
+           do \
+           echo "Installing doc/$$i"; \
+           rm -f $(MAN3_INSTALL_DIR)/$$i; \
+           sed -e '/man\.macros/r man.macros' -e '/man\.macros/d' \
+                   $$i > $(MAN3_INSTALL_DIR)/$$i; \
+           chmod 444 $(MAN3_INSTALL_DIR)/$$i; \
+           done;
+       @cd $(SRC_DIR)/doc; for i in *.n; \
+           do \
+           echo "Installing doc/$$i"; \
+           rm -f $(MANN_INSTALL_DIR)/$$i; \
+           sed -e '/man\.macros/r man.macros' -e '/man\.macros/d' \
+                   $$i > $(MANN_INSTALL_DIR)/$$i; \
+           chmod 444 $(MANN_INSTALL_DIR)/$$i; \
+           done;
+
+Makefile: $(SRC_DIR)/Makefile.in
+       $(SHELL) config.status
+
+clean:
+       rm -f *.a *.o core errs *~ \#* TAGS *.E a.out errors tclsh tcltest \
+               config.info
+
+distclean: clean
+       rm -f Makefile config.status config.cache
+
+depend:
+       makedepend -- $(CC_SWITCHES) -- $(SRCS)
+
+fixstrtod.o: $(SRC_DIR)/compat/fixstrtod.c
+       $(CC) -c $(CC_SWITCHES) $(SRC_DIR)/compat/fixstrtod.c
+
+getcwd.o: $(SRC_DIR)/compat/getcwd.c
+       $(CC) -c $(CC_SWITCHES) $(SRC_DIR)/compat/getcwd.c
+
+opendir.o: $(SRC_DIR)/compat/opendir.c
+       $(CC) -c $(CC_SWITCHES) $(SRC_DIR)/compat/opendir.c
+
+strerror.o: $(SRC_DIR)/compat/strerror.c
+       $(CC) -c $(CC_SWITCHES) $(SRC_DIR)/compat/strerror.c
+
+strncasecmp.o: $(SRC_DIR)/compat/strncasecmp.c
+       $(CC) -c $(CC_SWITCHES) $(SRC_DIR)/compat/strncasecmp.c
+
+strstr.o: $(SRC_DIR)/compat/strstr.c
+       $(CC) -c $(CC_SWITCHES) $(SRC_DIR)/compat/strstr.c
+
+strtod.o: $(SRC_DIR)/compat/strtod.c
+       $(CC) -c $(CC_SWITCHES) $(SRC_DIR)/compat/strtod.c
+
+strtol.o: $(SRC_DIR)/compat/strtol.c
+       $(CC) -c $(CC_SWITCHES) $(SRC_DIR)/compat/strtol.c
+
+strtoul.o: $(SRC_DIR)/compat/strtoul.c
+       $(CC) -c $(CC_SWITCHES) $(SRC_DIR)/compat/strtoul.c
+
+tmpnam.o: $(SRC_DIR)/compat/tmpnam.c
+       $(CC) -c $(CC_SWITCHES) $(SRC_DIR)/compat/tmpnam.c
+
+waitpid.o: $(SRC_DIR)/compat/waitpid.c
+       $(CC) -c $(CC_SWITCHES) $(SRC_DIR)/compat/waitpid.c
+
+.c.o:
+       $(CC) -c $(CC_SWITCHES) $<
+
+#
+# Target to check for proper usage of UCHAR macro.
+#
+
+checkuchar:
+       -egrep isalnum\|isalpha\|iscntrl\|isdigit\|islower\|isprint\|ispunct\|isspace\|isupper\|isxdigit $(SRCS) | grep -v UCHAR
+
+#
+# Target to make sure that only symbols with "Tcl" prefixes are
+# exported.
+#
+
+checkexports: libtcl.a
+       -nm -p libtcl.a | awk '$$2 ~ /[TDB]/ { print $$3 }' | sort -n | grep -v '^[Tt]cl'
+
+#
+# Target to create a proper Tcl distribution from information in the
+# master source directory.  DISTDIR must be defined to indicate where
+# to put the distribution.
+#
+
+configure: configure.in
+       autoconf
+dist: configure
+       rm -rf $(DISTDIR)
+       mkdir $(DISTDIR)
+       cp Makefile.in $(DISTDIR)
+       chmod 664 $(DISTDIR)/Makefile.in
+       cp -p $(SRCS) $(DISTDIR)
+       cp -p tclRegexp.h tcl.h tclInt.h tclPort.h patchlevel.h $(DISTDIR)
+       cp configure configure.in $(DISTDIR)
+       chmod 775 $(DISTDIR)/configure $(DISTDIR)/configure.in
+       cp -p changes README porting.notes porting.old license.terms \
+               install-sh $(DISTDIR)
+       chmod +x $(DISTDIR)/install-sh
+       mkdir $(DISTDIR)/library
+       cp -p license.terms library/*.tcl library/tclIndex $(DISTDIR)/library
+       mkdir $(DISTDIR)/doc
+       cp -p license.terms doc/*.[13n] doc/man.macros $(DISTDIR)/doc
+       mkdir $(DISTDIR)/compat
+       cp -p license.terms compat/*.c compat/*.h compat/README \
+               $(DISTDIR)/compat
+       mkdir $(DISTDIR)/tests
+       cp -p license.terms $(DISTDIR)/tests
+       cp -p tests/*.test tests/README tests/all tests/defs $(DISTDIR)/tests
+
+# DO NOT DELETE THIS LINE -- make depend depends on it.
diff --git a/tcl/tcl7.4/configure.in b/tcl/tcl7.4/configure.in
new file mode 100755 (executable)
index 0000000..e497266
--- /dev/null
@@ -0,0 +1,340 @@
+dnl    This file is an input file used by the GNU "autoconf" program to
+dnl    generate the file "configure", which is run during Tcl installation
+dnl    to configure the system for the local environment.
+AC_INIT(tcl.h)
+# @(#) configure.in 1.15 95/06/27 21:53:14
+
+AC_PROG_INSTALL
+AC_PROG_RANLIB
+AC_PREFIX_PROGRAM(tclsh)
+AC_PROG_CC
+AC_C_CROSS
+AC_SUBST(CC)
+
+#--------------------------------------------------------------------
+#      On SVR4 systems, force linking with the socket libs
+#--------------------------------------------------------------------
+AC_CHECK_LIB(socket, main, [LIBS="$LIBS -lsocket"])
+AC_CHECK_LIB(nsl, main, [LIBS="$LIBS -lnsl"])
+AC_SUBST(LIBS)
+
+#--------------------------------------------------------------------
+#      Supply substitutes for missing POSIX library procedures, or
+#      set flags so Tcl uses alternate procedures.
+#--------------------------------------------------------------------
+
+AC_REPLACE_FUNCS(getcwd opendir strerror strstr)
+AC_REPLACE_FUNCS(strtol tmpnam waitpid)
+AC_CHECK_FUNC(getwd, , AC_DEFINE(NO_GETWD))
+AC_CHECK_FUNC(wait3, , AC_DEFINE(NO_WAIT3))
+
+#--------------------------------------------------------------------
+#      On a few very rare systems, all of the libm.a stuff is
+#      already in libc.a.  Set compiler flags accordingly.
+#      Also, Linux requires the "ieee" library for math to work
+#      right (and it must appear before "-lm").
+#--------------------------------------------------------------------
+
+AC_CHECK_FUNC(sin, MATH_LIBS="", MATH_LIBS="-lm")
+AC_SUBST(MATH_LIBS)
+AC_CHECK_LIB(ieee, main, [MATH_LIBS="-lieee $MATH_LIBS"])
+
+#--------------------------------------------------------------------
+#      Supply substitutes for missing POSIX header files.  Special
+#      notes:
+#          - Sprite's dirent.h exists but is bogus.
+#          - stdlib.h doesn't define strtol, strtoul, or
+#            strtod insome versions of SunOS
+#          - some versions of string.h don't declare procedures such
+#            as strstr
+#--------------------------------------------------------------------
+
+AC_HAVE_HEADERS(unistd.h)
+AC_MSG_CHECKING(dirent.h)
+AC_TRY_LINK([#include <sys/types.h>
+#include <dirent.h>], [
+#ifndef _POSIX_SOURCE
+#   ifdef __Lynx__
+       /*
+        * Generate compilation error to make the test fail:  Lynx headers
+        * are only valid if really in the POSIX environment.
+        */
+
+       missing_procedure();
+#   endif
+#endif
+DIR *d;
+struct dirent *entryPtr;
+char *p;
+d = opendir("foobar");
+entryPtr = readdir(d);
+p = entryPtr->d_name;
+closedir(d);
+], tcl_ok=yes, tcl_ok=no)
+AC_EGREP_HEADER([Sprite version.* NOT POSIX], dirent.h, tcl_ok=no)
+if test $tcl_ok = no; then
+    AC_DEFINE(NO_DIRENT_H)
+fi
+AC_MSG_RESULT($tcl_ok)
+AC_CHECK_HEADER(errno.h, , AC_DEFINE(NO_ERRNO_H))
+AC_CHECK_HEADER(float.h, , AC_DEFINE(NO_FLOAT_H))
+AC_CHECK_HEADER(limits.h, , AC_DEFINE(NO_LIMITS_H))
+AC_CHECK_HEADER(stdlib.h, tcl_ok=1, tcl_ok=0)
+AC_EGREP_HEADER(strtol, stdlib.h, , tcl_ok=0)
+AC_EGREP_HEADER(strtoul, stdlib.h, , tcl_ok=0)
+AC_EGREP_HEADER(strtod, stdlib.h, , tcl_ok=0)
+if test $tcl_ok = 0; then
+    AC_DEFINE(NO_STDLIB_H)
+fi
+AC_CHECK_HEADER(string.h, tcl_ok=1, tcl_ok=0)
+AC_EGREP_HEADER(strstr, string.h, , tcl_ok=0)
+AC_EGREP_HEADER(strerror, string.h, , tcl_ok=0)
+if test $tcl_ok = 0; then
+    AC_DEFINE(NO_STRING_H)
+fi
+AC_CHECK_HEADER(sys/time.h, , AC_DEFINE(NO_SYS_TIME_H))
+AC_CHECK_HEADER(sys/wait.h, , AC_DEFINE(NO_SYS_WAIT_H))
+
+#--------------------------------------------------------------------
+#      On some systems strstr is broken: it returns a pointer even
+#      even if the original string is empty.
+#--------------------------------------------------------------------
+
+AC_MSG_CHECKING([proper strstr implementation])
+AC_TRY_RUN([
+extern int strstr();
+int main()
+{
+    exit(strstr("\0test", "test") ? 1 : 0);
+}
+], tcl_ok=yes, tcl_ok=no)
+if test $tcl_ok = yes; then
+    AC_MSG_RESULT(yes)
+else
+    AC_MSG_RESULT([broken, using substitute])
+    LIBOBJS="$LIBOBJS strstr.o"
+fi
+
+#--------------------------------------------------------------------
+#      Check for strtoul function.  This is tricky because under some
+#      versions of AIX strtoul returns an incorrect terminator
+#      pointer for the string "0".
+#--------------------------------------------------------------------
+
+AC_CHECK_FUNC(strtoul, tcl_ok=1, tcl_ok=0)
+AC_TRY_RUN([
+extern int strtoul();
+int main()
+{
+    char *string = "0";
+    char *term;
+    int value;
+    value = strtoul(string, &term, 0);
+    if ((value != 0) || (term != (string+1))) {
+        exit(1);
+    }
+    exit(0);
+}], , tcl_ok=0)
+if test $tcl_ok = 0; then
+    test -n "$verbose" && echo "       Adding strtoul.o."
+    LIBOBJS="$LIBOBJS strtoul.o"
+fi
+
+#--------------------------------------------------------------------
+#      Check for the strtod function.  This is tricky because in some
+#      versions of Linux strtod mis-parses strings starting with "+".
+#--------------------------------------------------------------------
+
+AC_CHECK_FUNC(strtod, tcl_ok=1, tcl_ok=0)
+AC_TRY_RUN([
+extern double strtod();
+int main()
+{
+    char *string = " +69";
+    char *term;
+    double value;
+    value = strtod(string, &term);
+    if ((value != 69) || (term != (string+4))) {
+       exit(1);
+    }
+    exit(0);
+}], , tcl_ok=0)
+if test $tcl_ok = 0; then
+    test -n "$verbose" && echo "       Adding strtod.o."
+    LIBOBJS="$LIBOBJS strtod.o"
+fi
+
+#--------------------------------------------------------------------
+#      Under Solaris 2.4, strtod returns the wrong value for the
+#      terminating character under some conditions.  Check for this
+#      and if the problem exists use a substitute procedure
+#      "fixstrtod" that corrects the error.
+#--------------------------------------------------------------------
+
+AC_CHECK_FUNC(strtod, tcl_strtod=1, tcl_strtod=0)
+if test "$tcl_strtod" = 1; then
+    AC_MSG_CHECKING([for Solaris strtod bug])
+    AC_TRY_RUN([
+       extern double strtod();
+       int main()
+       {
+           char *string = "NaN";
+           char *term;
+           strtod(string, &term);
+           if ((term != string) && (term[-1] == 0)) {
+               exit(1);
+           }
+           exit(0);
+       }], AC_MSG_RESULT(ok), [
+           AC_MSG_RESULT(buggy)
+           LIBOBJS="$LIBOBJS fixstrtod.o"
+           AC_DEFINE(strtod, fixstrtod)
+       ])
+fi
+
+#--------------------------------------------------------------------
+#      Check for various typedefs and provide substitutes if
+#      they don't exist.
+#--------------------------------------------------------------------
+
+AC_TYPE_MODE_T
+AC_TYPE_PID_T
+AC_TYPE_SIZE_T
+AC_TYPE_UID_T
+
+#--------------------------------------------------------------------
+#      If a system doesn't have an opendir function (man, that's old!)
+#      then we have to supply a different version of dirent.h which
+#      is compatible with the substitute version of opendir that's
+#      provided.  This version only works with V7-style directories.
+#--------------------------------------------------------------------
+
+AC_CHECK_FUNC(opendir, , AC_DEFINE(USE_DIRENT2_H))
+
+#--------------------------------------------------------------------
+#      Check for the existence of sys_errlist (this is only needed if
+#      there's no strerror, but I don't know how to conditionalize the
+#      check).
+#--------------------------------------------------------------------
+
+AC_MSG_CHECKING(sys_errlist)
+AC_TRY_LINK(, [
+extern char *sys_errlist[];
+extern int sys_nerr;
+sys_errlist[sys_nerr-1][0] = 0;
+], tcl_ok=yes, tcl_ok=no)
+AC_MSG_RESULT($tcl_ok)
+if test $tcl_ok = no; then
+    AC_DEFINE(NO_SYS_ERRLIST)
+fi
+
+#--------------------------------------------------------------------
+#      The check below checks whether <sys/wait.h> defines the type
+#      "union wait" correctly.  It's needed because of weirdness in
+#      HP-UX where "union wait" is defined in both the BSD and SYS-V
+#      environments.  Checking the usability of WIFEXITED seems to do
+#      the trick.
+#--------------------------------------------------------------------
+
+AC_MSG_CHECKING([union wait])
+AC_TRY_LINK([#include <sys/types.h> 
+#include <sys/wait.h>], [
+union wait x;
+WIFEXITED(x);          /* Generates compiler error if WIFEXITED
+                        * uses an int. */
+], tcl_ok=yes, tcl_ok=no)
+AC_MSG_RESULT($tcl_ok)
+if test $tcl_ok = no; then
+    AC_DEFINE(NO_UNION_WAIT)
+fi
+
+#--------------------------------------------------------------------
+#      Check to see whether the system supports the matherr function
+#      and its associated type "struct exception".
+#--------------------------------------------------------------------
+
+AC_MSG_CHECKING([matherr support])
+AC_TRY_COMPILE([#include <math.h>], [
+struct exception x;
+x.type = DOMAIN;
+x.type = SING;
+], tcl_ok=yes, tcl_ok=no)
+AC_MSG_RESULT($tcl_ok)
+if test $tcl_ok = yes; then
+    AC_DEFINE(NEED_MATHERR)
+fi
+
+#--------------------------------------------------------------------
+#      Check to see whether the system provides a vfork kernel call.
+#      If not, then use fork instead.  Also, check for a problem with
+#      Solaris 2.4 and vforks and signals that can core dumps can occur
+#      if a vforked child resets a signal handler.  If the problem
+#      exists, then use fork instead of vfork.
+#--------------------------------------------------------------------
+
+AC_CHECK_FUNC(vfork, tcl_ok=1, tcl_ok=0)
+if test "$tcl_ok" = 1; then
+    AC_MSG_CHECKING([Solaris 2.4 vfork/signal bug]);
+    AC_TRY_RUN([
+       #include <stdio.h>
+       #include <signal.h>
+       #include <sys/wait.h>
+       int gotSignal = 0;
+       sigProc(sig)
+           int sig;
+       {
+           gotSignal = 1;
+       }
+       main()
+       {
+           int pid, sts;
+           (void) signal(SIGCHLD, sigProc);
+           pid = vfork();
+           if (pid <  0) {
+               exit(1);
+           } else if (pid == 0) {
+               (void) signal(SIGCHLD, SIG_DFL);
+               _exit(0);
+           } else {
+               (void) wait(&sts);
+           }
+           exit((gotSignal) ? 0 : 1);
+       }], AC_MSG_RESULT(ok), [
+           AC_MSG_RESULT(buggy)
+           tcl_ok=0
+       ])
+fi
+rm -f core
+if test "$tcl_ok" = 0; then
+    AC_DEFINE(vfork, fork)
+fi
+
+#--------------------------------------------------------------------
+#      Check whether there is an strncasecmp function on this system.
+#      This is a bit tricky because under SCO it's in the socket
+#      library.
+#--------------------------------------------------------------------
+
+AC_CHECK_FUNC(strncasecmp, ,
+    AC_CHECK_LIB(socket, strncasecmp, , [LIBOBJS="$LIBOBJS strncasecmp.o"]))
+
+#--------------------------------------------------------------------
+#      The code below deals with several issues related to gettimeofday:
+#      1. Some systems don't provide a gettimeofday function at all
+#         (set NO_GETTOD if this is the case).
+#      2. SGI systems don't use the BSD form of the gettimeofday function,
+#         but they have a BSDgettimeofday function that can be used instead.
+#      3. See if gettimeofday is declared in the <sys/time.h> header file.
+#         if not, set the GETTOD_NOT_DECLARED flag so that tclPort.h can
+#         declare it.
+#--------------------------------------------------------------------
+
+AC_CHECK_FUNC(BSDgettimeofday, AC_DEFINE(HAVE_BSDGETTIMEOFDAY),
+       AC_CHECK_FUNC(gettimeofday, , AC_DEFINE(NO_GETTOD)))
+AC_MSG_CHECKING([for gettimeofday declaration])
+AC_EGREP_HEADER(gettimeofday, sys/time.h, AC_MSG_RESULT(present), [
+    AC_MSG_RESULT(missing)
+    AC_DEFINE(GETTOD_NOT_DECLARED)
+])
+
+AC_OUTPUT(Makefile)
diff --git a/version.conf b/version.conf
new file mode 100644 (file)
index 0000000..3984667
--- /dev/null
@@ -0,0 +1,11 @@
+#
+# version.conf for FastCGI. 
+#
+# This file is machine generated.
+# Do not edit this file.
+#
+# generated on Mon Mar 31 13:57:50 EST 1997
+#
+set Release T2.0.a.NT
+set Platform osf_32
+set ProductName FastCGI
diff --git a/version.in b/version.in
new file mode 100644 (file)
index 0000000..114f501
--- /dev/null
@@ -0,0 +1,7 @@
+dnl OMI_VERSION(Product,
+dnl             VersionType(VTX),
+dnl             MajorNumber,
+dnl             MinorNumber,
+dnl            PatchLevel(build#),
+dnl            Release(alpha,beta,candidate))
+OMI_VERSION(FastCGI,T, 2, 0, a,NT)