Commits

Vlad Seryakov committed ad7a619

initial import

  • Participants

Comments (0)

Files changed (13)

+
+$Header: /cvsroot/naviserver/modules/nsocaml/LICENSE,v 1.3 2007/03/10 18:11:32 seryakov Exp $
+
+
+MOZILLA PUBLIC LICENSE
+Version 1.1
+
+1. Definitions. 
+
+1.0.1. "Commercial Use" means distribution or otherwise making the
+Covered Code available to a third party.
+
+1.1. "Contributor" means each entity that creates or contributes to
+the creation of Modifications.
+
+1.2. "Contributor Version" means the combination of the Original
+Code, prior Modifications used by a Contributor, and the Modifications
+made by that particular Contributor.
+
+1.3. "Covered Code" means the Original Code or Modifications or the
+combination of the Original Code and Modifications, in each case
+including portions thereof.
+
+1.4. "Electronic Distribution Mechanism" means a mechanism generally
+accepted in the software development community for the electronic
+transfer of data.
+
+1.5. "Executable" means Covered Code in any form other than Source Code. 
+
+1.6. "Initial Developer" means the individual or entity identified
+as the Initial Developer in the Source Code notice required by Exhibit
+A.
+
+1.7. "Larger Work" means a work which combines Covered Code or
+portions thereof with code not governed by the terms of this License.
+
+1.8. "License" means this document. 
+
+1.8.1. "Licensable" means having the right to grant, to the maximum
+extent possible, whether at the time of the initial grant or
+subsequently acquired, any and all of the rights conveyed herein.
+
+1.9. "Modifications" means any addition to or deletion from the
+substance or structure of either the Original Code or any previous
+Modifications. When Covered Code is released as a series of files, a
+Modification is:
+
+A.  Any addition to or deletion from the contents of a file containing
+Original Code or previous Modifications.
+
+B.  Any new file that contains any part of the Original Code or
+previous Modifications. 
+ 
+1.10. "Original Code" means Source Code of computer software code
+which is described in the Source Code notice required by Exhibit A as
+Original Code, and which, at the time of its release under this
+License is not already Covered Code governed by this License.
+
+1.10.1. "Patent Claims" means any patent claim(s), now owned or
+hereafter acquired, including without limitation, method, process, and
+apparatus claims, in any patent Licensable by grantor.
+
+1.11. "Source Code" means the preferred form of the Covered Code for
+making modifications to it, including all modules it contains, plus
+any associated interface definition files, scripts used to control
+compilation and installation of an Executable, or source code
+differential comparisons against either the Original Code or another
+well known, available Covered Code of the Contributor's choice. The
+Source Code can be in a compressed or archival form, provided the
+appropriate decompression or de-archiving software is widely available
+for no charge.
+
+1.12. "You" (or "Your") means an individual or a legal entity
+exercising rights under, and complying with all of the terms of, this
+License or a future version of this License issued under Section
+6.1. For legal entities, "You" includes any entity which controls, is
+controlled by, or is under common control with You. For purposes of
+this definition, "control" means (a) the power, direct or indirect,
+to cause the direction or management of such entity, whether by
+contract or otherwise, or (b) ownership of more than fifty percent
+(50%) of the outstanding shares or beneficial ownership of such
+entity.
+
+2. Source Code License. 
+
+2.1. The Initial Developer Grant.
+The Initial Developer hereby grants You a world-wide, royalty-free,
+non-exclusive license, subject to third party intellectual property
+claims:
+
+(a) under intellectual property rights (other than patent or
+trademark) Licensable by Initial Developer to use, reproduce, modify,
+display, perform, sublicense and distribute the Original Code (or
+portions thereof) with or without Modifications, and/or as part of a
+Larger Work; and
+
+(b) under Patents Claims infringed by the making, using or selling of
+Original Code, to make, have made, use, practice, sell, and offer for
+sale, and/or otherwise dispose of the Original Code (or portions
+thereof).
+
+(c) the licenses granted in this Section 2.1(a) and (b) are effective
+on the date Initial Developer first distributes Original Code under
+the terms of this License.
+
+(d) Notwithstanding Section 2.1(b) above, no patent license is
+granted: 1) for code that You delete from the Original Code; 2)
+separate from the Original Code; or 3) for infringements caused by: i)
+the modification of the Original Code or ii) the combination of the
+Original Code with other software or devices.
+ 
+2.2. Contributor Grant.
+Subject to third party intellectual property claims, each Contributor
+hereby grants You a world- wide, royalty-free, non-exclusive license
+  
+(a) under intellectual property rights (other than patent or
+trademark) Licensable by Contributor, to use, reproduce, modify,
+display, perform, sublicense and distribute the Modifications created
+by such Contributor (or portions thereof) either on an unmodified
+basis, with other Modifications, as Covered Code and/or as part of a
+Larger Work; and 
+
+(b) under Patent Claims infringed by the making, using, or selling of
+Modifications made by that Contributor either alone and/or in
+combination with its Contributor Version (or portions of such
+combination), to make, use, sell, offer for sale, have made, and/or
+otherwise dispose of: 1) Modifications made by that Contributor (or
+portions thereof); and 2) the combination of Modifications made by
+that Contributor with its Contributor Version (or portions of such
+combination).
+
+(c) the licenses granted in Sections 2.2(a) and 2.2(b) are effective
+on the date Contributor first makes Commercial Use of the Covered
+Code.
+
+(d) Notwithstanding Section 2.2(b) above, no patent license is
+granted: 1) for any code that Contributor has deleted from the
+Contributor Version; 2) separate from the Contributor Version; 3) for
+infringements caused by: i) third party modifications of Contributor
+Version or ii) the combination of Modifications made by that
+Contributor with other software (except as part of the Contributor
+Version) or other devices; or 4) under Patent Claims infringed by
+Covered Code in the absence of Modifications made by that Contributor.
+
+3. Distribution Obligations. 
+
+3.1. Application of License.
+The Modifications which You create or to which You contribute are
+governed by the terms of this License, including without limitation
+Section 2.2. The Source Code version of Covered Code may be
+distributed only under the terms of this License or a future version
+of this License released under Section 6.1, and You must include a
+copy of this License with every copy of the Source Code You
+distribute. You may not offer or impose any terms on any Source Code
+version that alters or restricts the applicable version of this
+License or the recipients' rights hereunder.  However, You may include
+an additional document offering the additional rights described in
+Section 3.5.
+
+3.2. Availability of Source Code.
+Any Modification which You create or to which You contribute must be
+made available in Source Code form under the terms of this License
+either on the same media as an Executable version or via an accepted
+Electronic Distribution Mechanism to anyone to whom you made an
+Executable version available; and if made available via Electronic
+Distribution Mechanism, must remain available for at least twelve (12)
+months after the date it initially became available, or at least six
+(6) months after a subsequent version of that particular Modification
+has been made available to such recipients. You are responsible for
+ensuring that the Source Code version remains available even if the
+Electronic Distribution Mechanism is maintained by a third party.
+
+3.3. Description of Modifications.
+You must cause all Covered Code to which You contribute to contain a
+file documenting the changes You made to create that Covered Code and
+the date of any change. You must include a prominent statement that
+the Modification is derived, directly or indirectly, from Original
+Code provided by the Initial Developer and including the name of the
+Initial Developer in (a) the Source Code, and (b) in any notice in an
+Executable version or related documentation in which You describe the
+origin or ownership of the Covered Code.
+
+3.4. Intellectual Property Matters 
+
+(a) Third Party Claims.
+If Contributor has knowledge that a license under a third party's
+intellectual property rights is required to exercise the rights
+granted by such Contributor under Sections 2.1 or 2.2, Contributor
+must include a text file with the Source Code distribution titled
+"LEGAL" which describes the claim and the party making the claim in
+sufficient detail that a recipient will know whom to contact. If
+Contributor obtains such knowledge after the Modification is made
+available as described in Section 3.2, Contributor shall promptly
+modify the LEGAL file in all copies Contributor makes available
+thereafter and shall take other steps (such as notifying appropriate
+mailing lists or newsgroups) reasonably calculated to inform those who
+received the Covered Code that new knowledge has been obtained.
+
+(b) Contributor APIs.
+If Contributor's Modifications include an application programming
+interface and Contributor has knowledge of patent licenses which are
+reasonably necessary to implement that API, Contributor must also
+include this information in the LEGAL file.
+ 
+(c) Representations.
+Contributor represents that, except as disclosed pursuant to Section
+3.4(a) above, Contributor believes that Contributor's Modifications
+are Contributor's original creation(s) and/or Contributor has
+sufficient rights to grant the rights conveyed by this License.
+
+3.5. Required Notices.
+You must duplicate the notice in Exhibit A in each file of the Source
+Code.  If it is not possible to put such notice in a particular Source
+Code file due to its structure, then You must include such notice in a
+location (such as a relevant directory) where a user would be likely
+to look for such a notice.  If You created one or more Modification(s)
+You may add your name as a Contributor to the notice described in
+Exhibit A.  You must also duplicate this License in any documentation
+for the Source Code where You describe recipients' rights or ownership
+rights relating to Covered Code.  You may choose to offer, and to
+charge a fee for, warranty, support, indemnity or liability
+obligations to one or more recipients of Covered Code. However, You
+may do so only on Your own behalf, and not on behalf of the Initial
+Developer or any Contributor.  You must make it absolutely clear than
+any such warranty, support, indemnity or liability obligation is
+offered by You alone, and You hereby agree to indemnify the Initial
+Developer and every Contributor for any liability incurred by the
+Initial Developer or such Contributor as a result of warranty,
+support, indemnity or liability terms You offer.
+
+3.6. Distribution of Executable Versions. 
+You may distribute Covered Code in Executable form only if the
+requirements of Section 3.1-3.5 have been met for that Covered Code,
+and if You include a notice stating that the Source Code version of
+the Covered Code is available under the terms of this License,
+including a description of how and where You have fulfilled the
+obligations of Section 3.2. The notice must be conspicuously included
+in any notice in an Executable version, related documentation or
+collateral in which You describe recipients' rights relating to the
+Covered Code. You may distribute the Executable version of Covered
+Code or ownership rights under a license of Your choice, which may
+contain terms different from this License, provided that You are in
+compliance with the terms of this License and that the license for the
+Executable version does not attempt to limit or alter the recipient's
+rights in the Source Code version from the rights set forth in this
+License. If You distribute the Executable version under a different
+license You must make it absolutely clear that any terms which differ
+from this License are offered by You alone, not by the Initial
+Developer or any Contributor. You hereby agree to indemnify the
+Initial Developer and every Contributor for any liability incurred by
+the Initial Developer or such Contributor as a result of any such
+terms You offer.
+
+3.7. Larger Works. 
+You may create a Larger Work by combining Covered Code with other code
+not governed by the terms of this License and distribute the Larger
+Work as a single product. In such a case, You must make sure the
+requirements of this License are fulfilled for the Covered Code.
+
+4. Inability to Comply Due to Statute or Regulation. 
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Code due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description
+must be included in the LEGAL file described in Section 3.4 and must
+be included with all distributions of the Source Code. Except to the
+extent prohibited by statute or regulation, such description must be
+sufficiently detailed for a recipient of ordinary skill to be able to
+understand it.
+
+5. Application of this License. 
+
+This License applies to code to which the Initial Developer has
+attached the notice in Exhibit A and to related Covered Code.
+
+6. Versions of the License. 
+
+6.1. New Versions. 
+Netscape Communications Corporation ("Netscape") may publish revised
+and/or new versions of the License from time to time. Each version
+will be given a distinguishing version number.
+
+6.2. Effect of New Versions. 
+Once Covered Code has been published under a particular version of the
+License, You may always continue to use it under the terms of that
+version. You may also choose to use such Covered Code under the terms
+of any subsequent version of the License published by Netscape.  No
+one other than Netscape has the right to modify the terms applicable
+to Covered Code created under this License.
+
+6.3. Derivative Works. 
+If You create or use a modified version of this License (which you may
+only do in order to apply it to code which is not already Covered Code
+governed by this License), You must (a) rename Your license so that
+the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
+"MPL", "NPL" or any confusingly similar phrase do not appear in your
+license (except to note that your license differs from this License)
+and (b) otherwise make it clear that Your version of the license
+contains terms which differ from the Mozilla Public License and
+Netscape Public License.  (Filling in the name of the Initial
+Developer, Original Code or Contributor in the notice described in
+Exhibit A shall not of themselves be deemed to be modifications of
+this License.)
+
+7. DISCLAIMER OF WARRANTY. 
+
+COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, 
+WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, 
+INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS 
+FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-
+INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 
+COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE 
+IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER 
+CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR 
+CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL 
+PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED 
+HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
+
+8. TERMINATION. 
+
+8.1.  This License and the rights granted hereunder will terminate
+automatically if You fail to comply with terms herein and fail to cure
+such breach within 30 days of becoming aware of the breach. All
+sublicenses to the Covered Code which are properly granted shall
+survive any termination of this License. Provisions which, by their
+nature, must remain in effect beyond the termination of this License
+shall survive.
+
+8.2.  If You initiate litigation by asserting a patent infringement
+claim (excluding declaratory judgment actions) against Initial
+Developer or a Contributor (the Initial Developer or Contributor
+against whom You file such action is referred to as "Participant")
+alleging that:
+
+(a) such Participant's Contributor Version directly or indirectly
+infringes any patent, then any and all rights granted by such
+Participant to You under Sections 2.1 and/or 2.2 of this License
+shall, upon 60 days notice from Participant terminate prospectively,
+unless if within 60 days after receipt of notice You either: (i) agree
+in writing to pay Participant a mutually agreeable reasonable royalty
+for Your past and future use of Modifications made by such
+Participant, or (ii) withdraw Your litigation claim with respect to
+the Contributor Version against such Participant.  If within 60 days
+of notice, a reasonable royalty and payment arrangement are not
+mutually agreed upon in writing by the parties or the litigation claim
+is not withdrawn, the rights granted by Participant to You under
+Sections 2.1 and/or 2.2 automatically terminate at the expiration of
+the 60 day notice period specified above.
+
+(b) any software, hardware, or device, other than such Participant's
+Contributor Version, directly or indirectly infringes any patent, then
+any rights granted to You by such Participant under Sections 2.1(b)
+and 2.2(b) are revoked effective as of the date You first made, used,
+sold, distributed, or had made, Modifications made by that
+Participant.
+
+8.3.  If You assert a patent infringement claim against Participant
+alleging that such Participant's Contributor Version directly or
+indirectly infringes any patent where such claim is resolved (such as
+by license or settlement) prior to the initiation of patent
+infringement litigation, then the reasonable value of the licenses
+granted by such Participant under Sections 2.1 or 2.2 shall be taken
+into account in determining the amount or value of any payment or
+license.
+
+8.4.  In the event of termination under Sections 8.1 or 8.2 above, all
+end user license agreements (excluding distributors and resellers)
+which have been validly granted by You or any distributor hereunder
+prior to termination shall survive termination.
+
+9. LIMITATION OF LIABILITY. 
+
+UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT 
+(INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE 
+INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF 
+COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO 
+ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL 
+DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES 
+FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR 
+MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, 
+EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF 
+SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO 
+LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S 
+NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. 
+SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF 
+INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND 
+LIMITATION MAY NOT APPLY TO YOU.
+
+10. U.S. GOVERNMENT END USERS. 
+
+The Covered Code is a "commercial item," as that term is defined in
+48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
+software" and "commercial computer software documentation," as such
+terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
+C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
+all U.S. Government End Users acquire Covered Code with only those
+rights set forth herein.
+
+11. MISCELLANEOUS. 
+
+This License represents the complete agreement concerning subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. This License shall be governed by
+California law provisions (except to the extent applicable law, if
+any, provides otherwise), excluding its conflict-of-law
+provisions. With respect to disputes in which at least one party is a
+citizen of, or an entity chartered or registered to do business in the
+United States of America, any litigation relating to this License
+shall be subject to the jurisdiction of the Federal Courts of the
+Northern District of California, with venue lying in Santa Clara
+County, California, with the losing party responsible for costs,
+including without limitation, court costs and reasonable attorneys'
+fees and expenses. The application of the United Nations Convention on
+Contracts for the International Sale of Goods is expressly
+excluded. Any law or regulation which provides that the language of a
+contract shall be construed against the drafter shall not apply to
+this License.
+
+12. RESPONSIBILITY FOR CLAIMS. 
+
+As between Initial Developer and the Contributors, each party is
+responsible for claims and damages arising, directly or indirectly,
+out of its utilization of rights under this License and You agree to
+work with Initial Developer and Contributors to distribute such
+responsibility on an equitable basis. Nothing herein is intended or
+shall be deemed to constitute any admission of liability.
+
+13. MULTIPLE-LICENSED CODE. 
+
+Initial Developer may designate portions of the Covered Code as
+"Multiple-Licensed".  "Multiple-Licensed" means that the Initial
+Developer permits you to utilize portions of the Covered Code under
+Your choice of the APL or the alternative licenses, if any, specified
+by the Initial Developer in the file described in Exhibit A.
+
+
+
+AMENDMENTS
+
+The AOLserver Public License Version 1.1 ("APL") consists of the
+Mozilla Public License Version 1.1 with the following Amendments,
+including "Exhibit A - AOLserver Public License".  Files identified
+with "Exhibit A - AOLserver Public License" are governed by the
+AOLserver Public License Version 1.1.
+
+Additional Terms applicable to the AOLserver Public License. 
+
+I. Effect. 
+These additional terms described in this AOLserver Public License --
+Amendments shall apply to the AOLserver Code and to all Covered Code
+under this License.
+
+II. "AOL's Branded Code" means Covered Code that AOL distributes
+and/or permits others to distribute under one or more trademark(s)
+which are controlled by AOL but which are not licensed for use under
+this License.
+
+III. AOL Trademarks.  This License does not grant any rights to use
+the trademarks "AOL", "America Online", "AOLserver" or the "AOL
+Triangle" logo even if such marks are included in the Original Code
+or Modifications.
+
+IV. Inability to Comply Due to Contractual Obligation.  Prior to
+licensing the Original Code under this License, AOL has licensed third
+party code for use in AOL's Branded Code. To the extent that AOL is
+limited contractually from making such third party code available
+under this License, AOL may choose to reintegrate such code into
+Covered Code without being required to distribute such code in Source
+Code form, even if such code would otherwise be considered
+"Modifications" under this License.
+
+V. [Intentionally Omitted]
+ 
+VI. Litigation. 
+Notwithstanding the limitations of Section 11 above, the provisions
+regarding litigation in Section 11(a), (b) and (c) of the License
+shall apply to all disputes relating to this License.
+
+
+EXHIBIT A - AOLserver Public License.
+  
+"The contents of this file are subject to the AOLserver Public
+License Version 1.1 (the "License"); you may not use this file except
+in compliance with the License. You may obtain a copy of the License
+at http://aolserver.com/.
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+the License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is AOLserver Code and related documentation
+distributed by AOL.
+
+The Initial Developer of the Original Code is America Online,
+Inc. Portions created by AOL are Copyright (C) 1999 America Online,
+Inc. All Rights Reserved.
+
+Contributor(s): ______________________________________.
+
+Alternatively, the contents of this file may be used under the terms
+of the GNU General Public License (the "GPL"), in which case the
+provisions of GPL are applicable instead of those above.  If you wish
+to allow use of your version of this file only under the terms of the
+GPL and not to allow others to use your version of this file under the
+License, indicate your decision by deleting the provisions above and
+replace them with the notice and other provisions required by the GPL.
+If you do not delete the provisions above, a recipient may use your
+version of this file under either the License or the GPL.
+
+ifndef NAVISERVER
+    NAVISERVER  = /usr/local/ns
+endif
+
+# OCaml configuration
+OCAMLC 	 	= ocamlc
+OCAMLMKLIB 	= ocamlmklib
+OCAMLHOME       = $(shell $(OCAMLC) -where)
+OCAMLCFLAGS 	= -g -w s -thread
+OCAMLLIBS	= -L$(OCAMLHOME) -lcamlrun -ltermcap -lunix -lstr -lnaviserver
+OCAMLLDFLAGS	= -cclib "-fPIC -shared $(OCAMLLIBS) $(LDFLAGS) $(LIBS) $(LDRPATH)"
+# Required OCaml modules
+OCAMLMODS	= naviserver.cma dynlink.cma str.cma unix.cma
+OCAMLOBJS 	= nsocaml.cmo
+
+# NaviServer configuration
+MOD		= nsocaml.so
+BUILD		+= naviserver.cma
+CLEAN		+= clean-ocaml
+CFLAGS	 	= -I$(OCAMLHOME)
+OBJS     	= nsocaml.o
+MODLIBS		= -L$(OCAMLHOME) -lcamlrun -ltermcap -lunix -lstr -lnaviserver
+
+NSLIB		= naviserver
+
+include  $(NAVISERVER)/include/Makefile.module
+
+# Custom compilcation of Naviserver module
+nsocaml.so: $(OCAMLOBJS) $(OBJS) install-ocaml
+	$(OCAMLC) -linkall -custom $(OCAMLCFLAGS) $(OBJS) $(OCAMLMODS) $(OCAMLOBJS) -o $@ $(OCAMLLDFLAGS)
+
+$(NSLIB).cma:	$(NSLIB).cmo $(NSLIB).o $(NSLIB).ml
+	$(OCAMLMKLIB) -o $(NSLIB) $(NSLIB).cmo
+	$(OCAMLMKLIB) -o $(NSLIB) $(NSLIB).o $(LDFLAGS) $(LIBS) $(LDRPATH)
+	$(RANLIB) lib$(NSLIB).a
+
+$(NSLIB).o:	$(NSLIB).c
+	$(OCAMLC) -I $(NAVISERVER)/include -c $(NSLIB).c -o $@
+	
+%.cmi: %.mli
+	$(OCAMLC) $(OCAMLCFLAGS) -c $<
+        
+%.cmo: %.ml
+	$(OCAMLC) $(OCAMLCFLAGS) -c $<
+
+clean-ocaml:
+	rm -rf *.cma *.cmo *.cmi *.o *.so *~ *.a
+	make -C test clean
+
+world:	clean all install tests
+
+tests:
+	make -C test all
+
+install-ocaml:
+	if test -f dll$(NSLIB).so; then cp -f dll$(NSLIB).so $(OCAMLHOME)/stublibs; fi
+	cp -f lib$(NSLIB).a $(OCAMLHOME)/lib$(NSLIB).a
+	cp -f $(NSLIB).cma $(NSLIB).cmi $(OCAMLHOME)
+
+OCaml Module for NaviServer 4.x
+Release 0.2
+vlad@crystalballinc.com
+
+This is NaviServer module that implements OCaml interpreter inside NaviServer.
+
+/*--------------------------------------------------------------------*/
+
+Compiling and Installing
+
+To compile this driver, you'll need to have OCaml installed.
+All NaviServer specific API is built as separate OCaml library naviserver.cma,
+to see currently available functions check naviserver.ml source file.
+
+Usage
+
+  ns_ocaml usage:
+
+    ns_ocaml load filepath
+      Load and execute OCaml object file
+
+    ns_ocaml call function ?arg?
+      Call OCaml function from Tcl, pass optional parameter. OCaml function
+      should be registered using Callback.register in OCaml.
+
+Authors
+     Vlad Seryakov vlad@crystalballinc.com

File naviserver.c

+/* 
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis,WITHOUT WARRANTY OF ANY KIND,either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * Alternatively,the contents of this file may be used under the terms
+ * of the GNU General Public License(the "GPL"),in which case the
+ * provisions of GPL are applicable instead of those above.  If you wish
+ * to allow use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under the
+ * License,indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the GPL.
+ * If you do not delete the provisions above,a recipient may use your
+ * version of this file under either the License or the GPL.
+ *
+ * Author Vlad Seryakov vlad@crystalballinc.com
+ * 
+ */
+
+#define USE_TCL8X
+
+#include <caml/alloc.h>
+#include <caml/callback.h>
+#include <caml/memory.h>
+#include <caml/mlvalues.h>
+#include "ns.h"
+#include "nsd.h"
+
+static value
+copy_string2(char *str)
+{
+   return copy_string(str ? str : "");
+}
+
+static char *
+GetServer()
+{
+   Ns_Conn *conn = Ns_GetConn();
+
+   if(conn) return Ns_ConnServer(conn);
+   return nsconf.servers.string;
+}
+
+static NsInterp *
+GetInterp()
+{
+   Ns_Conn *conn = Ns_GetConn();
+   if(conn) return NsGetInterp(Ns_GetConnInterp(conn));
+   return NsGetInterp(Ns_TclAllocateInterp(GetServer()));
+}
+
+static void
+ThreadArgProc(Tcl_DString *dsPtr, void *proc, void *arg)
+{
+    Ns_GetProcInfo(dsPtr, proc, arg);
+}
+
+CAMLprim value
+Ns_Eval_OCaml(value oscript)
+{
+    CAMLparam1(oscript);
+    CAMLlocal1(retval);
+    char *result = "";
+    NsInterp *itPtr;
+
+    if((itPtr = GetInterp())) {
+      if(Tcl_EvalEx(itPtr->interp,String_val(oscript),-1,0) != TCL_OK)
+        result = Ns_TclLogError(itPtr->interp);
+      else
+        result = (char *)Tcl_GetStringResult(itPtr->interp);
+    }
+    retval = copy_string(result);
+    if(itPtr) Ns_TclDeAllocateInterp(itPtr->interp);
+    CAMLreturn(retval);
+}
+
+CAMLprim value
+Ns_Log_OCaml(value olevel,value ostr)
+{
+    CAMLparam2(olevel, ostr);
+    int clevel = 0;
+    char *level = String_val(olevel);
+    char *str = String_val(ostr);
+
+    if(!strcasecmp(level,"Debug")) clevel = Debug; else
+    if(!strcasecmp(level,"Error")) clevel = Error; else
+    if(!strcasecmp(level,"Notice")) clevel = Notice; else
+    if(!strcasecmp(level,"Warning")) clevel = Warning; else
+    if(!strcasecmp(level,"Fatal")) clevel = Fatal;
+    Ns_Log(clevel,str);
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_Info_OCaml(value oname)
+{
+    CAMLparam1(oname);
+    CAMLlocal1(retval);
+    Tcl_DString ds;
+    NsServer *servPtr;
+    char *result = "";
+
+    static CONST char *cmds[] = {
+        "address", "argv0", "boottime", "builddate", "callbacks",
+        "config", "home", "hostname", "label", "locks", "log",
+        "major", "minor", "name", "nsd", "pageroot", "patchlevel",
+        "pid", "platform", "pools", "scheduled", "server", "servers",
+        "sockcallbacks", "tag", "tcllib", "threads", "uptime",
+        "version", "winnt", 0 };
+    enum {
+        IAddressIdx, IArgv0Idx, IBoottimeIdx, IBuilddateIdx, ICallbacksIdx,
+        IConfigIdx, IHomeIdx, hostINameIdx, ILabelIdx, ILocksIdx, ILogIdx,
+        IMajorIdx, IMinorIdx, INameIdx, INsdIdx, IPageRootIdx, IPatchLevelIdx,
+        IPidIdx, IPlatformIdx, IPoolsIdx, IScheduledIdx, IServerIdx, IServersIdx,
+        sockICallbacksIdx, ITagIdx, ITclLibIdx, IThreadsIdx, IUptimeIdx,
+        IVersionIdx, IWinntIdx, INoneIdx
+    } opt;
+
+    for(opt = 0;cmds[opt];opt++)
+      if(!strcmp(cmds[opt],String_val(oname))) break;
+
+    Tcl_DStringInit(&ds);
+    switch(opt) {
+     case INoneIdx:
+        break;
+     case IAddressIdx:
+        if(!(result = Ns_InfoAddress())) result = "";
+        break;
+     case IArgv0Idx:
+        if(!(result = nsconf.argv0)) result = "";
+        break;
+     case IBoottimeIdx:
+        Ns_DStringPrintf(&ds,"%ld",Ns_InfoBootTime());
+        result = ds.string;
+        break;
+     case IBuilddateIdx:
+        result = Ns_InfoBuildDate();
+        break;
+     case ICallbacksIdx:
+        NsGetCallbacks(&ds);
+        result = ds.string;
+        break;
+     case IConfigIdx:
+        if(!(result = Ns_InfoConfigFile())) result = "";
+        break;
+     case IHomeIdx:
+        if(!(result = Ns_InfoHomePath())) result = "";
+        break;
+     case hostINameIdx:
+        if(!(result = Ns_InfoHostname())) result = "";
+        break;
+     case ILabelIdx:
+        if(!(result = Ns_InfoLabel())) result = "";
+        break;
+     case ILocksIdx:
+        Ns_MutexList(&ds);
+        result = ds.string;
+        break;
+     case ILogIdx:
+        if(!(result = Ns_InfoErrorLog())) result = "";
+        break;
+     case IMajorIdx:
+        Ns_DStringPrintf(&ds,"%ld",NS_MAJOR_VERSION);
+        result = ds.string;
+        break;
+     case IMinorIdx:
+        Ns_DStringPrintf(&ds,"%ld",NS_MINOR_VERSION);
+        result = ds.string;
+        break;
+     case INameIdx:
+        if(!(result = Ns_InfoServerName())) result = "";
+        break;
+     case INsdIdx:
+        if(!(result = nsconf.nsd)) result = "";
+        break;
+     case IPageRootIdx:
+        if((servPtr = NsGetServer(GetServer()))) result = servPtr->fastpath.pageroot;
+        break;
+     case IPatchLevelIdx:
+        result = NS_PATCH_LEVEL;
+        break;
+     case IPidIdx:
+        Ns_DStringPrintf(&ds,"%ld",Ns_InfoPid());
+        result = ds.string;
+        break;
+     case IPlatformIdx:
+        if(!(result = Ns_InfoPlatform())) result = "";
+        break;
+     case IPoolsIdx:
+        break;
+     case IScheduledIdx:
+        NsGetScheduled(&ds);
+        result = ds.string;
+        break;
+     case IServerIdx:
+        if((servPtr = NsGetServer(GetServer()))) result = servPtr->server;
+        break;
+     case IServersIdx:
+        result = GetServer();
+        break;
+     case sockICallbacksIdx:
+        NsGetSockCallbacks(&ds);
+        result = ds.string;
+        break;
+     case ITagIdx:
+        if(!(result = Ns_InfoTag())) result = "";
+        break;
+     case ITclLibIdx:
+        if(!(result = Ns_TclLibrary(GetServer()))) result = "";
+        break;
+     case IThreadsIdx:
+        Ns_ThreadList(&ds,ThreadArgProc);
+        result = ds.string;
+        break;
+     case IUptimeIdx:
+        Ns_DStringPrintf(&ds,"%ld",Ns_InfoUptime());
+        result = ds.string;
+        break;
+     case IVersionIdx:
+        result = NS_VERSION;
+        break;
+     case IWinntIdx:
+#ifdef _WIN32
+        result = "true";
+#else
+	result = "false";
+#endif
+        break;
+    }
+    retval = copy_string(result);
+    Tcl_DStringFree(&ds);
+    CAMLreturn(retval);
+}
+
+CAMLprim value
+Ns_Server_OCaml(value oname)
+{
+    CAMLparam1(oname);
+    CAMLlocal1(retval);
+    static CONST char *cmds[] = {
+         "active", "all", "connections", "keepalive",
+         "pools", "queued", "threads", "waiting", 0
+    };
+    enum {
+         SActiveIdx, SAllIdx, SConnectionsIdx, SKeepaliveIdx,
+         SPoolsIdx, SQueuedIdx, SThreadsIdx, SWaitingIdx, INoneIdx
+    } opt;
+    char buf[100];
+    Conn *connPtr;
+    Tcl_DString ds;
+    NsServer *servPtr;
+    ConnPool *poolPtr;
+    char *result = "";
+    Ns_Time now, diff;
+
+    for(opt = 0;cmds[opt];opt++)
+      if(!strcmp(cmds[opt],String_val(oname))) break;
+
+    Tcl_DStringInit(&ds);
+    switch(opt) {
+     case INoneIdx:
+        break;
+     case SQueuedIdx:
+     case SActiveIdx:
+     case SAllIdx:
+        if(!(servPtr = NsGetServer(GetServer()))) break;
+        Ns_MutexLock(&servPtr->pools.lock);
+        connPtr = servPtr->pools.defaultPtr->queue.active.firstPtr;
+        while(connPtr) {
+          Ns_GetTime(&now);
+          Ns_DiffTime(&now, &connPtr->startTime, &diff);
+          Ns_DStringPrintf(&ds,"%d %s running %s %s %ld.%ld %d",
+             connPtr->id,
+             Ns_ConnPeer((Ns_Conn *)connPtr),
+             connPtr->request && connPtr->request->method ? connPtr->request->method : "?",
+             connPtr->request && connPtr->request->url ? connPtr->request->url : "?",
+             diff.sec,
+             diff.usec,
+             connPtr->nContentSent);
+          connPtr = connPtr->nextPtr;
+        }
+        Ns_MutexUnlock(&servPtr->pools.lock);
+        result = ds.string;
+        break;
+     case SConnectionsIdx:
+        if(!(servPtr = NsGetServer(GetServer()))) break;
+        Ns_MutexLock(&servPtr->pools.lock);
+        Ns_DStringPrintf(&ds,"%d",servPtr->pools.nextconnid);
+        Ns_MutexUnlock(&servPtr->pools.lock);
+        result = ds.string;
+        break;
+     case SKeepaliveIdx:
+        Ns_DStringPrintf(&ds,"%d",nsconf.keepalive.npending);
+        result = ds.string;
+        break;
+     case SPoolsIdx:
+        if(!(servPtr = NsGetServer(GetServer()))) break;
+        Ns_MutexLock(&servPtr->pools.lock);
+        for(poolPtr = servPtr->pools.firstPtr;poolPtr;poolPtr = poolPtr->nextPtr)
+          Ns_DStringPrintf(&ds,"%s ",poolPtr->pool);
+        Ns_MutexUnlock(&servPtr->pools.lock);
+        result = ds.string;
+        break;
+     case SThreadsIdx:
+        if(!(servPtr = NsGetServer(GetServer()))) break;
+        Ns_MutexLock(&servPtr->pools.lock);
+        sprintf(buf, "min %d max %d current %d idle %d",
+                servPtr->pools.defaultPtr->threads.min,
+                servPtr->pools.defaultPtr->threads.max,
+                servPtr->pools.defaultPtr->threads.current,
+                servPtr->pools.defaultPtr->threads.idle);
+        Ns_MutexUnlock(&servPtr->pools.lock);
+        result = buf;
+        break;
+     case SWaitingIdx:
+        if(!(servPtr = NsGetServer(GetServer()))) break;
+        Ns_MutexLock(&servPtr->pools.lock);
+        Ns_DStringPrintf(&ds,"%d",servPtr->pools.defaultPtr->queue.wait.num);
+        Ns_MutexUnlock(&servPtr->pools.lock);
+        result = ds.string;
+        break;
+    }
+    retval = copy_string(result);
+    Tcl_DStringFree(&ds);
+    CAMLreturn(retval);
+}
+
+CAMLprim value
+Ns_Conn_OCaml(value oname)
+{
+    CAMLparam1(oname);
+    CAMLlocal1(retval);
+    int idx;
+    Ns_Set *form;
+    Conn *connPtr;
+    Ns_Conn *conn;
+    Tcl_DString ds;
+    NsInterp *itPtr;
+    char *result = "";
+    Tcl_HashEntry *hPtr;
+    Tcl_HashSearch search;
+
+    static CONST char *cmds[] = {
+	 "authpassword", "authuser", "close", "content", "contentlength",
+	 "copy", "driver", "encoding", "files", "fileoffset",
+	 "filelength", "fileheaders", "flags", "form", "headers",
+	 "host", "id", "isconnected", "location", "method",
+	 "outputheaders", "peeraddr", "peerport", "port", "protocol",
+	 "query", "request", "server", "sock", "start", "status",
+	 "url", "urlc", "urlencoding", "urlv", "version", "write_encoded",
+         0
+    };
+    enum ISubCmdIdx {
+	 CAuthPasswordIdx, CAuthUserIdx, CCloseIdx, CContentIdx,
+	 CContentLengthIdx, CCopyIdx, CDriverIdx, CEncodingIdx,
+	 CFilesIdx, CFileOffIdx, CFileLenIdx, CFileHdrIdx, CFlagsIdx,
+	 CFormIdx, CHeadersIdx, CHostIdx, CIdIdx, CIsConnectedIdx,
+	 CLocationIdx, CMethodIdx, COutputHeadersIdx, CPeerAddrIdx,
+	 CPeerPortIdx, CPortIdx, CProtocolIdx, CQueryIdx, CRequestIdx,
+	 CServerIdx, CSockIdx, CStartIdx, CStatusIdx, CUrlIdx,
+	 CUrlcIdx, CUrlEncodingIdx, CUrlvIdx, CVersionIdx, CWriteEncodedIdx,
+         INoneIdx
+    } opt;
+
+    for(opt = 0;cmds[opt];opt++)
+      if(!strcmp(cmds[opt],String_val(oname))) break;
+
+    connPtr = (Conn*)conn = Ns_GetConn();
+
+    if(opt != CIsConnectedIdx && !connPtr) CAMLreturn(copy_string(result));
+
+    Tcl_DStringInit(&ds);
+    switch(opt) {
+     case INoneIdx:
+        break;
+
+     case CIsConnectedIdx:
+        result = connPtr ? "true" : "false";
+	break;
+		
+     case CUrlvIdx:
+     	for(idx = 0; idx < connPtr->request->urlc; idx++)
+	  Ns_DStringPrintf(&ds,"%s ",connPtr->request->urlv[idx]);
+        break;
+
+     case CAuthUserIdx:
+	result = connPtr->authUser;
+	break;
+	    
+     case CAuthPasswordIdx:
+	result = connPtr->authPasswd;
+        break;
+
+     case CContentIdx:
+        result = Ns_ConnContent(conn);
+        break;
+	    
+     case CContentLengthIdx:
+	Ns_DStringPrintf(&ds,"%d",conn->contentLength);
+        result = ds.string;
+        break;
+
+     case CEncodingIdx:
+	if(connPtr->encoding) result = (char *)Tcl_GetEncodingName(connPtr->encoding);
+        break;
+	
+     case CUrlEncodingIdx:
+        if(connPtr->urlEncoding) result = (char *)Tcl_GetEncodingName(connPtr->urlEncoding);
+        break;
+	
+     case CPeerAddrIdx:
+	result = Ns_ConnPeer(conn);
+        break;
+	
+     case CPeerPortIdx:
+        Ns_DStringPrintf(&ds,"%d",Ns_ConnPeerPort(conn));
+        result = ds.string;
+        break;
+
+     case CHeadersIdx:
+        itPtr = GetInterp();
+        if(!(itPtr->nsconn.flags & CONN_TCLHDRS)) {
+          Tcl_ResetResult(itPtr->interp);
+          Ns_TclEnterSet(itPtr->interp,connPtr->headers,NS_TCL_SET_STATIC);
+	  strcpy(itPtr->nsconn.hdrs,Tcl_GetStringResult(itPtr->interp));
+	  itPtr->nsconn.flags |= CONN_TCLHDRS;
+	}
+        result = itPtr->nsconn.hdrs;
+	break;
+	
+     case COutputHeadersIdx:
+        itPtr = GetInterp();
+	if(!(itPtr->nsconn.flags & CONN_TCLOUTHDRS)) {
+          Tcl_ResetResult(itPtr->interp);
+          Ns_TclEnterSet(itPtr->interp,connPtr->outputheaders,NS_TCL_SET_STATIC);
+	  strcpy(itPtr->nsconn.outhdrs,Tcl_GetStringResult(itPtr->interp));
+	  itPtr->nsconn.flags |= CONN_TCLOUTHDRS;
+	}
+        result = itPtr->nsconn.outhdrs;
+	break;
+	
+     case CFormIdx:
+        itPtr = GetInterp();
+	if(!(itPtr->nsconn.flags & CONN_TCLFORM)) {
+      	  form = Ns_ConnGetQuery(conn);
+	  if(form == NULL) {
+	    itPtr->nsconn.form[0] = '\0';
+	  } else {
+            Tcl_ResetResult(itPtr->interp);
+            Ns_TclEnterSet(itPtr->interp,form,NS_TCL_SET_STATIC);
+	    strcpy(itPtr->nsconn.form,Tcl_GetStringResult(itPtr->interp));
+	  }
+	  itPtr->nsconn.flags |= CONN_TCLFORM;
+	}
+        result = itPtr->nsconn.form;
+	break;
+
+     case CFilesIdx:
+	hPtr = Tcl_FirstHashEntry(&connPtr->files, &search);
+	while(hPtr) {
+	  Ns_DStringPrintf(&ds,"%s ",Tcl_GetHashKey(&connPtr->files, hPtr));
+	  hPtr = Tcl_NextHashEntry(&search);
+	}
+        result = ds.string;
+	break;
+
+     case CFileOffIdx:
+     case CFileLenIdx:
+     case CFileHdrIdx:
+        break;
+
+     case CCopyIdx:
+        break;
+
+     case CWriteEncodedIdx:
+        result = Ns_ConnGetWriteEncodedFlag(conn) ? "true" : "false";
+        break;
+
+     case CRequestIdx:
+	result = conn->request->line;
+        break;
+
+     case CMethodIdx:
+	result = conn->request->method;
+	break;
+
+     case CProtocolIdx:
+	result = conn->request->protocol;
+	break;
+
+     case CHostIdx:
+	result = conn->request->host;
+	break;
+	
+     case CPortIdx:
+	Ns_DStringPrintf(&ds,"%d",conn->request->port);
+        result = ds.string;
+        break;
+
+     case CUrlIdx:
+	result = conn->request->url;
+        break;
+	
+     case CQueryIdx:
+	result = conn->request->query;
+	break;
+	
+     case CUrlcIdx:
+	Ns_DStringPrintf(&ds,"%d",conn->request->urlc);
+        result = ds.string;
+        break;
+	
+     case CVersionIdx:
+	Ns_DStringPrintf(&ds,"%.2f",conn->request->version);
+        result = ds.string;
+        break;
+
+     case CLocationIdx:
+	result = Ns_ConnLocation(conn);
+        break;
+
+     case CDriverIdx:
+	result = Ns_ConnDriverName(conn);
+        break;
+    
+     case CServerIdx:
+	result = Ns_ConnServer(conn);
+        break;
+
+     case CStatusIdx:
+	Ns_DStringPrintf(&ds,"%d",Ns_ConnResponseStatus(conn));
+        result = ds.string;
+        break;
+
+     case CSockIdx:
+	Ns_DStringPrintf(&ds,"%d",Ns_ConnSock(conn));
+        result = ds.string;
+        break;
+	
+     case CIdIdx:
+	Ns_DStringPrintf(&ds,"%d",Ns_ConnId(conn));
+        result = ds.string;
+        break;
+	
+     case CFlagsIdx:
+	Ns_DStringPrintf(&ds,"%d",connPtr->flags);
+        result = ds.string;
+        break;
+
+     case CStartIdx:
+	Ns_DStringPrintf(&ds,"%ld",connPtr->startTime.sec);
+        result = ds.string;
+        break;
+
+     case CCloseIdx:
+	Ns_ConnClose(conn);
+        break;
+    }
+    retval = copy_string2(result);
+    Tcl_DStringFree(&ds);
+    CAMLreturn(retval);
+}
+
+CAMLprim value
+Ns_ReturnRedirect_OCaml(value ourl)
+{
+    CAMLparam1(ourl);
+    Ns_Conn *conn = Ns_GetConn();
+    if(conn) Ns_ConnReturnRedirect(conn,String_val(ourl));
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_ReturnNotFound_OCaml()
+{
+    CAMLparam0();
+    Ns_Conn *conn = Ns_GetConn();
+    if(conn) Ns_ConnReturnNotFound(conn);
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_ReturnForbidden_OCaml()
+{
+    CAMLparam0();
+    Ns_Conn *conn = Ns_GetConn();
+    if(conn) Ns_ConnReturnForbidden(conn);
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_ReturnUnauthorized_OCaml()
+{
+    CAMLparam0();
+    Ns_Conn *conn = Ns_GetConn();
+    if(conn) Ns_ConnReturnUnauthorized(conn);
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_ReturnInternalError_OCaml()
+{
+    CAMLparam0();
+    Ns_Conn *conn = Ns_GetConn();
+    if(conn) Ns_ConnReturnInternalError(conn);
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_Return_OCaml(value ostatus,value otype,value odata)
+{
+    CAMLparam3(ostatus,otype,odata);
+    Ns_Conn *conn = Ns_GetConn();
+    if(conn) Ns_ConnReturnData(conn,Int_val(ostatus),String_val(odata),strlen(String_val(odata)),String_val(otype));
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_ReturnFile_OCaml(value ostatus,value otype,value ofile)
+{
+    CAMLparam3(ostatus,otype,ofile);
+    Ns_Conn *conn = Ns_GetConn();
+    if(conn) Ns_ConnReturnFile(conn,Int_val(ostatus),String_val(otype),String_val(ofile));
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_Write_OCaml(value ostr)
+{
+    CAMLparam1(ostr);
+    Ns_Conn *conn = Ns_GetConn();
+    if(conn) Ns_ConnPuts(conn,String_val(ostr));
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_QueryExists_OCaml(value ostr)
+{
+    CAMLparam1(ostr);
+    int result = -1;
+    Ns_Conn *conn = Ns_GetConn();
+    Ns_Set *form = conn ? Ns_ConnGetQuery(conn) : 0;
+    if(form) result = Ns_SetIFind(form,String_val(ostr));
+    CAMLreturn(Val_int((result >= 0)));
+}
+
+CAMLprim value
+Ns_QueryGet_OCaml(value ostr)
+{
+    CAMLparam1(ostr);
+    CAMLlocal1(retval);
+    char *result = "";
+    Ns_Conn *conn = Ns_GetConn();
+    Ns_Set *form = conn ? Ns_ConnGetQuery(conn) : 0;
+    if(form) result = Ns_SetIGet(form,String_val(ostr));
+    retval = copy_string2(result);
+    CAMLreturn(retval);
+}
+
+CAMLprim value
+Ns_QueryGetAll_OCaml(value ostr)
+{
+    CAMLparam1(ostr);
+    CAMLlocal3(result,nrec,orec);
+    int i;
+    Ns_Conn *conn = Ns_GetConn();
+    Ns_Set *form = conn ? Ns_ConnGetQuery(conn) : 0;
+
+    result = Val_int(0); /* [] */
+    for(i = 0;form && i < form->size;i++) {
+      if(!strcasecmp(String_val(ostr),form->fields[i].name) && form->fields[i].value) {
+        orec = result;
+        result = alloc_small(2,0);
+        nrec = copy_string(form->fields[i].value);
+        Field(result,0) = nrec;
+        Field(result,1) = orec;
+      }
+    }
+    CAMLreturn(result);
+}
+
+CAMLprim value
+Ns_UrlEncode_OCaml(value ostr)
+{
+    CAMLparam1(ostr);
+    CAMLlocal1(retval);
+    Ns_DString ds;
+
+    Ns_DStringInit(&ds);
+    Ns_EncodeUrlCharset(&ds,String_val(ostr),0);
+    retval = copy_string(ds.string);
+    Ns_DStringFree(&ds);
+    CAMLreturn(retval);
+}
+
+CAMLprim value
+Ns_UrlDecode_OCaml(value ostr)
+{
+    CAMLparam1(ostr);
+    CAMLlocal1(retval);
+    Ns_DString ds;
+
+    Ns_DStringInit(&ds);
+    Ns_DecodeUrlCharset(&ds,String_val(ostr),0);
+    retval = copy_string(ds.string);
+    Ns_DStringFree(&ds);
+    CAMLreturn(retval);
+}
+
+CAMLprim value
+Ns_Config_OCaml(value osection,value okey)
+{
+    CAMLparam2(osection,okey);
+    CAMLlocal1(retval);
+    char *result = Ns_ConfigGetValue(String_val(osection),String_val(okey));
+    retval = copy_string2(result);
+    CAMLreturn(retval);
+}
+
+CAMLprim value
+Ns_GuessType_OCaml(value otype)
+{
+    CAMLparam1(otype);
+    CAMLlocal1(retval);
+    char *result = Ns_GetMimeType(String_val(otype));
+    retval = copy_string2(result);
+    CAMLreturn(retval);
+}
+
+CAMLprim value
+Ns_QuoteHtml_OCaml(value ostr)
+{
+    CAMLparam1(ostr);
+    CAMLlocal1(retval);
+    Ns_DString ds;
+
+    Ns_DStringInit(&ds);
+    Ns_QuoteHtml(&ds,String_val(ostr));
+    retval = copy_string(ds.string);
+    Ns_DStringFree(&ds);
+    CAMLreturn(retval);
+}
+
+CAMLprim value
+Ns_StripHtml_OCaml(value ostr)
+{
+    CAMLparam1(ostr);
+    CAMLlocal1(retval);
+    int inTag = 0, inSpec = 0;
+    char *sPtr, *inPtr, *outPtr, *ePtr;
+
+    inPtr = outPtr = sPtr = ns_strdup(String_val(ostr));
+    while(*inPtr != '\0') {
+      if(*inPtr == '<') {
+        inTag = 1;
+      } else
+      if(inTag && (*inPtr == '>')) {
+        inTag = 0;
+      } else
+      if(inSpec && (*inPtr == ';')) {
+        inSpec = 0;
+      } else
+      if(!inTag && !inSpec) {
+        if(*inPtr == '&') {
+          ePtr = inPtr;
+          if(*ePtr == '&') ePtr++;
+          while(*ePtr && *ePtr != ' ' && *ePtr != ';' && *ePtr != '&') ePtr++;
+          inSpec = (*ePtr == ';');
+        }
+        if(!inSpec) *outPtr++ = *inPtr;
+      }
+      ++inPtr;
+    }
+    *outPtr = 0;
+    retval = copy_string(sPtr);
+    ns_free(sPtr);
+    CAMLreturn(retval);
+}
+
+/* 
+ * The following represent the valid combinations of
+ * NS_TCL_SET flags
+ */
+ 
+#define SET_DYNAMIC         'd'
+#define SET_STATIC          't'
+#define SET_SHARED_DYNAMIC  's'
+#define SET_SHARED_STATIC   'p'
+#define IS_DYNAMIC(id)      (*(id) == SET_DYNAMIC || *(id) == SET_SHARED_DYNAMIC)
+#define IS_SHARED(id)       (*(id) == SET_SHARED_DYNAMIC || *(id) == SET_SHARED_STATIC)
+
+CAMLprim value
+Ns_SetCleanup_OCaml()
+{
+    CAMLparam0();
+    NsInterp *itPtr;
+    Tcl_HashEntry *hPtr;
+    Tcl_HashSearch search;
+    Tcl_HashTable *tablePtr;
+
+    if(!(itPtr = GetInterp())) CAMLreturn(Val_unit);
+
+    tablePtr = &itPtr->sets;
+    hPtr = Tcl_FirstHashEntry(tablePtr, &search);
+    while(hPtr != NULL) {
+      char *key = Tcl_GetHashKey(tablePtr, hPtr);
+      if(IS_DYNAMIC(key)) Ns_SetFree((Ns_Set*)Tcl_GetHashValue(hPtr));
+      hPtr = Tcl_NextHashEntry(&search);
+    }
+    Tcl_DeleteHashTable(tablePtr);
+    Tcl_InitHashTable(tablePtr, TCL_STRING_KEYS);
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_SetList_OCaml()
+{
+    CAMLparam0();
+    CAMLlocal3(result,nrec,orec);
+    NsInterp *itPtr;
+    Tcl_HashEntry *hPtr;
+    Tcl_HashSearch search;
+    Tcl_HashTable *tablePtr;
+
+    result = Val_int(0); /* [] */
+    if(!(itPtr = GetInterp()) || !(tablePtr = &itPtr->sets)) CAMLreturn(result);
+    hPtr = Tcl_FirstHashEntry(tablePtr,&search);
+    while(hPtr) {
+      orec = result;
+      result = alloc_small(2,0);
+      nrec = copy_string(Tcl_GetHashKey(tablePtr,hPtr));
+      Field(result,0) = nrec;
+      Field(result,1) = orec;
+      hPtr = Tcl_NextHashEntry(&search);
+    }
+    CAMLreturn(result);
+}
+
+CAMLprim value
+Ns_SetNew_OCaml(value oname)
+{
+    CAMLparam1(oname);
+    CAMLlocal1(retval);
+    Ns_Set *set;
+    NsInterp *itPtr;
+    char *result = "";
+
+    if((itPtr = GetInterp())) {
+      set = Ns_SetCreate(String_val(oname));
+      Tcl_ResetResult(itPtr->interp);
+      Ns_TclEnterSet(itPtr->interp,set,NS_TCL_SET_DYNAMIC);
+      result = (char*)Tcl_GetStringResult(itPtr->interp);
+    }
+    retval = copy_string2(result);
+    CAMLreturn(retval);
+}
+
+CAMLprim value
+Ns_SetCopy_OCaml(value oname)
+{
+    CAMLparam1(oname);
+    CAMLlocal1(retval);
+    Ns_Set *set;
+    NsInterp *itPtr;
+    char *result = "";
+
+    if((itPtr = GetInterp()) && (set = Ns_TclGetSet(itPtr->interp,String_val(oname)))) {
+      Tcl_ResetResult(itPtr->interp);
+      Ns_TclEnterSet(itPtr->interp,Ns_SetCopy(set),NS_TCL_SET_DYNAMIC);
+      result = (char*)Tcl_GetStringResult(itPtr->interp);
+    }
+    retval = copy_string2(result);
+    CAMLreturn(retval);
+}
+
+CAMLprim value
+Ns_SetSplit_OCaml(value oname)
+{
+    CAMLparam1(oname);
+    int i;
+    NsInterp *itPtr;
+    Ns_Set *set, **sets;
+
+    if(!(itPtr = GetInterp()) ||
+       !(set = Ns_TclGetSet(itPtr->interp,String_val(oname)))) CAMLreturn(Val_unit);
+
+    sets = Ns_SetSplit(set,'.');
+    for(i = 0; sets[i]; i++) Ns_TclEnterSet(itPtr->interp,sets[i],NS_TCL_SET_DYNAMIC);
+    ns_free(sets);
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_SetArray_OCaml(value oname)
+{
+    CAMLparam1(oname);
+    CAMLlocal3(result,nrec,orec);
+    int i;
+    Ns_Set *set;
+    NsInterp *itPtr;
+
+    result = Val_int(0); /* [] */
+    if(!(itPtr = GetInterp()) ||
+       !(set = Ns_TclGetSet(itPtr->interp,String_val(oname)))) CAMLreturn(result);
+
+    for(i = 0; i < Ns_SetSize(set); ++i) {
+      orec = result;
+      result = alloc_small(2,0);
+      nrec = copy_string(Ns_SetKey(set,i));
+      Field(result,0) = nrec;
+      Field(result,1) = orec;
+      orec = result;
+      result = alloc_small(2,0);
+      nrec = copy_string2(Ns_SetValue(set,i));
+      Field(result,0) = nrec;
+      Field(result,1) = orec;
+    }
+    CAMLreturn(result);
+}
+
+CAMLprim value
+Ns_SetSize_OCaml(value oname)
+{
+    CAMLparam1(oname);
+    NsInterp *itPtr;
+    Ns_Set *set;
+
+    if(!(itPtr = GetInterp()) ||
+       !(set = Ns_TclGetSet(itPtr->interp,String_val(oname)))) CAMLreturn(Val_int(0));
+    CAMLreturn(Val_int(Ns_SetSize(set)));
+}
+
+CAMLprim value
+Ns_SetName_OCaml(value oname)
+{
+    CAMLparam1(oname);
+    CAMLlocal1(retval);
+    NsInterp *itPtr;
+    Ns_Set *set;
+
+    if((itPtr = GetInterp())) set = Ns_TclGetSet(itPtr->interp,String_val(oname));
+    retval = copy_string(set && set->name ? set->name : "");
+    CAMLreturn(retval);
+}
+
+CAMLprim value
+Ns_SetPrint_OCaml(value oname)
+{
+    CAMLparam1(oname);
+    NsInterp *itPtr;
+    Ns_Set *set;
+
+    if(!(itPtr = GetInterp()) ||
+       !(set = Ns_TclGetSet(itPtr->interp,String_val(oname)))) CAMLreturn(Val_unit);
+    Ns_SetPrint(set);
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_SetFree_OCaml(value oname)
+{
+    CAMLparam1(oname);
+    NsInterp *itPtr;
+
+    if((itPtr = GetInterp())) Ns_TclFreeSet(itPtr->interp,String_val(oname));
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_SetFind_OCaml(value oname,value okey)
+{
+    CAMLparam2(oname,okey);
+    NsInterp *itPtr;
+    Ns_Set *set;
+
+    if(!(itPtr = GetInterp()) ||
+       !(set = Ns_TclGetSet(itPtr->interp,String_val(oname)))) CAMLreturn(Val_int(0));
+    CAMLreturn(Val_int(Ns_SetFind(set,String_val(okey))));
+}
+
+CAMLprim value
+Ns_SetIFind_OCaml(value oname,value okey)
+{
+    CAMLparam2(oname,okey);
+    NsInterp *itPtr;
+    Ns_Set *set;
+
+    if(!(itPtr = GetInterp()) ||
+       !(set = Ns_TclGetSet(itPtr->interp,String_val(oname)))) CAMLreturn(Val_int(0));
+    CAMLreturn(Val_int(Ns_SetIFind(set,String_val(okey))));
+}
+
+CAMLprim value
+Ns_SetUnique_OCaml(value oname,value okey)
+{
+    CAMLparam2(oname,okey);
+    NsInterp *itPtr;
+    Ns_Set *set;
+
+    if(!(itPtr = GetInterp()) ||
+       !(set = Ns_TclGetSet(itPtr->interp,String_val(oname)))) CAMLreturn(Val_int(0));
+    CAMLreturn(Val_int(Ns_SetUnique(set,String_val(okey))));
+}
+
+CAMLprim value
+Ns_SetIUnique_OCaml(value oname,value okey)
+{
+    CAMLparam2(oname,okey);
+    NsInterp *itPtr;
+    Ns_Set *set;
+
+    if(!(itPtr = GetInterp()) ||
+       !(set = Ns_TclGetSet(itPtr->interp,String_val(oname)))) CAMLreturn(Val_int(0));
+    CAMLreturn(Val_int(Ns_SetIUnique(set,String_val(okey))));
+}
+
+CAMLprim value
+Ns_SetDelKey_OCaml(value oname,value okey)
+{
+    CAMLparam2(oname,okey);
+    NsInterp *itPtr;
+    Ns_Set *set;
+
+    if(!(itPtr = GetInterp()) ||
+       !(set = Ns_TclGetSet(itPtr->interp,String_val(oname)))) CAMLreturn(Val_unit);
+    Ns_SetDeleteKey(set,String_val(okey));
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_SetIDelKey_OCaml(value oname,value okey)
+{
+    CAMLparam2(oname,okey);
+    NsInterp *itPtr;
+    Ns_Set *set;
+
+    if(!(itPtr = GetInterp()) ||
+       !(set = Ns_TclGetSet(itPtr->interp,String_val(oname)))) CAMLreturn(Val_unit);
+    Ns_SetIDeleteKey(set,String_val(okey));
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_SetGet_OCaml(value oname,value okey)
+{
+    CAMLparam2(oname,okey);
+    CAMLlocal1(retval);
+    Ns_Set *set;
+    NsInterp *itPtr;
+    char *result = "";
+
+    if((itPtr = GetInterp()) && (set = Ns_TclGetSet(itPtr->interp,String_val(oname))))
+      result = Ns_SetGet(set,String_val(okey));
+    retval = copy_string2(result);
+    CAMLreturn(retval);
+}
+
+CAMLprim value
+Ns_SetIGet_OCaml(value oname,value okey)
+{
+    CAMLparam2(oname,okey);
+    CAMLlocal1(retval);
+    Ns_Set *set;
+    NsInterp *itPtr;
+    char *result = "";
+
+    if((itPtr = GetInterp()) && (set = Ns_TclGetSet(itPtr->interp,String_val(oname))))
+      result = Ns_SetIGet(set,String_val(okey));
+    retval = copy_string2(result);
+    CAMLreturn(retval);
+}
+
+CAMLprim value
+Ns_SetValue_OCaml(value oname,value oidx)
+{
+    CAMLparam2(oname,oidx);
+    CAMLlocal1(retval);
+    Ns_Set *set;
+    NsInterp *itPtr;
+    char *result = "";
+
+    if((itPtr = GetInterp()) &&
+       (set = Ns_TclGetSet(itPtr->interp,String_val(oname))) &&
+       Int_val(oidx) < Ns_SetSize(set))
+      result = Ns_SetValue(set,Int_val(oidx));
+    retval = copy_string2(result);
+    CAMLreturn(retval);
+}
+
+CAMLprim value
+Ns_SetIsNull_OCaml(value oname,value oidx)
+{
+    CAMLparam2(oname,oidx);
+    Ns_Set *set;
+    NsInterp *itPtr;
+
+    if((itPtr = GetInterp()) &&
+       (set = Ns_TclGetSet(itPtr->interp,String_val(oname))) &&
+       Int_val(oidx) < Ns_SetSize(set))
+      CAMLreturn(Int_val(Ns_SetValue(set,Int_val(oidx)) ? 1 : 0));
+    CAMLreturn(Int_val(0));
+}
+
+CAMLprim value
+Ns_SetKey_OCaml(value oname,value oidx)
+{
+    CAMLparam2(oname,oidx);
+    CAMLlocal1(retval);
+    Ns_Set *set;
+    NsInterp *itPtr;
+    char *result = "";
+
+    if((itPtr = GetInterp()) && (set = Ns_TclGetSet(itPtr->interp,String_val(oname))))
+      result = Ns_SetKey(set,Int_val(oidx));
+    retval = copy_string2(result);
+    CAMLreturn(retval);
+}
+
+
+CAMLprim value
+Ns_SetDelete_OCaml(value oname,value oidx)
+{
+    CAMLparam2(oname,oidx);
+    Ns_Set *set;
+    NsInterp *itPtr;
+
+    if((itPtr = GetInterp()) &&
+       (set = Ns_TclGetSet(itPtr->interp,String_val(oname))) &&
+       Int_val(oidx) < Ns_SetSize(set))
+      Ns_SetDelete(set,Int_val(oidx));
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_SetTrunc_OCaml(value oname,value oidx)
+{
+    CAMLparam2(oname,oidx);
+    Ns_Set *set;
+    NsInterp *itPtr;
+
+    if((itPtr = GetInterp()) &&
+       (set = Ns_TclGetSet(itPtr->interp,String_val(oname))) &&
+       Int_val(oidx) < Ns_SetSize(set))
+      Ns_SetTrunc(set,Int_val(oidx));
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_SetUpdate_OCaml(value oname,value okey,value ovalue)
+{
+    CAMLparam3(oname,okey,ovalue);
+    Ns_Set *set;
+    NsInterp *itPtr;
+
+    if((itPtr = GetInterp()) &&
+       (set = Ns_TclGetSet(itPtr->interp,String_val(oname)))) {
+      Ns_SetDeleteKey(set,String_val(okey));
+      Ns_SetPut(set,String_val(okey),String_val(ovalue));
+    }
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_SetICPut_OCaml(value oname,value okey,value ovalue)
+{
+    CAMLparam3(oname,okey,ovalue);
+    Ns_Set *set;
+    NsInterp *itPtr;
+
+    if((itPtr = GetInterp()) &&
+       (set = Ns_TclGetSet(itPtr->interp,String_val(oname)))) {
+      int i = Ns_SetIFind(set,String_val(okey));
+      if(i < 0) i = Ns_SetPut(set,String_val(okey),String_val(ovalue));
+    }
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_SetCPut_OCaml(value oname,value okey,value ovalue)
+{
+    CAMLparam3(oname,okey,ovalue);
+    Ns_Set *set;
+    NsInterp *itPtr;
+
+    if((itPtr = GetInterp()) &&
+       (set = Ns_TclGetSet(itPtr->interp,String_val(oname)))) {
+      int i = Ns_SetFind(set,String_val(okey));
+      if(i < 0) i = Ns_SetPut(set,String_val(okey),String_val(ovalue));
+    }
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_SetPut_OCaml(value oname,value okey,value ovalue)
+{
+    CAMLparam3(oname,okey,ovalue);
+    Ns_Set *set;
+    NsInterp *itPtr;
+
+    if((itPtr = GetInterp()) &&
+       (set = Ns_TclGetSet(itPtr->interp,String_val(oname))))
+      Ns_SetPut(set,String_val(okey),String_val(ovalue));
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_SetMerge_OCaml(value oname,value oname2)
+{
+    CAMLparam2(oname,oname2);
+    Ns_Set *set, *set2;
+    NsInterp *itPtr;
+
+    if((itPtr = GetInterp()) &&
+       (set = Ns_TclGetSet(itPtr->interp,String_val(oname))) &&
+       (set2 = Ns_TclGetSet(itPtr->interp,String_val(oname2))))
+      Ns_SetMerge(set,set2);
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_SetMove_OCaml(value oname,value oname2)
+{
+    CAMLparam2(oname,oname2);
+    Ns_Set *set, *set2;
+    NsInterp *itPtr;
+
+    if((itPtr = GetInterp()) &&
+       (set = Ns_TclGetSet(itPtr->interp,String_val(oname))) &&
+       (set2 = Ns_TclGetSet(itPtr->interp,String_val(oname2))))
+      Ns_SetMove(set,set2);
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_NormalizePath_OCaml(value opath)
+{
+    CAMLparam1(opath);
+    CAMLlocal1(retval);
+    Ns_DString ds;
+
+    Ns_DStringInit(&ds);
+    Ns_NormalizePath(&ds,String_val(opath));
+    retval = copy_string2(ds.string);
+    Ns_DStringFree(&ds);
+    CAMLreturn(retval);
+}
+
+CAMLprim value
+Ns_Url2File_OCaml(value opath)
+{
+    CAMLparam1(opath);
+    CAMLlocal1(retval);
+    Ns_DString ds;
+    NsInterp *itPtr = GetInterp();
+
+    Ns_DStringInit(&ds);
+    if(itPtr) NsUrlToFile(&ds,itPtr->servPtr,String_val(opath));
+    retval = copy_string2(ds.string);
+    Ns_DStringFree(&ds);
+    CAMLreturn(retval);
+}
+
+CAMLprim value
+Ns_Time_OCaml()
+{
+    CAMLparam0();
+    CAMLreturn(Int_val(time(0)));
+}
+
+CAMLprim value
+Ns_FmtTime_OCaml(value otime,value ofmt)
+{
+    CAMLparam2(otime,ofmt);
+    CAMLlocal1(retval);
+    char result[512];
+    time_t time = Int_val(otime);
+
+    strftime(result,sizeof(result),String_val(ofmt),ns_localtime(&time));
+    retval = copy_string2(result);
+    CAMLreturn(retval);
+}
+
+/*
+ *  nsv_ implementation copied from tclvar.c due to static declaration
+ */
+
+typedef struct Bucket {
+    Ns_Mutex lock;
+    Tcl_HashTable arrays;
+} Bucket;
+ 
+typedef struct Array {
+    Bucket *bucketPtr;
+    Tcl_HashEntry *entryPtr;
+    Tcl_HashTable vars;
+} Array;
+
+#define UnlockArray(arrayPtr) Ns_MutexUnlock(&((arrayPtr)->bucketPtr->lock));
+
+static Array *
+LockArray(char *array,int create)
+{
+    NsInterp *itPtr = GetInterp();
+    Bucket *bucketPtr;
+    Tcl_HashEntry *hPtr;
+    Array *arrayPtr;
+    register char *p = array;
+    register unsigned int result = 0;
+    int i, new;
+
+    if(!itPtr) return 0;
+    while(1) {
+      if((i = *p++) == 0) break;
+      result += (result<<3) + i;
+    }
+    i = result % itPtr->servPtr->nsv.nbuckets;
+    bucketPtr = &itPtr->servPtr->nsv.buckets[i];
+    Ns_MutexLock(&bucketPtr->lock);
+    if(create) {
+      hPtr = Tcl_CreateHashEntry(&bucketPtr->arrays, array, &new);
+      if(!new) {
+        arrayPtr = Tcl_GetHashValue(hPtr);
+      } else {
+       arrayPtr = ns_malloc(sizeof(Array));
+       arrayPtr->bucketPtr = bucketPtr;
+       arrayPtr->entryPtr = hPtr;
+       Tcl_InitHashTable(&arrayPtr->vars, TCL_STRING_KEYS);
+       Tcl_SetHashValue(hPtr, arrayPtr);
+      }
+    } else {
+      if(!(hPtr = Tcl_FindHashEntry(&bucketPtr->arrays, array))) {
+        Ns_MutexUnlock(&bucketPtr->lock);
+        return NULL;
+      }
+      arrayPtr = Tcl_GetHashValue(hPtr);
+    }
+    return arrayPtr;
+}
+
+static void
+UpdateVar(Tcl_HashEntry *hPtr,char *val,int append)
+{
+    char *ostr, *nstr;
+    int olen = 0,nlen;
+
+    if(!val || !*val) return;
+    nlen = strlen(val);
+    ostr = Tcl_GetHashValue(hPtr);
+    if(append) {
+      if(ostr) olen = strlen(ostr);
+      nlen += olen;
+    }
+    nstr = ns_realloc(ostr,(size_t)(nlen+1));
+    nstr[olen] = 0;
+    strcat(nstr,val);
+    Tcl_SetHashValue(hPtr,nstr);
+}
+
+static void
+SetVar(Array *arrayPtr,char *key,char *value)
+{
+    int new;
+    Tcl_HashEntry *hPtr;
+
+    hPtr = Tcl_CreateHashEntry(&arrayPtr->vars,key,&new);
+    UpdateVar(hPtr,value,0);
+}
+
+static void
+FlushArray(Array *arrayPtr)
+{
+    Tcl_HashEntry *hPtr;
+    Tcl_HashSearch search;
+
+    hPtr = Tcl_FirstHashEntry(&arrayPtr->vars, &search);
+    while(hPtr != NULL) {
+      ns_free(Tcl_GetHashValue(hPtr));
+      Tcl_DeleteHashEntry(hPtr);
+      hPtr = Tcl_NextHashEntry(&search);
+    }
+}
+
+CAMLprim value
+Ns_NsvGet_OCaml(value oarray,value oname)
+{
+    CAMLparam2(oarray,oname);
+    CAMLlocal1(retval);
+    Array *arrayPtr;
+    Tcl_HashEntry *hPtr;
+    char *result = "";
+
+    if((arrayPtr = LockArray(String_val(oarray),0))) {
+      hPtr = Tcl_FindHashEntry(&arrayPtr->vars,String_val(oname));
+      if(hPtr) result = Tcl_GetHashValue(hPtr);
+      UnlockArray(arrayPtr);
+    }
+    retval = copy_string2(result);
+    CAMLreturn(retval);
+}
+
+CAMLprim value
+Ns_NsvExists_OCaml(value oarray,value oname)
+{
+    CAMLparam2(oarray,oname);
+    Array *arrayPtr;
+    int result = 0;
+
+    if((arrayPtr = LockArray(String_val(oarray),0))) {
+      if(Tcl_FindHashEntry(&arrayPtr->vars,String_val(oname))) result = 1;
+      UnlockArray(arrayPtr);
+    }
+    CAMLreturn(Val_int(result));
+}
+
+CAMLprim value
+Ns_NsvSet_OCaml(value oarray,value oname,value ovalue)
+{
+    CAMLparam3(oarray,oname,ovalue);
+    Array *arrayPtr;
+
+    arrayPtr = LockArray(String_val(oarray),1);
+    SetVar(arrayPtr,String_val(oname),String_val(ovalue));
+    UnlockArray(arrayPtr);
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_NsvIncr_OCaml(value oarray,value oname,value ovalue)
+{
+    CAMLparam3(oarray,oname,ovalue);
+    Array *arrayPtr;
+    char buf[32];
+    int new,result = 0;
+    Tcl_HashEntry *hPtr;
+
+    arrayPtr = LockArray(String_val(oarray),1);
+    hPtr = Tcl_CreateHashEntry(&arrayPtr->vars,String_val(oname),&new);
+    if(!new) result = atoi(Tcl_GetHashValue(hPtr));
+    result += Int_val(ovalue);
+    sprintf(buf,"%d",result);
+    UpdateVar(hPtr,buf,0);
+    UnlockArray(arrayPtr);
+    CAMLreturn(Val_int(result));
+}
+
+CAMLprim value
+Ns_NsvAppend_OCaml(value oarray,value oname,value ovalue)
+{
+    CAMLparam3(oarray,oname,ovalue);
+    Array *arrayPtr;
+    int new;
+    Tcl_HashEntry *hPtr;
+
+    arrayPtr = LockArray(String_val(oarray),1);
+    hPtr = Tcl_CreateHashEntry(&arrayPtr->vars,String_val(oname),&new);
+    UpdateVar(hPtr,String_val(ovalue),1);
+    UnlockArray(arrayPtr);
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_NsvUnset_OCaml(value oarray,value oname)
+{
+    CAMLparam2(oarray,oname);
+    Tcl_HashEntry *hPtr = NULL;
+    Array *arrayPtr;
+
+    if(!(arrayPtr = LockArray(String_val(oarray),0))) CAMLreturn(Val_unit);
+    if(!strcmp(String_val(oname),""))
+      Tcl_DeleteHashEntry(arrayPtr->entryPtr);
+    else {
+      hPtr = Tcl_FindHashEntry(&arrayPtr->vars,String_val(oname));
+      if(hPtr) {
+        ns_free(Tcl_GetHashValue(hPtr));
+        Tcl_DeleteHashEntry(hPtr);
+      }
+    }
+    UnlockArray(arrayPtr);
+    if(!strcmp(String_val(oname),"")) {
+      FlushArray(arrayPtr);
+      Tcl_DeleteHashTable(&arrayPtr->vars);
+      ns_free(arrayPtr);
+    }
+    CAMLreturn(Val_unit);
+}
+
+CAMLprim value
+Ns_NsvNames_OCaml(value oarray,value oname)
+{
+    CAMLparam2(oarray,oname);
+    CAMLlocal3(result,nrec,orec);
+    NsInterp *itPtr;
+    Tcl_HashEntry *hPtr;
+    Tcl_HashSearch search;
+    Bucket *bucketPtr;
+    char *pattern, *key;
+    int i;
+
+    result = Val_int(0); /* [] */
+    if(!(itPtr = GetInterp())) CAMLreturn(result);
+
+    pattern = String_val(oarray);
+    for(i = 0; i < itPtr->servPtr->nsv.nbuckets; i++) {
+      bucketPtr = &itPtr->servPtr->nsv.buckets[i];
+      Ns_MutexLock(&bucketPtr->lock);
+      hPtr = Tcl_FirstHashEntry(&bucketPtr->arrays,&search);
+      while(hPtr != NULL) {
+        key = Tcl_GetHashKey(&bucketPtr->arrays,hPtr);
+        if(!*pattern || Tcl_StringMatch(key,pattern)) {
+          orec = result;
+          result = alloc_small(2,0);
+          nrec = copy_string(key);
+          Field(result,0) = nrec;
+          Field(result,1) = orec;
+        }
+        hPtr = Tcl_NextHashEntry(&search);
+      }
+      Ns_MutexUnlock(&bucketPtr->lock);
+    }
+    CAMLreturn(result);
+}
+
+CAMLprim value
+Ns_NsvArrayNames_OCaml(value oarray,value oname)
+{
+    CAMLparam2(oarray,oname);
+    CAMLlocal3(result,nrec,orec);
+    Array *arrayPtr;
+    Tcl_HashEntry *hPtr;
+    Tcl_HashSearch search;
+    char *pattern, *key;
+
+    result = Val_int(0); /* [] */
+    arrayPtr = LockArray(String_val(oarray),0);
+    if(arrayPtr != NULL) {
+      pattern = String_val(oname);
+      hPtr = Tcl_FirstHashEntry(&arrayPtr->vars, &search);
+      while(hPtr != NULL) {
+        key = Tcl_GetHashKey(&arrayPtr->vars, hPtr);
+        if(!*pattern || Tcl_StringMatch(key,pattern)) {
+          orec = result;
+          result = alloc_small(2,0);
+          nrec = copy_string(key);
+          Field(result,0) = nrec;
+          Field(result,1) = orec;
+        }
+        hPtr = Tcl_NextHashEntry(&search);
+      }
+      UnlockArray(arrayPtr);
+    }
+    CAMLreturn(result);
+}

File naviserver.ml

+(* OCaml module for NaviServer
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1(the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis,WITHOUT WARRANTY OF ANY KIND,either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * Alternatively,the contents of this file may be used under the terms
+ * of the GNU General Public License(the "GPL"),in which case the
+ * provisions of GPL are applicable instead of those above.  If you wish
+ * to allow use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under the
+ * License,indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the GPL.
+ * If you do not delete the provisions above,a recipient may use your
+ * version of this file under either the License or the GPL.
+ *
+ * Author Vlad Seryakov vlad@crystalballinc.com
+ *
+ *)
+
+(*----- Declare external functions -----*)
+
+external ns_eval : string -> string = "Ns_Eval_OCaml"
+
+external ns_log : string -> string -> unit = "Ns_Log_OCaml"
+
+external ns_info : string -> string = "Ns_Info_OCaml"
+
+external ns_conn : string -> string = "Ns_Conn_OCaml"
+
+external ns_server : string -> string = "Ns_Server_OCaml"
+
+external ns_write : string -> unit = "Ns_Write_OCaml"
+
+external ns_returnredirect : string -> unit = "Ns_ReturnRedirect_OCaml"
+
+external ns_returnnotfound : unit -> unit = "Ns_ReturnNotFound_OCaml"
+
+external ns_returnforbidden : unit -> unit = "Ns_ReturnForbidden_OCaml"
+
+external ns_returnunauthorized : unit -> unit = "Ns_ReturnUnauthorized_OCaml"
+
+external ns_returninternalerror : unit -> unit = "Ns_ReturnInternalError_OCaml"
+
+external ns_return : int -> string -> string -> unit = "Ns_Return_OCaml"
+
+external ns_returnfile : int -> string -> string -> unit = "Ns_ReturnFile_OCaml"
+
+external ns_queryexists : string -> int = "Ns_QueryExists_OCaml"
+
+external ns_queryget : string -> string = "Ns_QueryGet_OCaml"
+
+external ns_querygetall : string -> string list = "Ns_QueryGetAll_OCaml"
+
+external ns_urlencode : string -> string = "Ns_UrlEncode_OCaml"
+
+external ns_urldecode : string -> string = "Ns_UrlDecode_OCaml"
+
+external ns_config : string -> string -> string = "Ns_Config_OCaml"
+
+external ns_guesstype : string -> string = "Ns_GuessType_OCaml"
+
+external ns_quotehtml : string -> string = "Ns_QuoteHtml_OCaml"
+
+external ns_striphtml : string -> string = "Ns_StripHtml_OCaml"
+
+external ns_set_cleanup : unit -> unit = "Ns_SetCleanup_OCaml"
+
+external ns_set_array : unit -> string list = "Ns_SetArray_OCaml"
+
+external ns_set_list : unit -> string list = "Ns_SetList_OCaml"
+
+external ns_set_split : string -> unit = "Ns_SetSplit_OCaml"
+
+external ns_set_copy : string -> string = "Ns_SetCopy_OCaml"
+
+external ns_set_new : string -> str