You are viewing a plain text version of this content. The canonical link for it is here.
Posted to rivet-dev@tcl.apache.org by mx...@apache.org on 2011/03/01 19:05:21 UTC
svn commit: r1075953 - in /tcl/rivet/branches/2.0: ChangeLog Makefile.in
VERSION configure.ac src/apache-1/mod_rivet.c src/apache-1/mod_rivet.h
src/apache-1/rivetCore.c src/apache-2/mod_rivet.c src/apache-2/mod_rivet.h
src/apache-2/rivetCore.c
Author: mxmanghi
Date: Tue Mar 1 18:05:20 2011
New Revision: 1075953
URL: http://svn.apache.org/viewvc?rev=1075953&view=rev
Log:
2011-03-01 Massimo Manghi <mx...@apache.org>
* src/apache-1/mod_rivet.[c|h]: Add handling of AbortScript and AfterEveryScript configuration parameters
* src/apache-1/rivetCode.c: Add code for apache_log_error,apache_table, abort_code and abort_page
* src/apache-2/mod_rivet.[c|h]: Add handling of AbortScript and AfterEveryScript configuration parameters
* src/apache-1/rivetCode.c: New code for abort_page and add new command abort_code
* configure.ac: module version bumped up to 2.0.3
Modified:
tcl/rivet/branches/2.0/ChangeLog
tcl/rivet/branches/2.0/Makefile.in
tcl/rivet/branches/2.0/VERSION
tcl/rivet/branches/2.0/configure.ac
tcl/rivet/branches/2.0/src/apache-1/mod_rivet.c
tcl/rivet/branches/2.0/src/apache-1/mod_rivet.h
tcl/rivet/branches/2.0/src/apache-1/rivetCore.c
tcl/rivet/branches/2.0/src/apache-2/mod_rivet.c
tcl/rivet/branches/2.0/src/apache-2/mod_rivet.h
tcl/rivet/branches/2.0/src/apache-2/rivetCore.c
Modified: tcl/rivet/branches/2.0/ChangeLog
URL: http://svn.apache.org/viewvc/tcl/rivet/branches/2.0/ChangeLog?rev=1075953&r1=1075952&r2=1075953&view=diff
==============================================================================
--- tcl/rivet/branches/2.0/ChangeLog (original)
+++ tcl/rivet/branches/2.0/ChangeLog Tue Mar 1 18:05:20 2011
@@ -1,3 +1,10 @@
+2011-03-01 Massimo Manghi <mx...@apache.org>
+ * src/apache-1/mod_rivet.[c|h]: Add handling of AbortScript and AfterEveryScript configuration parameters
+ * src/apache-1/rivetCode.c: Add code for apache_log_error,apache_table, abort_code and abort_page
+ * src/apache-2/mod_rivet.[c|h]: Add handling of AbortScript and AfterEveryScript configuration parameters
+ * src/apache-1/rivetCode.c: New code for abort_page and add new command abort_code
+ * configure.ac: module version bumped up to 2.0.3
+
2010-10-20 Massimo Manghi <mx...@apache.org>
* debian/[clean,install,docs,dirs]: removed because not needed or redundant
* debian/copyright: completed to include also the Tcl license. Added copyright notes for stuff in win/
Modified: tcl/rivet/branches/2.0/Makefile.in
URL: http://svn.apache.org/viewvc/tcl/rivet/branches/2.0/Makefile.in?rev=1075953&r1=1075952&r2=1075953&view=diff
==============================================================================
--- tcl/rivet/branches/2.0/Makefile.in (original)
+++ tcl/rivet/branches/2.0/Makefile.in Tue Mar 1 18:05:20 2011
@@ -18,7 +18,7 @@
#
# top-level Makefile.am for Apache Rivet: gets turned into a Makefile.in by automake
#
-# $Id: Makefile.am 1024313 2010-10-19 16:29:56Z mxmanghi $
+# $Id: Makefile.am 1074761 2011-02-26 00:27:56Z mxmanghi $
#
# 2007/12/25: Added target uninistall-local that removes the tcl stuff (mxmanghi)
# 2010/06/22: target instal-data-local searches for pkgIndex.tcl files and deletes them
Modified: tcl/rivet/branches/2.0/VERSION
URL: http://svn.apache.org/viewvc/tcl/rivet/branches/2.0/VERSION?rev=1075953&r1=1075952&r2=1075953&view=diff
==============================================================================
--- tcl/rivet/branches/2.0/VERSION (original)
+++ tcl/rivet/branches/2.0/VERSION Tue Mar 1 18:05:20 2011
@@ -1 +1 @@
-2.0.2
+2.0.3
Modified: tcl/rivet/branches/2.0/configure.ac
URL: http://svn.apache.org/viewvc/tcl/rivet/branches/2.0/configure.ac?rev=1075953&r1=1075952&r2=1075953&view=diff
==============================================================================
--- tcl/rivet/branches/2.0/configure.ac (original)
+++ tcl/rivet/branches/2.0/configure.ac Tue Mar 1 18:05:20 2011
@@ -22,7 +22,7 @@ dnl
# so you can encode the package version directly into the source files.
#-----------------------------------------------------------------------
-AC_INIT([Rivet],[2.0.2])
+AC_INIT([Rivet],[2.0.3])
TEA_INIT([3.9])
AC_CONFIG_AUX_DIR(tclconfig)
Modified: tcl/rivet/branches/2.0/src/apache-1/mod_rivet.c
URL: http://svn.apache.org/viewvc/tcl/rivet/branches/2.0/src/apache-1/mod_rivet.c?rev=1075953&r1=1075952&r2=1075953&view=diff
==============================================================================
--- tcl/rivet/branches/2.0/src/apache-1/mod_rivet.c (original)
+++ tcl/rivet/branches/2.0/src/apache-1/mod_rivet.c Tue Mar 1 18:05:20 2011
@@ -134,9 +134,19 @@ Rivet_ExecuteAndCheck(Tcl_Interp *interp
ap_assert (Tcl_ListObjIndex (interp, errorCodeListObj, 1, &errorCodeElementObj) == TCL_OK);
errorCodeSubString = Tcl_GetString (errorCodeElementObj);
- if (strcmp (errorCodeSubString, "ABORTPAGE") == 0) {
- goto good;
- }
+ if (strcmp (errorCodeSubString, "ABORTPAGE") == 0)
+ {
+ if (conf->rivet_abort_script)
+ {
+ if (Tcl_EvalObjEx(interp,conf->rivet_abort_script,0) == TCL_ERROR)
+ {
+ CONST84 char *errorinfo = Tcl_GetVar( interp, "errorInfo", 0 );
+ TclWeb_PrintError("<b>Rivet ErrorScript failed!</b>",1,globals->req);
+ TclWeb_PrintError( errorinfo, 0, globals->req );
+ }
+ }
+ goto good;
+ }
}
Tcl_SetVar( interp, "errorOutbuf",
@@ -145,7 +155,7 @@ Rivet_ExecuteAndCheck(Tcl_Interp *interp
/* If we don't have an error script, use the default error handler. */
if (conf->rivet_error_script ) {
- errscript = Tcl_NewStringObj(conf->rivet_error_script, -1);
+ errscript = conf->rivet_error_script;
} else {
errscript = conf->rivet_default_error_script;
}
@@ -164,7 +174,16 @@ Rivet_ExecuteAndCheck(Tcl_Interp *interp
}
/* Make sure to flush the output if buffer_add was the only output */
- good:
+good:
+
+ if (conf->after_every_script) {
+ if (Tcl_EvalObjEx(interp,conf->after_every_script,0) == TCL_ERROR)
+ {
+ CONST84 char *errorinfo = Tcl_GetVar( interp, "errorInfo", 0 );
+ TclWeb_PrintError("<b>Rivet AfterEveryScript failed!</b>",1,globals->req);
+ TclWeb_PrintError( errorinfo, 0, globals->req );
+ }
+ }
if (!globals->req->headers_set && (globals->req->charset != NULL)) {
char* ct_header = ap_pstrcat(globals->req->req->pool,"text/html;",globals->req->charset,NULL);
@@ -281,7 +300,7 @@ Rivet_ParseExecFile(TclWebRequest *req,
if (toplevel) {
if (rsc->rivet_before_script) {
- Tcl_AppendObjToObj(outbuf, Tcl_NewStringObj(rsc->rivet_before_script, -1));
+ Tcl_AppendObjToObj(outbuf, rsc->rivet_before_script);
}
}
@@ -303,8 +322,7 @@ Rivet_ParseExecFile(TclWebRequest *req,
}
if (toplevel) {
if (rsc->rivet_after_script) {
- Tcl_AppendObjToObj(outbuf,
- Tcl_NewStringObj(rsc->rivet_after_script, -1));
+ Tcl_AppendObjToObj(outbuf,rsc->rivet_after_script);
}
}
@@ -633,6 +651,14 @@ Rivet_SendContent(request_rec *r)
retval = OK;
sendcleanup:
globals->req->content_sent = 0;
+
+ globals->page_aborting = 0;
+ if (globals->abort_code != NULL)
+ {
+ Tcl_DecrRefCount(globals->abort_code);
+ globals->abort_code = NULL;
+ }
+
Tcl_MutexUnlock(&sendMutex);
return retval;
}
@@ -767,8 +793,15 @@ Rivet_PerInterpInit(server_rec *s, rivet
globals = ap_pcalloc(p, sizeof(rivet_interp_globals));
Tcl_SetAssocData(interp, "rivet", NULL, globals);
+ /* abort_page status variables in globals are set here and then
+ * reset in Rivet_SendContent just before the request processing is
+ * completed */
+
+ globals->page_aborting = 0;
+ globals->abort_code = NULL;
+
/* Eval Rivet's init.tcl file to load in the Tcl-level
- commands. */
+ commands. */
/* We call Tcl_EvalFile on init.tcl. This call sets up
* some variables and adds RIVETLIB_DESTDIR to auto_path.
@@ -954,6 +987,45 @@ Rivet_InitTclStuff(server_rec *s, pool *
}
}
+/*
+ *----------------------------------------------------------------------
+ *
+ * Rivet_AssignStringtoConf --
+ *
+ * Assign a string to a Tcl_Obj valued configuration parameter
+ *
+ * Arguments:
+ *
+ * - objPnt: Pointer to a pointer to a Tcl_Obj. If the pointer *objPnt
+ * is NULL (configuration script obj pointers are initialized to NULL)
+ * a new Tcl_Obj is created
+ * - string_value: a string to be assigned to the Tcl_Obj
+ *
+ * Results:
+ *
+ * - Pointer to a Tcl_Obj containing the parameter value.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_Obj*
+Rivet_AssignStringToConf (Tcl_Obj** objPnt, const char* string_value)
+{
+ Tcl_Obj *objarg = NULL;
+
+ if (*objPnt == NULL)
+ {
+ objarg = Tcl_NewStringObj(string_value,-1);
+ Tcl_IncrRefCount(objarg);
+ *objPnt = objarg;
+ } else {
+ objarg = *objPnt;
+ Tcl_AppendToObj(objarg, string_value, -1);
+ }
+ Tcl_AppendToObj( objarg, "\n", 1 );
+ return objarg;
+}
+
/*
*----------------------------------------------------------------------
@@ -980,44 +1052,21 @@ Rivet_SetScript( ap_pool *pool, rivet_se
Tcl_Obj *objarg = NULL;
if( STREQU( script, "GlobalInitScript" ) ) {
- if( rsc->rivet_global_init_script == NULL ) {
- objarg = Tcl_NewStringObj( string, -1 );
- Tcl_IncrRefCount( objarg );
- Tcl_AppendToObj( objarg, "\n", 1 );
- rsc->rivet_global_init_script = objarg;
- } else {
- objarg = rsc->rivet_global_init_script;
- Tcl_AppendToObj( objarg, string, -1 );
- Tcl_AppendToObj( objarg, "\n", 1 );
- }
+ objarg = Rivet_AssignStringToConf(&(rsc->rivet_global_init_script),string);
} else if( STREQU( script, "ChildInitScript" ) ) {
- if( rsc->rivet_child_init_script == NULL ) {
- objarg = Tcl_NewStringObj( string, -1 );
- Tcl_IncrRefCount( objarg );
- Tcl_AppendToObj( objarg, "\n", 1 );
- rsc->rivet_child_init_script = objarg;
- } else {
- objarg = rsc->rivet_child_init_script;
- Tcl_AppendToObj( objarg, string, -1 );
- Tcl_AppendToObj( objarg, "\n", 1 );
- }
+ objarg = Rivet_AssignStringToConf(&(rsc->rivet_child_init_script),string);
} else if( STREQU( script, "ChildExitScript" ) ) {
- if( rsc->rivet_child_exit_script == NULL ) {
- objarg = Tcl_NewStringObj( string, -1 );
- Tcl_IncrRefCount( objarg );
- Tcl_AppendToObj( objarg, "\n", 1 );
- rsc->rivet_child_exit_script = objarg;
- } else {
- objarg = rsc->rivet_child_exit_script;
- Tcl_AppendToObj( objarg, string, -1 );
- Tcl_AppendToObj( objarg, "\n", 1 );
- }
+ objarg = Rivet_AssignStringToConf(&(rsc->rivet_child_exit_script),string);
} else if( STREQU( script, "BeforeScript" ) ) {
- rsc->rivet_before_script = ap_pstrcat(pool, string, "\n", NULL);
+ objarg = Rivet_AssignStringToConf(&(rsc->rivet_before_script),string);
} else if( STREQU( script, "AfterScript" ) ) {
- rsc->rivet_after_script = ap_pstrcat(pool, string, "\n", NULL);
+ objarg = Rivet_AssignStringToConf(&(rsc->rivet_after_script),string);
} else if( STREQU( script, "ErrorScript" ) ) {
- rsc->rivet_error_script = ap_pstrcat(pool, string, "\n", NULL);
+ objarg = Rivet_AssignStringToConf(&(rsc->rivet_error_script),string);
+ } else if( STREQU( script, "AbortScript" ) ) {
+ objarg = Rivet_AssignStringToConf(&(rsc->rivet_abort_script),string);
+ } else if( STREQU( script, "AfterEveryScript" ) ) {
+ objarg = Rivet_AssignStringToConf(&(rsc->after_every_script),string);
}
if( !objarg ) return string;
@@ -1151,6 +1200,10 @@ Rivet_MergeDirConfigVars( pool *p, rivet
add->rivet_after_script : base->rivet_after_script;
new->rivet_error_script = add->rivet_error_script ?
add->rivet_error_script : base->rivet_error_script;
+ new->rivet_abort_script = add->rivet_abort_script ?
+ add->rivet_abort_script : base->rivet_abort_script;
+ new->after_every_script = add->after_every_script ?
+ add->after_every_script : base->after_every_script;
new->user_scripts_updated = add->user_scripts_updated ?
add->user_scripts_updated : base->user_scripts_updated;
@@ -1227,6 +1280,8 @@ Rivet_CopyConfig( rivet_server_conf *old
newrsc->rivet_before_script = oldrsc->rivet_before_script;
newrsc->rivet_after_script = oldrsc->rivet_after_script;
newrsc->rivet_error_script = oldrsc->rivet_error_script;
+ newrsc->rivet_abort_script = oldrsc->rivet_abort_script;
+ newrsc->after_every_script = oldrsc->after_every_script;
newrsc->user_scripts_updated = oldrsc->user_scripts_updated;
@@ -1259,13 +1314,15 @@ Rivet_CreateConfig( pool *p, server_rec
FILEDEBUGINFO;
- rsc->server_interp = NULL;
- rsc->rivet_global_init_script = NULL;
- rsc->rivet_child_init_script = NULL;
- rsc->rivet_child_exit_script = NULL;
- rsc->rivet_before_script = NULL;
- rsc->rivet_after_script = NULL;
- rsc->rivet_error_script = NULL;
+ rsc->server_interp = NULL;
+ rsc->rivet_global_init_script = NULL;
+ rsc->rivet_child_init_script = NULL;
+ rsc->rivet_child_exit_script = NULL;
+ rsc->rivet_before_script = NULL;
+ rsc->rivet_after_script = NULL;
+ rsc->rivet_error_script = NULL;
+ rsc->rivet_abort_script = NULL;
+ rsc->after_every_script = NULL;
rsc->user_scripts_updated = 0;
@@ -1374,6 +1431,12 @@ Rivet_MergeConfig(pool *p, void *basev,
rsc->rivet_error_script = overrides->rivet_error_script ?
overrides->rivet_error_script : base->rivet_error_script;
+ rsc->rivet_abort_script = overrides->rivet_abort_script ?
+ overrides->rivet_abort_script : base->rivet_abort_script;
+
+ rsc->after_every_script = overrides->after_every_script ?
+ overrides->after_every_script : base->after_every_script;
+
rsc->rivet_default_error_script = overrides->rivet_default_error_script ?
overrides->rivet_default_error_script : base->rivet_default_error_script;
Modified: tcl/rivet/branches/2.0/src/apache-1/mod_rivet.h
URL: http://svn.apache.org/viewvc/tcl/rivet/branches/2.0/src/apache-1/mod_rivet.h?rev=1075953&r1=1075952&r2=1075953&view=diff
==============================================================================
--- tcl/rivet/branches/2.0/src/apache-1/mod_rivet.h (original)
+++ tcl/rivet/branches/2.0/src/apache-1/mod_rivet.h Tue Mar 1 18:05:20 2011
@@ -58,9 +58,11 @@ typedef struct {
Tcl_Obj *rivet_global_init_script; /* run once when apache is started */
Tcl_Obj *rivet_child_init_script;
Tcl_Obj *rivet_child_exit_script;
- char *rivet_before_script; /* script run before each page */
- char *rivet_after_script; /* after */
- char *rivet_error_script; /* for errors */
+ Tcl_Obj *rivet_before_script; /* script run before each page */
+ Tcl_Obj *rivet_after_script; /* after */
+ Tcl_Obj *rivet_error_script; /* for errors */
+ Tcl_Obj *rivet_abort_script; /* script run upon abort_page call */
+ Tcl_Obj *after_every_script; /* script to be run always */
/* This flag is used with the above directives. If any of them
have changed, it gets set. */
@@ -90,8 +92,11 @@ typedef struct {
'de-globalize' them */
typedef struct {
- request_rec *r; /* request rec */
- TclWebRequest *req; /* TclWeb API request */
+ request_rec *r; /* request rec */
+ TclWebRequest *req; /* TclWeb API request */
+ int page_aborting; /* set by abort_page. */
+ /* to be reset by Rivet_SendContent */
+ Tcl_Obj* abort_code;
} rivet_interp_globals;
int Rivet_ParseExecFile(TclWebRequest *req, char *filename, int toplevel);
Modified: tcl/rivet/branches/2.0/src/apache-1/rivetCore.c
URL: http://svn.apache.org/viewvc/tcl/rivet/branches/2.0/src/apache-1/rivetCore.c?rev=1075953&r1=1075952&r2=1075953&view=diff
==============================================================================
--- tcl/rivet/branches/2.0/src/apache-1/rivetCore.c (original)
+++ tcl/rivet/branches/2.0/src/apache-1/rivetCore.c Tue Mar 1 18:05:20 2011
@@ -36,6 +36,8 @@
#include <tcl.h>
#include <string.h>
+#include <apr_tables.h>
+#include <apr_errno.h>
#include "apache_request.h"
#include "mod_rivet.h"
@@ -536,6 +538,257 @@ TCL_CMD_HEADER ( Rivet_Var )
/*
*/
+static int append_key_callback (void *data, const char *key, const char *val)
+{
+ Tcl_Obj *list = data;
+
+ Tcl_ListObjAppendElement (NULL, list, Tcl_NewStringObj (key, -1));
+ return 1;
+}
+
+static int
+append_key_value_callback (void *data, const char *key, const char *val)
+{
+ Tcl_Obj *list = data;
+
+ Tcl_ListObjAppendElement (NULL, list, Tcl_NewStringObj (key, -1));
+ Tcl_ListObjAppendElement (NULL, list, Tcl_NewStringObj (val, -1));
+ return 1;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Rivet_ApacheTable --
+ *
+ * Deals with Rivet key-value tables in the request structure
+ *
+ * apache_table get tablename key
+ * apache_table set tablename key value
+ * apache_table set tablename list
+ * apache_table exists tablename key
+ * apache_table unset tablename key
+ * apache_table names tablename
+ * apache_table array_get tablename
+ * apache_table clear tablename
+ *
+ * Table names can be "notes", "headers_in", "headers_out",
+ * "err_headers_out", and "subprocess_env".
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side Effects:
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+TCL_CMD_HEADER( Rivet_ApacheTable )
+{
+ table *table = NULL;
+ int subcommandindex;
+
+ static CONST84 char *SubCommand[] = {
+ "get",
+ "set",
+ "exists",
+ "unset",
+ "names",
+ "array_get",
+ "clear",
+ NULL
+ };
+
+ enum subcommand {
+ SUB_GET,
+ SUB_SET,
+ SUB_EXISTS,
+ SUB_UNSET,
+ SUB_NAMES,
+ SUB_ARRAY_GET,
+ SUB_CLEAR
+ };
+
+ static CONST84 char *tableNames[] = {
+ "notes",
+ "headers_in",
+ "headers_out",
+ "err_headers_out",
+ "subprocess_env",
+ NULL
+ };
+
+ int tableindex;
+
+ enum tablename {
+ TABLE_NOTES,
+ TABLE_HEADERS_IN,
+ TABLE_HEADERS_OUT,
+ TABLE_ERR_HEADERS_OUT,
+ TABLE_SUBPROCESS_ENV
+ };
+
+ rivet_interp_globals *globals = Tcl_GetAssocData(interp, "rivet", NULL);
+
+ if ((objc < 3) || (objc > 5)) {
+ Tcl_WrongNumArgs(interp, 1, objv, "option tablename ?args?");
+ return TCL_ERROR;
+ }
+
+ if (Tcl_GetIndexFromObj(interp, objv[1], SubCommand,
+ "get|set|unset|list",
+ 0, &subcommandindex) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+
+ if (Tcl_GetIndexFromObj (interp, objv[2], tableNames,
+ "notes|headers_in|headers_out|err_header_out|subprocess_env",
+ 0, &tableindex) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+
+ switch ((enum tablename)tableindex)
+ {
+ case TABLE_NOTES: {
+ table = globals->r->notes;
+ break;
+ }
+
+ case TABLE_HEADERS_IN: {
+ table = globals->r->headers_in;
+ break;
+ }
+
+ case TABLE_HEADERS_OUT: {
+ table = globals->r->headers_out;
+ break;
+ }
+
+ case TABLE_ERR_HEADERS_OUT: {
+ table = globals->r->err_headers_out;
+ break;
+ }
+
+ case TABLE_SUBPROCESS_ENV: {
+ table = globals->r->subprocess_env;
+ break;
+ }
+ }
+
+ switch ((enum subcommand)subcommandindex)
+ {
+ case SUB_GET: {
+ const char *key;
+ const char *value;
+
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "tablename key");
+ return TCL_ERROR;
+ }
+
+ key = Tcl_GetString (objv[3]);
+ value = ap_table_get (table, key);
+
+ if (value != NULL) {
+ Tcl_SetObjResult (interp, Tcl_NewStringObj (value, -1));
+ }
+ break;
+ }
+
+ case SUB_EXISTS: {
+ const char *key;
+ const char *value;
+
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "tablename key");
+ return TCL_ERROR;
+ }
+
+ key = Tcl_GetString (objv[3]);
+ value = ap_table_get (table, key);
+
+ Tcl_SetObjResult (interp, Tcl_NewBooleanObj (value != NULL));
+ break;
+ }
+
+
+ case SUB_SET: {
+ int i;
+ char *key;
+ char *value;
+
+ if (objc == 4) {
+ int listObjc;
+ Tcl_Obj **listObjv;
+
+ if (Tcl_ListObjGetElements (interp, objv[3], &listObjc, &listObjv) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+
+ if (listObjc % 2 == 1) {
+ Tcl_SetObjResult (interp, Tcl_NewStringObj ("list must have even number of elements", -1));
+ return TCL_ERROR;
+ }
+
+ for (i = 0; i < listObjc; i += 2) {
+ ap_table_set (table, Tcl_GetString (listObjv[i]), Tcl_GetString (listObjv[i+1]));
+ }
+
+ break;
+ }
+
+ if (objc != 5) {
+ Tcl_WrongNumArgs(interp, 2, objv, "tablename key value");
+ return TCL_ERROR;
+ }
+
+ key = Tcl_GetString (objv[3]);
+ value = Tcl_GetString (objv[4]);
+
+ ap_table_set (table, key, value);
+ break;
+ }
+
+ case SUB_UNSET: {
+ char *key;
+
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "tablename key");
+ return TCL_ERROR;
+ }
+
+ key = Tcl_GetString (objv[3]);
+ ap_table_unset (table, key);
+ break;
+ }
+
+ case SUB_NAMES: {
+ Tcl_Obj *list = Tcl_NewObj ();
+
+ ap_table_do(append_key_callback, (void*)list, table, NULL);
+
+ Tcl_SetObjResult (interp, list);
+ break;
+ }
+
+ case SUB_ARRAY_GET: {
+ Tcl_Obj *list = Tcl_NewObj ();
+
+ ap_table_do(append_key_value_callback, (void*)list, table, NULL);
+
+ Tcl_SetObjResult (interp, list);
+ break;
+ }
+
+ case SUB_CLEAR: {
+ ap_clear_table (table);
+ }
+ }
+
+ return TCL_OK;
+}
+
+
/*
*-----------------------------------------------------------------------------
*
@@ -784,14 +1037,52 @@ TCL_CMD_HEADER( Rivet_NoBody )
TCL_CMD_HEADER( Rivet_AbortPageCmd )
{
+ rivet_interp_globals *globals = Tcl_GetAssocData( interp, "rivet", NULL );
static char *errorMessage = "Page generation terminated by abort_page directive";
- if (objc != 1)
+ if (objc > 2)
{
- Tcl_WrongNumArgs(interp, 1, objv, "");
- return TCL_ERROR;
+ Tcl_WrongNumArgs(interp, 1, objv, "");
+ return TCL_ERROR;
}
+ if (objc == 2)
+ {
+ char* cmd_arg = Tcl_GetStringFromObj(objv[1],NULL);
+
+ if (strcmp(cmd_arg,"-aborting") == 0)
+ {
+ Tcl_SetObjResult (interp,Tcl_NewBooleanObj(globals->page_aborting));
+ return TCL_OK;
+ }
+
+ /*
+ * we assume abort_code to be null, as abort_page shouldn't run twice while
+ * processing the same request
+ */
+
+ if (globals->abort_code == NULL)
+ {
+ globals->abort_code = objv[1];
+ Tcl_IncrRefCount(globals->abort_code);
+ }
+ }
+
+ /*
+ * If page_aborting is true then this is the second call to abort_page
+ * processing the same request: we ignore it and return a normal
+ * completion code
+ */
+
+ if (globals->page_aborting)
+ {
+ return TCL_OK;
+ }
+
+ /* this is the first (and supposedly unique) abort_page call during this request */
+
+ globals->page_aborting = 1;
+
Tcl_AddErrorInfo (interp, errorMessage);
Tcl_SetErrorCode (interp, "RIVET", "ABORTPAGE", errorMessage, (char *)NULL);
return TCL_ERROR;
@@ -799,6 +1090,28 @@ TCL_CMD_HEADER( Rivet_AbortPageCmd )
/*
*-----------------------------------------------------------------------------
+ * Rivet_AbortCodeCmd --
+ *
+ * Returns the abort code stored internally by passing a user defined parameter
+ * to the command 'abort_page'.
+ *
+ *
+ *-----------------------------------------------------------------------------
+ */
+TCL_CMD_HEADER( Rivet_AbortCodeCmd )
+{
+ rivet_interp_globals *globals = Tcl_GetAssocData( interp, "rivet", NULL );
+
+ if (globals->abort_code != NULL)
+ {
+ Tcl_SetObjResult(interp,globals->abort_code);
+ }
+
+ return TCL_OK;
+}
+
+/*
+ *-----------------------------------------------------------------------------
*
* Rivet_EnvCmd --
*
@@ -868,6 +1181,116 @@ TCL_CMD_HEADER( Rivet_VirtualFilenameCmd
return TCL_OK;
}
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Rivet_LogError --
+ *
+ * Log an error from Rivet
+ *
+ * log_error priority message
+ *
+ * priority can be one of "emerg", "alert", "crit", "err",
+ * "warning", "notice", "info", "debug"
+ *
+ * Results:
+ * A message is logged to the Apache error log.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+TCL_CMD_HEADER( Rivet_LogErrorCmd )
+{
+ char *loglevel = NULL;
+ char *message = NULL;
+
+ server_rec *serverRec;
+
+ int loglevelindex;
+ int apLogLevel = 0;
+
+ static CONST84 char *logLevel[] = {
+ "emerg",
+ "alert",
+ "crit",
+ "err",
+ "warning",
+ "notice",
+ "info",
+ "debug",
+ NULL
+ };
+
+ enum loglevel {
+ EMERG,
+ ALERT,
+ CRIT,
+ ERR,
+ WARNING,
+ NOTICE,
+ INFO,
+ DEBUG
+ };
+
+ rivet_interp_globals *globals = Tcl_GetAssocData(interp, "rivet", NULL);
+
+ if( objc != 3 ) {
+ Tcl_WrongNumArgs( interp, 1, objv, "loglevel message" );
+ return TCL_ERROR;
+ }
+
+ loglevel = Tcl_GetString(objv[1]);
+ message = Tcl_GetString (objv[2]);
+ if (Tcl_GetIndexFromObj(interp, objv[1], logLevel,
+ "emerg|alert|crit|err|warning|notice|info|debug",
+ 0, &loglevelindex) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+
+ switch ((enum loglevel)loglevelindex)
+ {
+ case EMERG:
+ apLogLevel = APLOG_EMERG;
+ break;
+
+ case ALERT:
+ apLogLevel = APLOG_ALERT;
+ break;
+
+ case CRIT:
+ apLogLevel = APLOG_CRIT;
+ break;
+
+ case ERR:
+ apLogLevel = APLOG_ERR;
+ break;
+
+ case WARNING:
+ apLogLevel = APLOG_WARNING;
+ break;
+
+ case NOTICE:
+ apLogLevel = APLOG_NOTICE;
+ break;
+
+ case INFO:
+ apLogLevel = APLOG_INFO;
+ break;
+
+ case DEBUG:
+ default:
+ apLogLevel = APLOG_DEBUG;
+ break;
+ }
+
+ /* if we are serving a page, we know our server,
+ * else send null for server
+ */
+ serverRec = (globals->r == NULL) ? NULL : globals->r->server;
+
+ ap_log_error (APLOG_MARK, apLogLevel, serverRec, "%s", message);
+ return TCL_OK;
+}
#define TESTPANIC 0
#ifdef TESTPANIC
@@ -993,13 +1416,23 @@ Rivet_InitCore( Tcl_Interp *interp )
Rivet_EnvCmd,
NULL,
(Tcl_CmdDeleteProc *)NULL);
-
+ Tcl_CreateObjCommand(interp,
+ "apache_log_error",
+ Rivet_LogErrorCmd,
+ NULL,
+ (Tcl_CmdDeleteProc *)NULL);
+ Tcl_CreateObjCommand(interp,
+ "apache_table",
+ Rivet_ApacheTable,
+ NULL,
+ (Tcl_CmdDeleteProc *)NULL);
#ifdef TESTPANIC
Tcl_CreateCommand(interp, "testpanic", TestpanicCmd, (ClientData) 0,
(Tcl_CmdDeleteProc *) NULL);
#endif
TCL_OBJ_CMD( "abort_page", Rivet_AbortPageCmd );
+ TCL_OBJ_CMD( "abort_code", Rivet_AbortCodeCmd );
TCL_OBJ_CMD( "virtual_filename", Rivet_VirtualFilenameCmd );
/*
Modified: tcl/rivet/branches/2.0/src/apache-2/mod_rivet.c
URL: http://svn.apache.org/viewvc/tcl/rivet/branches/2.0/src/apache-2/mod_rivet.c?rev=1075953&r1=1075952&r2=1075953&view=diff
==============================================================================
--- tcl/rivet/branches/2.0/src/apache-2/mod_rivet.c (original)
+++ tcl/rivet/branches/2.0/src/apache-2/mod_rivet.c Tue Mar 1 18:05:20 2011
@@ -345,6 +345,15 @@ Rivet_ExecuteAndCheck(Tcl_Interp *interp
errorCodeSubString = Tcl_GetString (errorCodeElementObj);
if (strcmp (errorCodeSubString, "ABORTPAGE") == 0) {
+ if (conf->rivet_abort_script)
+ {
+ if (Tcl_EvalObjEx(interp,conf->rivet_abort_script,0) == TCL_ERROR)
+ {
+ CONST84 char *errorinfo = Tcl_GetVar( interp, "errorInfo", 0 );
+ TclWeb_PrintError("<b>Rivet ErrorScript failed!</b>",1,globals->req);
+ TclWeb_PrintError( errorinfo, 0, globals->req );
+ }
+ }
goto good;
}
}
@@ -355,7 +364,7 @@ Rivet_ExecuteAndCheck(Tcl_Interp *interp
/* If we don't have an error script, use the default error handler. */
if (conf->rivet_error_script ) {
- errscript = Tcl_NewStringObj(conf->rivet_error_script, -1);
+ errscript = conf->rivet_error_script;
} else {
errscript = conf->rivet_default_error_script;
}
@@ -376,6 +385,15 @@ Rivet_ExecuteAndCheck(Tcl_Interp *interp
/* Make sure to flush the output if buffer_add was the only output */
good:
+ if (conf->after_every_script) {
+ if (Tcl_EvalObjEx(interp,conf->after_every_script,0) == TCL_ERROR)
+ {
+ CONST84 char *errorinfo = Tcl_GetVar( interp, "errorInfo", 0 );
+ TclWeb_PrintError("<b>Rivet AfterEveryScript failed!</b>",1,globals->req);
+ TclWeb_PrintError( errorinfo, 0, globals->req );
+ }
+ }
+
if (!globals->req->headers_set && (globals->req->charset != NULL)) {
TclWeb_SetHeaderType (apr_pstrcat(globals->req->req->pool,"text/html;",globals->req->charset,NULL),globals->req);
}
@@ -476,8 +494,7 @@ Rivet_ParseExecFile(TclWebRequest *req,
if (toplevel) {
if (rsc->rivet_before_script) {
- Tcl_AppendObjToObj(outbuf,
- Tcl_NewStringObj(rsc->rivet_before_script, -1));
+ Tcl_AppendObjToObj(outbuf,rsc->rivet_before_script);
}
}
@@ -504,7 +521,7 @@ Rivet_ParseExecFile(TclWebRequest *req,
}
if (toplevel) {
if (rsc->rivet_after_script) {
- Tcl_AppendObjToObj(outbuf,Tcl_NewStringObj(rsc->rivet_after_script, -1));
+ Tcl_AppendObjToObj(outbuf,rsc->rivet_after_script);
}
}
@@ -623,10 +640,11 @@ Rivet_CopyConfig( rivet_server_conf *old
newrsc->server_interp = oldrsc->server_interp;
newrsc->rivet_global_init_script = oldrsc->rivet_global_init_script;
-
newrsc->rivet_before_script = oldrsc->rivet_before_script;
newrsc->rivet_after_script = oldrsc->rivet_after_script;
newrsc->rivet_error_script = oldrsc->rivet_error_script;
+ newrsc->rivet_abort_script = oldrsc->rivet_abort_script;
+ newrsc->after_every_script = oldrsc->after_every_script;
newrsc->user_scripts_updated = oldrsc->user_scripts_updated;
@@ -667,6 +685,10 @@ Rivet_MergeDirConfigVars(apr_pool_t *p,
add->rivet_after_script : base->rivet_after_script;
new->rivet_error_script = add->rivet_error_script ?
add->rivet_error_script : base->rivet_error_script;
+ new->rivet_abort_script = add->rivet_abort_script ?
+ add->rivet_abort_script : base->rivet_abort_script;
+ new->after_every_script = add->after_every_script ?
+ add->after_every_script : base->after_every_script;
new->user_scripts_updated = add->user_scripts_updated ?
add->user_scripts_updated : base->user_scripts_updated;
@@ -722,13 +744,15 @@ Rivet_CreateConfig(apr_pool_t *p, server
FILEDEBUGINFO;
- rsc->server_interp = NULL;
- rsc->rivet_global_init_script = NULL;
- rsc->rivet_child_init_script = NULL;
- rsc->rivet_child_exit_script = NULL;
- rsc->rivet_before_script = NULL;
- rsc->rivet_after_script = NULL;
- rsc->rivet_error_script = NULL;
+ rsc->server_interp = NULL;
+ rsc->rivet_global_init_script = NULL;
+ rsc->rivet_child_init_script = NULL;
+ rsc->rivet_child_exit_script = NULL;
+ rsc->rivet_before_script = NULL;
+ rsc->rivet_after_script = NULL;
+ rsc->rivet_error_script = NULL;
+ rsc->rivet_abort_script = NULL;
+ rsc->after_every_script = NULL;
rsc->user_scripts_updated = 0;
@@ -858,6 +882,13 @@ Rivet_PerInterpInit(server_rec *s, rivet
globals = apr_pcalloc(p, sizeof(rivet_interp_globals));
Tcl_SetAssocData(interp, "rivet", NULL, globals);
+ /* abort_page status variables in globals are set here and then
+ * reset in Rivet_SendContent just before the request processing is
+ * completed */
+
+ globals->page_aborting = 0;
+ globals->abort_code = NULL;
+
/* Eval Rivet's init.tcl file to load in the Tcl-level
commands. */
@@ -888,6 +919,45 @@ Rivet_PerInterpInit(server_rec *s, rivet
Tcl_Release(interp);
}
+/*
+ *----------------------------------------------------------------------
+ *
+ * Rivet_AssignStringtoConf --
+ *
+ * Assign a string to a Tcl_Obj valued configuration parameter
+ *
+ * Arguments:
+ *
+ * - objPnt: Pointer to a pointer to a Tcl_Obj. If the pointer *objPnt
+ * is NULL (configuration script obj pointers are initialized to NULL)
+ * a new Tcl_Obj is created
+ * - string_value: a string to be assigned to the Tcl_Obj
+ *
+ * Results:
+ *
+ * - Pointer to a Tcl_Obj containing the parameter value.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_Obj*
+Rivet_AssignStringToConf (Tcl_Obj** objPnt, const char* string_value)
+{
+ Tcl_Obj *objarg = NULL;
+
+ if (*objPnt == NULL)
+ {
+ objarg = Tcl_NewStringObj(string_value,-1);
+ Tcl_IncrRefCount(objarg);
+ *objPnt = objarg;
+ } else {
+ objarg = *objPnt;
+ Tcl_AppendToObj(objarg, string_value, -1);
+ }
+ Tcl_AppendToObj( objarg, "\n", 1 );
+ return objarg;
+}
+
/*
*----------------------------------------------------------------------
@@ -915,46 +985,24 @@ Rivet_SetScript(apr_pool_t *pool, rivet_
Tcl_Obj *objarg = NULL;
if( STREQU( script, "GlobalInitScript" ) ) {
- if( rsc->rivet_global_init_script == NULL ) {
- objarg = Tcl_NewStringObj( string, -1 );
- Tcl_IncrRefCount( objarg );
- Tcl_AppendToObj( objarg, "\n", 1 );
- rsc->rivet_global_init_script = objarg;
- } else {
- objarg = rsc->rivet_global_init_script;
- Tcl_AppendToObj( objarg, string, -1 );
- Tcl_AppendToObj( objarg, "\n", 1 );
- }
+ objarg = Rivet_AssignStringToConf(&(rsc->rivet_global_init_script),string);
} else if( STREQU( script, "ChildInitScript" ) ) {
- if( rsc->rivet_child_init_script == NULL ) {
- objarg = Tcl_NewStringObj( string, -1 );
- Tcl_IncrRefCount( objarg );
- Tcl_AppendToObj( objarg, "\n", 1 );
- rsc->rivet_child_init_script = objarg;
- } else {
- objarg = rsc->rivet_child_init_script;
- Tcl_AppendToObj( objarg, string, -1 );
- Tcl_AppendToObj( objarg, "\n", 1 );
- }
+ objarg = Rivet_AssignStringToConf(&(rsc->rivet_child_init_script),string);
} else if( STREQU( script, "ChildExitScript" ) ) {
- if( rsc->rivet_child_exit_script == NULL ) {
- objarg = Tcl_NewStringObj( string, -1 );
- Tcl_IncrRefCount( objarg );
- Tcl_AppendToObj( objarg, "\n", 1 );
- rsc->rivet_child_exit_script = objarg;
- } else {
- objarg = rsc->rivet_child_exit_script;
- Tcl_AppendToObj( objarg, string, -1 );
- Tcl_AppendToObj( objarg, "\n", 1 );
- }
+ objarg = Rivet_AssignStringToConf(&(rsc->rivet_child_exit_script),string);
} else if( STREQU( script, "BeforeScript" ) ) {
- rsc->rivet_before_script = apr_pstrcat(pool, string, "\n", NULL);
+ objarg = Rivet_AssignStringToConf(&(rsc->rivet_before_script),string);
} else if( STREQU( script, "AfterScript" ) ) {
- rsc->rivet_after_script = apr_pstrcat(pool, string, "\n", NULL);
+ objarg = Rivet_AssignStringToConf(&(rsc->rivet_after_script),string);
} else if( STREQU( script, "ErrorScript" ) ) {
- rsc->rivet_error_script = apr_pstrcat(pool, string, "\n", NULL);
+ objarg = Rivet_AssignStringToConf(&(rsc->rivet_error_script),string);
+ } else if( STREQU( script, "AbortScript" ) ) {
+ objarg = Rivet_AssignStringToConf(&(rsc->rivet_abort_script),string);
+ } else if( STREQU( script, "AfterEveryScript" ) ) {
+ objarg = Rivet_AssignStringToConf(&(rsc->after_every_script),string);
}
+
if( !objarg ) return string;
return Tcl_GetStringFromObj( objarg, NULL );
@@ -1173,6 +1221,12 @@ Rivet_MergeConfig(apr_pool_t *p, void *b
rsc->rivet_default_error_script = overrides->rivet_default_error_script ?
overrides->rivet_default_error_script : base->rivet_default_error_script;
+ rsc->rivet_abort_script = overrides->rivet_abort_script ?
+ overrides->rivet_abort_script : base->rivet_abort_script;
+
+ rsc->after_every_script = overrides->after_every_script ?
+ overrides->after_every_script : base->after_every_script;
+
/* cache_size is global, and set up later. */
/* cache_free is not set up at this point. */
@@ -1719,6 +1773,15 @@ Rivet_SendContent(request_rec *r)
retval = OK;
sendcleanup:
globals->req->content_sent = 0;
+
+ /* page_aborting and abort_code are reset at every request */
+
+ globals->page_aborting = 0;
+ if (globals->abort_code != NULL)
+ {
+ Tcl_DecrRefCount(globals->abort_code);
+ globals->abort_code = NULL;
+ }
Tcl_MutexUnlock(&sendMutex);
return retval;
}
Modified: tcl/rivet/branches/2.0/src/apache-2/mod_rivet.h
URL: http://svn.apache.org/viewvc/tcl/rivet/branches/2.0/src/apache-2/mod_rivet.h?rev=1075953&r1=1075952&r2=1075953&view=diff
==============================================================================
--- tcl/rivet/branches/2.0/src/apache-2/mod_rivet.h (original)
+++ tcl/rivet/branches/2.0/src/apache-2/mod_rivet.h Tue Mar 1 18:05:20 2011
@@ -55,9 +55,11 @@ typedef struct _rivet_server_conf {
Tcl_Obj *rivet_global_init_script; /* run once when apache is started */
Tcl_Obj *rivet_child_init_script;
Tcl_Obj *rivet_child_exit_script;
- char *rivet_before_script; /* script run before each page */
- char *rivet_after_script; /* after */
- char *rivet_error_script; /* for errors */
+ Tcl_Obj *rivet_before_script; /* script run before each page */
+ Tcl_Obj *rivet_after_script; /* after */
+ Tcl_Obj *rivet_error_script; /* for errors */
+ Tcl_Obj *rivet_abort_script; /* script run upon abort_page call */
+ Tcl_Obj *after_every_script; /* script to be run always */
/* This flag is used with the above directives. If any of them
have changed, it gets set. */
@@ -85,8 +87,11 @@ typedef struct _rivet_server_conf {
'de-globalize' them */
typedef struct _rivet_interp_globals {
- request_rec *r; /* request rec */
- TclWebRequest *req; /* TclWeb API request */
+ request_rec *r; /* request rec */
+ TclWebRequest *req; /* TclWeb API request */
+ int page_aborting; /* set by abort_page. */
+ /* to be reset by Rivet_SendContent */
+ Tcl_Obj* abort_code;
} rivet_interp_globals;
int Rivet_ParseExecFile(TclWebRequest *req, char *filename, int toplevel);
Modified: tcl/rivet/branches/2.0/src/apache-2/rivetCore.c
URL: http://svn.apache.org/viewvc/tcl/rivet/branches/2.0/src/apache-2/rivetCore.c?rev=1075953&r1=1075952&r2=1075953&view=diff
==============================================================================
--- tcl/rivet/branches/2.0/src/apache-2/rivetCore.c (original)
+++ tcl/rivet/branches/2.0/src/apache-2/rivetCore.c Tue Mar 1 18:05:20 2011
@@ -1043,14 +1043,52 @@ TCL_CMD_HEADER( Rivet_NoBody )
TCL_CMD_HEADER( Rivet_AbortPageCmd )
{
+ rivet_interp_globals *globals = Tcl_GetAssocData( interp, "rivet", NULL );
static char *errorMessage = "Page generation terminated by abort_page directive";
- if (objc != 1)
+ if (objc > 2)
{
- Tcl_WrongNumArgs(interp, 1, objv, "");
- return TCL_ERROR;
+ Tcl_WrongNumArgs(interp, 1, objv, "");
+ return TCL_ERROR;
+ }
+
+ if (objc == 2)
+ {
+ char* cmd_arg = Tcl_GetStringFromObj(objv[1],NULL);
+
+ if (strcmp(cmd_arg,"-aborting") == 0)
+ {
+ Tcl_SetObjResult (interp,Tcl_NewBooleanObj(globals->page_aborting));
+ return TCL_OK;
+ }
+
+ /*
+ * we assume abort_code to be null, as abort_page shouldn't run twice while
+ * processing the same request
+ */
+
+ if (globals->abort_code == NULL)
+ {
+ globals->abort_code = objv[1];
+ Tcl_IncrRefCount(globals->abort_code);
+ }
+ }
+
+ /*
+ * If page_aborting is true then this is the second call to abort_page
+ * processing the same request: we ignore it and return a normal
+ * completion code
+ */
+
+ if (globals->page_aborting)
+ {
+ return TCL_OK;
}
+ /* this is the first (and supposedly unique) abort_page call during this request */
+
+ globals->page_aborting = 1;
+
Tcl_AddErrorInfo (interp, errorMessage);
Tcl_SetErrorCode (interp, "RIVET", "ABORTPAGE", errorMessage, (char *)NULL);
return TCL_ERROR;
@@ -1058,6 +1096,28 @@ TCL_CMD_HEADER( Rivet_AbortPageCmd )
/*
*-----------------------------------------------------------------------------
+ * Rivet_AbortCodeCmd --
+ *
+ * Returns the abort code stored internally by passing a user defined parameter
+ * to the command 'abort_page'.
+ *
+ *
+ *-----------------------------------------------------------------------------
+ */
+TCL_CMD_HEADER( Rivet_AbortCodeCmd )
+{
+ rivet_interp_globals *globals = Tcl_GetAssocData( interp, "rivet", NULL );
+
+ if (globals->abort_code != NULL)
+ {
+ Tcl_SetObjResult(interp,globals->abort_code);
+ }
+
+ return TCL_OK;
+}
+
+/*
+ *-----------------------------------------------------------------------------
*
* Rivet_EnvCmd --
*
@@ -1393,6 +1453,7 @@ Rivet_InitCore( Tcl_Interp *interp )
#endif
TCL_OBJ_CMD( "abort_page", Rivet_AbortPageCmd );
+ TCL_OBJ_CMD( "abort_code", Rivet_AbortCodeCmd );
TCL_OBJ_CMD( "virtual_filename", Rivet_VirtualFilenameCmd );
return TCL_OK;
---------------------------------------------------------------------
To unsubscribe, e-mail: rivet-cvs-unsubscribe@tcl.apache.org
For additional commands, e-mail: rivet-cvs-help@tcl.apache.org