Index: mod_nsd/CAVEATS
===================================================================
RCS file: /usr/local/cvsroot/mod_nsd/Attic/CAVEATS,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ mod_nsd/CAVEATS 13 Apr 2001 21:09:32 -0000 1.1
@@ -0,0 +1,148 @@
+This file is obsolete. See README.html for a more up-to-date list.
+
+DANGER --- ns_set_clear in ns_set.c breaks with Apache ALLOC_USE_MALLOC.
+
+WARNING --- this will open a lot more connections to your database
+ than the real aolserver, since each server process (and the
+ scheduler process) has a separate copy of the pools.
+
+
+Tcl incompatibilities and limitations, in stuff which isn't just
+absent; I don't detail those because I *think* I've included every
+function which is actually invoked by ACS. For a list of functions
+which we have simply ditched, see tclcmds.c; it includes functions
+that deal with server sockets (ns_socklisten, ns_accept, etc.), all
+the cache stuff, the thread creation and synchronization primitives
+(ns_thread, ns_mutex, ns_cond, etc.), anything dealing with the
+control socket (ns_cport, etc.), and a hodgepodge of system-level
+miscellany (ns_rollfile, ns_fork, etc.), and probably other stuff
+I've just forgotten about.
+
+(Well, actually, ns_mutex isn't technically absent; it's replaced
+with a no-op stub which ignores all arguments --- this is necessary
+to get ACS running, and I do it there only because I really think
+the mutex isn't needed there anyway, so long as filters aren't being
+redefined after configuration --- and if they are, we have bigger
+problems).
+
+I do not discuss limitations in the C API (other than to briefly state
+that the thread synchronization stuff doesn't exist there either, and
+is generally #ifdeffed out in the code I've copied over); for that,
+you're on your own, though database drivers that don't mess with the
+innards of ns_sets *shouldn't* be much work.
+
+General: At server restart, all state is reinitialized from scratch.
+ Accordingly, "started_p" flags should not be necessary.
+
+ On the other hand, restart currently just blows up anyway
+ trying to unload the shared libraries, so this is at least
+ momentarily a non-issue.
+
+ns_schedule_XXX --- only works during startup. Note that, as discussed
+ below, mod_aolserver shares a lot less between threads than the
+ real thing, and restricts many more functions to run only during
+ server startup.
+
+ To get around this, any procedure scheduled as:
+
+ ns_schedule_proc -once secs my_startup_func
+
+ where "secs" is less than 120 (two minutes) will actually be run
+ in the parent process, after the database is configured (so they
+ can read the database), but before any child processes are created
+ (so, e.g., ns_register_filter/proc will still work). These will
+ be run in the order of the scheduled intervals given, so if we have
+
+ ns_schedule_proc -once 30 initialize_hairy_stuff
+ ns_schedule_proc -once 2 initialize_really_basic_stuff
+
+ "initialize_really_basic_stuff" is guaranteed to run first,
+ no matter in what order the two were scheduled.
+
+ [Yes, this does involve the parent process closing all database
+ handles, and the children reopening them...]
+
+ ns_cancel, ns_pause, ns_resume, ns_unschedule_proc are not
+ supported. Supporting these, and supporting ns_schedule_proc
+ in response to web requests, would not actually be *terribly* hard
+ (we could mirror the list of procs in an nsv array, and have the
+ scheduler reload it on a signal --- this wouldn't happen often
+ enough for performance to be an issue), but I don't *think* there's
+ a pressing need.
+
+ns_register_filter/proc --- Only work during configuration, or during
+ "scheduled startup procedures". Fixing this would be real work, though
+ not impossible; our nsv implementation isn't nearly fast enough to
+ perform acceptably for access to the shared state, so we'd have
+ to do something with Engelschall's MM. (And no, an MM-based nsv
+ isn't worth it just for the sake of this; Berkeley db would be just
+ so much easier).
+
+ Preauth filters run before postauth filters, but after Apache auth.
+
+ns_share --- ns_share variables do persist across requests, and are
+ inherited from configuration (and "scheduled startup procedures")
+ by request-handling threads, and the scheduler thread. However,
+ they are *not* shared among interpreters; for that, use nsv.
+
+ Some of the alerter and memoization stuff is known to trip over
+ this.
+
+nsv: Only supported commands are nsv_get, nsv_set, nsv_unset, nsv_exists,
+ nsv_array names, nsv_array exists, nsv_append. Each one opens and closes a
+ GDBM file, which means they're a lot slower than they have to be;
+ I don't *think* this is likely to be a bottleneck, but we'll see.
+
+ (If it is, we should switch to Berkeley db, which does the internal
+ locking necessary to support multiple writers simultaneously; that
+ should be adequate for anything that isn't trying to use these as
+ a substitute for ordinary tcl variables).
+
+ns_info --- Pageroot is taken from aolerver config, not Apache config.
+
+ ns_info log is a potentially inaccurate guess; if the
+ administrator has configured pipe logs, a physical log file may
+ not even *exist*...
+
+ADP --- no debugging support; fancy parser is the only one supported
+ (don't bother trying to declare others in the config file);
+ its features should work, though (register_tag and ns_adp_parse
+ are tested).
+
+ns_conn --- the authpasswd, contentlength, and protocol subcommands are not
+ supported.
+
+ ns_conn url takes off PATH_INFO. This may be incompatible.
+
+ns_url2file --- again, takes off PATH_INFO. This may be incompatible.
+
+ns_server --- active subcommand is presently stubbed, always returning "".
+ Other subcommands are unsupported.
+
+ We could use Apache's extended status features to do this *about* right;
+ long URLs would be clipped. (With a higher-performance nsv implement-
+ ation, we could also do it for ourselves on the side).
+
+ns_atclose --- Does not work in scheduled procedures (though this would
+ be trivial to add if needed).
+
+ns_configsections --- not supported.
+ns_configsection --- the set implementing the config section for ns/foo/bar
+ won't show up as the bar element of ns_configsection foo
+
+ns_set --- ns_set -persist is only supported during configuration
+ ns_set split is not supported.
+
+ns_returnfile --- does not do if-modified-since. This is a bug.
+
+ns_geturl --- Just invokes ns_httpget. Does not return headers; does
+ follow redirects; will time out. There is one invocation
+ of this in ACS; I'd like to see it ditched and drop the
+ one line of support, just to nuke the redundancy.
+
+ns_modlog --- Everything goes to the error log; you can't be specific
+ about facilities and logging thresholds, though they are
+ put into the log. The command is stubbed and does nothing.
+
+ns_checkurl/ns_requestauthorize: stubbed. The commands do nothing, and
+ always return OK.
Index: mod_nsd/CHANGES
===================================================================
RCS file: /usr/local/cvsroot/mod_nsd/CHANGES,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ mod_nsd/CHANGES 13 Apr 2001 21:09:32 -0000 1.1
@@ -0,0 +1,29 @@
+
+/*- changes since release-1_1 above this line ------------------------*/
+
+Upgraded ora8.c to oracle driver 2.3.
+
+Added support for all nsv_array subcommands.
+
+Lightly tested with ACS 3.4.5.
+
+/*- changes since release-0_9_5 above this line ----------------------*/
+
+README.html: misc. updates
+
+configure.in, aclocal.m4, configure, Makefile.in: find and link MM library
+
+dbdrv.c: call Ns_SetClearValuesOnPutValue to reduce garbage buildup
+
+ns_set.c, ns_set.h, tclset.c: general cleanup & garbage buildup reduction
+
+mod_aolserver.c, nsv.c, mm_hash.h, mm_hash.c, apache.tcl,
+shared_vars.tcl: new nsv_*, ns_mutex implementation
+
+mod_aolserver.c: load libpthread on linux to work around oracle SIGCHLD
+ problem
+
+nsd.ini.sample: cleaned up & removed obsolete stuff
+
+/*- changes since release-0_9 above this line ------------------------*/
+
Index: mod_nsd/Makefile.in
===================================================================
RCS file: /usr/local/cvsroot/mod_nsd/Makefile.in,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ mod_nsd/Makefile.in 13 Apr 2001 21:09:32 -0000 1.1
@@ -0,0 +1,109 @@
+# $Header: /usr/local/cvsroot/mod_nsd/Makefile.in,v 1.1 2001/04/13 21:09:32 ppetru Exp $
+
+SHELL = /bin/sh
+
+# values determined by configure
+
+APACHE_BASE = @APACHE_BASE@
+APXS = @APXS@
+CC = @CC@
+CFLAGS = -I. @CFLAGS@ -DORA8_DRIVER_VERSION=\"2.3\"
+CPPFLAGS = -DDB_DRIVER_NAME=\"$(DBNAME)\" @CPPFLAGS@
+DBNAME = @DBNAME@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+LIBS = @LIBS@ @TCL_LIB_SPEC@ @TCL_LIBS@
+TCL_SHLIB_SUFFIX = @TCL_SHLIB_SUFFIX@
+
+
+# derived compile variables
+
+DB_DRIVER = $(DBNAME).c
+
+# derived install variables
+
+BOOTSTRAP_MAIN_DIR = $(APACHE_BASE)/libexec
+BOOTSTRAP_OTHER_DIR = $(BOOTSTRAP_MAIN_DIR)/aol_bootstrap
+TCL_MODULE_DIR = $(APACHE_BASE)/tcl_modules
+
+################################################################
+
+MODULE_FILE = mod_aolserver$(TCL_SHLIB_SUFFIX)
+
+HDRS = \
+ adp.h conn.h dstring.h mod_aolserver.h modlog.h ns_set.h nsd.h \
+ str.h tls.h config.h db.h ns.h config.h ns_basics.h assert.h mm_hash.h
+
+MAS_SRCS = \
+ mod_aolserver.c ns_set.c tclset.c dstring.c tls.c \
+ modlog.c conn.c misc.c adp.c adpfancy.c str.c tclcmds.c tclmisc.c \
+ crypt.c htuu.c tclfile.c tclsock.c sock.c urlencode.c dbinit.c \
+ dbdrv.c dbutil.c dbtcl.c config.c quotehtml.c random.c mm_hash.c nsv.c
+
+DB_SRCS = postgres.c ora8.c
+
+BOOTSTRAP_MAIN = aol_bootstrap.tcl
+
+BOOTSTRAP_OTHER = \
+ aol_bootstrap/README \
+ aol_bootstrap/Init_hooks.tcl \
+ aol_bootstrap/Init_slave.tcl \
+ aol_bootstrap/apache.tcl \
+ aol_bootstrap/config.tcl \
+ aol_bootstrap/dbsupport.tcl \
+ aol_bootstrap/files.tcl \
+ aol_bootstrap/misc.tcl \
+ aol_bootstrap/regfilter.tcl \
+ aol_bootstrap/regproc.tcl \
+ aol_bootstrap/sched.tcl \
+ aol_bootstrap/shared_vars.tcl \
+ aol_bootstrap/status.tcl
+
+TCL_SRCS = \
+ tcl_modules/README tcl_modules/form.tcl \
+ tcl_modules/http.tcl tcl_modules/nsd.ini.sample tcl_modules/nsdb.tcl \
+ tcl_modules/sendmail.tcl tcl_modules/util.tcl
+
+MISCFILES = \
+ Makefile.in configure.in aclocal.m4 configure TODO CAVEATS README
+
+DISTFILES = \
+ $(MAS_SRCS) $(DB_SRCS) $(BOOTSTRAP_SRCS) $(TCL_SRCS) $(HDRS) \
+ $(MISCFILES)
+
+SRCS = $(MAS_SRCS) $(DB_DRIVER)
+
+OBJS = $(SRCS:.c=.o)
+
+BUILD_MODULE = $(APXS) -c $(LIBS)
+
+INSERT_MODULE = $(APXS) -i -a
+
+########################################################################
+
+all: $(MODULE_FILE) install
+
+$(MODULE_FILE): $(OBJS)
+ $(APXS) -c -o $@ $(OBJS) $(LIBS)
+
+install: install-mod_aolserver install-bootstrap install-tcl
+
+install-mod_aolserver: mod_aolserver$(TCL_SHLIB_SUFFIX)
+ @$(INSERT_MODULE) mod_aolserver$(TCL_SHLIB_SUFFIX)
+
+install-bootstrap: $(BOOTSTRAP_MAIN) $(BOOTSTRAP_OTHER)
+ $(INSTALL_DATA) $(BOOTSTRAP_MAIN) $(BOOTSTRAP_MAIN_DIR)
+ mkdir -p $(BOOTSTRAP_OTHER_DIR)
+ cp $(BOOTSTRAP_OTHER) $(BOOTSTRAP_OTHER_DIR)
+
+install-tcl: $(TCL_SRCS)
+ mkdir -p $(TCL_MODULE_DIR)
+ cp $(TCL_SRCS) $(TCL_MODULE_DIR)
+
+clean:
+ rm -f *.o $(MODULE_FILE)
+
+mod_aolserver.tgz: $(DISTFILES)
+ tar cvzf mod_aolserver.tgz $(DISTFILES)
+
Index: mod_nsd/README.html
===================================================================
RCS file: /usr/local/cvsroot/mod_nsd/Attic/README.html,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ mod_nsd/README.html 13 Apr 2001 21:09:32 -0000 1.1
@@ -0,0 +1,451 @@
+
+
+ README for mod_aolserver
+
+
+ README for mod_aolserver
+ an Apache module by Robert
+ S. Thau and Rob Mayoff
+
+
+This is mod_aolserver release 1.1, an Apache module that emulates enough
+of the AOLserver API to run the ArsDigita Community System.
+You can try out an ACS installation running on this module at http://apache.arsdigita-dev.com/.
+That system has bulletin boards for discussing mod_aolserver.
+
+
+
+
+
+In order to use this software, you will need these other packages:
+
+
+
+ - Apache 1.3.12 or later in the 1.3 series. This module has not
+ been developed for or tested with any Apache 2.0 release.
+
+
- Tcl 8.1 or later. Tcl 8.3 is recommended because that is what
+ AOLserver 3.0 uses and ACS authors may take advantage of Tcl 8.3
+ features.
+
+
- MM Library. This is a portable shared memory
+ library.
+
+
- Either Oracle 8i or PostgreSQL.
+
+
+
+You must install these packages before compiling mod_aolserver.
+
+Apache
+
+Apache is available here.
+
+We developed mod_aolserver as a dynamic shared object, so you must
+compile/install Apache with mod_so built in. You can determine
+whether your Apache has this module by running this command:
+
+
httpd -l | grep mod_so
+
+If the output is a line that says mod_so.c
, then your
+Apache has the required module.
+
+We test with Apache 1.3.12 so you should try to use that or a later
+version.
+
+
Tcl
+
+Tcl is available here.
+
+AOLserver uses Tcl 8.3, so that's what I use to build mod_aolserver.
+It will probably work with Tcl 8.1 or later, but ACS might (now or in
+the future) use 8.2 or 8.3 features. Whichever version you use, make
+sure you build this with --enable-shared
.
+
+
Warning: Red Hat Linux, through version 6.2 at least, only includes
+Tcl 8.0, so if you're on a Red Hat system, you must install a later
+version. You do not need to uninstall the Tcl RPM, though; you can
+install Tcl 8.3 somewhere else for mod_aolserver's use.
+
+
MM
+
+The MM library is available
+here.
+
+AOLserver provides various APIs for threads to share data and
+synchronize their operations. Although Apache is not threaded, we
+emulate some of those APIs. The mechanism we use to share data between
+processes is Ralf Engelschall's MM.
+
+
We tested with MM version 1.1.1.
+
+
Note: if you are using Linux, MM will by default use System V style
+SHM shared memory. This work fine. Do not try to force it to use
+MAP_ANON shared memory. I had strange problems when I did that.
+
+
Oracle 8i
+
+Oracle 8i is available here.
+
+The ArsDigita Community
+System is built on top of Oracle 8i. mod_aolserver includes a
+version of ArsDigita's Oracle driver. If you want to run the ACS
+on mod_aolserver, you'll need Oracle 8i installed before compiling
+mod_aolserver.
+
+
As of release 1.1, mod_aolserver uses ArsDigita's AOLserver Oracle
+driver release 2.3.
+
+
PostgreSQL
+
+PostgreSQL is available here.
+
+An intrepid group of programmers are porting the ACS to PostgreSQL;
+they call the project "OpenACS". You can download that version from
+their development site.
+mod_aolserver includes a version of the PostgreSQL database driver.
+If you want to run OpenACS on mod_aolserver, you'll need PostgreSQL
+installed before compiling mod_aolserver.
+
+
+
+mod_aolserver uses GNU autoconf to configure the build process. You can
+type ./configure --help
to see your options. The important
+ones are these:
+
+
+
+
+
--with-database=dbname
+ (Required.)
+ You must include this flag when running configure.
+ dbname must be either ora8
or
+ postgres
. (mod_aolserver currently compiles in exactly
+ one database driver and does not support dynamically-loaded
+ drivers.)
+
+
+
--with-ora8=dir
+ (Optional.)
+ Normally you set $ORACLE_HOME to your Oracle installation directory.
+ If for some reason you want to specify it on the configure command
+ line instead, use this flag.
+
+
+
--with-postgres=dir
+ (Optional.)
+ If the PostgreSQL headers and libraries aren't in locations that the
+ compiler will find automatically, or in /usr/local
,
+ you can use this flag to specify
+ the PostgreSQL installation directory. The headers should be in
+ dir/include
and the libraries in
+ dir/lib
, of course.
+
+
+
--with-apxs=dir
+ (Optional.)
+ Use this flag to specify the full path to the copy of
+ apxs
you want to use to build and install this module,
+ for example /u/mayoff/test/bin/apxs
.
+ By default, configure will use the first copy of apxs
+ in your PATH.
+
+
+
--with-tcl=dir
+ (Optional.)
+ You may include this flag if you've installed Tcl in a non-standard
+ location. Set dir to the directory containing
+ tclConfig.sh
. Normally that would be something like
+ /usr/lib
or /usr/local/lib
.
+
+
+
--with-mm=dir
+ (Optional.)
+ If you did not install MM in /usr, then you should use this flag to
+ specify where you installed it. The MM headers should be in
+ dir/include
and the MM library should be in
+ dir/lib
.
+
+
+
+In my test environment, I have private copies of Tcl, Apache, and MM,
+so my typical configure command looks something like this:
+
+
./configure \
+ --with-database=ora8 \
+ --with-tcl=/u/mayoff/test/lib \
+ --with-apxs=/u/mayoff/test/bin/apxs \
+ --with-mm=/u/mayoff/test
+
+
+After running configure, you should just be able to run
+make
. It will compile the module and install it in your
+existing Apache installation using apxs.
+
+
+
+mod_aolserver provides two request handlers:
+
+
+ aolserver
- This handler is for .tcl
+ files.
+
+
adp
- This handler is for .adp
files.
+
+
+You must enable ExecCGI for these handlers to work. Neither of
+these handlers is really necessary if you're running ACS, because
+ACS uses ns_register_proc
to handle all requests for
+.tcl
and .adp
files anyway.
+
+mod_aolserver needs an AOLserver configuration file, in either
+.tcl
or .ini
format. You must specify the
+location of the file using the AolServerConf config_file
+directive in your httpd.conf
:
+
+
AolServerConf config_file /u/mayoff/test/nsd.tcl
+ or
+AolServerConf config_file /u/mayoff/test/nsd.ini
+
+Either of these files can contain an AuxConfigDir
+parameter, which mod_aolserver will process just like AOLserver would.
+You'll need that parameter if you want to run ACS.
+
+Here's an
+httpd.conf
excerpt to get you started. Assume that I have
+installed Apache in /u/mayoff/test
and the ACS in
+/u/mayoff/test/acs
.
+
+
# This is added automatically by apxs when you `make' or `make install':
+LoadModule aolserver_module libexec/mod_aolserver.so
+
+AolServerConf config_file /u/mayoff/test/nsd.ini
+
+# The following setting is returned by [ns_info log]
+AolServerConf log_file_location /u/mayoff/test/logs/error_log
+
+# Note: Make sure that PageRoot in your nsd.ini is set
+# to the same thing as this DocumentRoot.
+DocumentRoot "/u/mayoff/test/acs/www"
+
+<Directory "/u/mayoff/test/acs/www">
+ Options Indexes FollowSymLinks MultiViews ExecCGI
+ DirectoryIndex index.tcl index.adp index.shtml index.html index.htm
+ AddHandler adp adp
+ AddHandler aolserver tcl
+</Directory>
+
+This should cause mod_aolserver stuff to run only in the virtual server
+containing the AolServerConf directive (or the main server only if you
+put it outside a virtual server section). You're probably better off
+running this in the main server of a test Apache installation right
+now, rather than running it in a virtual server of your production
+installation.
+
+
+
+Configuring mod_aolserver is pretty straightforward. The module
+will load whatever configuration file you specified with the
+AolServerConf config_file
directive in your Apache
+configuration. The config file can be in either .ini or .tcl format.
+Many AOLserver configuration directives have no effect under
+mod_aolserver, but they will not be detrimental either.
+
+
A sample configuration file can be found in the mod_aolserver
+distribution as tcl_modules/nsd.ini.sample
. You should be
+able to use that config file, with some site-specific modifications, to
+run ACS on Apache.
+
+
Be sure to disable the watchdog in your ACS
+configuration (by setting WatchDogFrequency to 0) or you'll get
+a lot of mail. The Apache error log is formatted differently than the
+AOLserver error log, so the watchdog parser doesn't work.
+
+
Also, if you're using the Oracle database driver, you may
+encounter problems if you leave the datasource empty. On my test
+system running Red Hat Linux 6.2 and Oracle 8.1.6, I found that Apache
+would either hang or dump core during startup. It turns out to be a
+problem with the "Bequeath Protocol", which is the way that the driver
+talks to Oracle if you leave the DataSource blank.
+
+
The workaround for using Oracle is to set the
+DataSource in your database pools to a TCP or IPC connection.
+This means you need to know a little bit about configuring Net*8
+if you haven't done so before. The configuration assistant,
+netasst
, is pretty good in Oracle 8.1.6, so you shouldn't
+have too much difficulty. If you run into trouble, you can try asking
+for help at the
+ACS on Apache web site or Web Tools
+Review.
+
+
+
+Send bug reports and other feedback to
+mod_aolserver@arsdigita.com.
+
+
+Here are the known limitations, bugs, and to-do tasks.
+
+
+
+ - Database connections are not pooled between
+ Apache children. This means you could end up opening a lot more
+ database connections under mod_aolserver than under AOLserver.
+ If you're using the Oracle driver, you'll probably want to configure
+ Oracle to use shared connections instead of dedicated connections,
+ or restrict your Apache MaxClients setting to something very small
+ (like 8) to keep the number of database connections low.
+
+
- Database drivers are not dynamically loaded. This means that
+ you can only use one of the two drivers we include - the ora8 driver
+ and the postgres driver - and you must choose one and only one to
+ use at compile time.
+
+
- ns_register_* and ns_schedule_* only work during server
+ initialization. ns_cancel, ns_pause, ns_resume, and
+ ns_unschedule_proc are not implemented.
+
+
ACS only registers and schedules procedures as server
+ initialization time, so this should not cause problems.
+
+
Procedures that are scheduled to run once, after 120 seconds or
+ less, are considered initialization procedures and are run in the
+ Apache parent after all other mod_aolserver initialization but
+ before the children are forked. Such procedures are used because in
+ AOLserver you don't have access to database handles during
+ initialization but you do have access to them from scheduled
+ procedures.
+
+
- Preauth filters run after the Apache authorization phase.
+
+
- ns_share does not share data among
+ processes. ns_share is deprecated, and ACS is moving away
+ from its use. Use nsv_* instead, which does share data among
+ processes.
+
+
- ns_critsec, ns_cond, and ns_sema are not implemented.
+
+
- ns_thread is not implemented.
+
+
The bulkmail module will probably not work, because
+ it uses ns_thread.
+
+
Note that if you call ns_scheduled_proc -thread
,
+ mod_aolserver will fork a separate process to run
+ the proc, instead of running the proc in the scheduler process.
+
+
- ns_info pageroot returns the value from nsd.ini, but the
+ DocumentRoot from httpd.conf is what matters. Make sure you keep
+ them in sync.
+
+
- No debug support in ADPs. Only the fancy ADP parser is
+ supported.
+
+
- The ns_conn subcommands authpasswd, contentlength, and
+ protocol are not implemented. ns_conn url drops PATH_INFO. This
+ should not affect ACS since everything goes through the abstract URL
+ system anyway.
+
+
ns_url2file
drops PATH_INFO. This doesn't appear to affect
+ ACS.
+
+ [ns_server active]
always returns "". Other commands are not
+ supported.
+
+ ns_atclose
doesn't work for scheduled procs.
+
+ ns_configsections
is not supported. The sets
+ returned by ns_configsection don't nest. For example,
+ [ns_set get [ns_configsection ns/foo] bar]
will not be
+ the set returned by [ns_configsection ns/foo/bar]
.
+
+ [ns_set split]
is not supported. [ns_set
+ -persist]
is only supported during initialization.
+
+ ns_geturl
just invokes ns_httpget. ACS only
+ uses this in one place. We should fix ACS to just use ns_httpget.
+
+ - ns_log and ns_modlog send everything to the Apache error log.
+ There is no control over specific facilities or log levels.
+
+
- The error log watchdog doesn't work because the
+ Apache/mod_aolserver error log format is different from AOLserver's,
+ and the watchdog parser only understands the AOLserver format.
+
+
- ns_checkurl and ns_requestauthorize are no-ops.
+
+
- For other unimplemented commands, look at tclcmds.c. For
+ example, some socket-related commands, cache commands, and other
+ things like ns_fork and ns_rollfile are not implemented.
+
+
-
+ With mod_aolserver's implementation of Ns_Set, you need an
+ Ns_SetTrunc(.., 0) to actually reclaim storage for anything that's been
+ put in the set in the meantime (due to use of the Apache pools). The
+ result is that right now, a select consumes the storage required for
+ all rows retrieved before it is finally reclaimed. It may be easier to
+ fix this in the set code than the driver (I'd need something that wipes
+ the values without affecting the keys; I could then just call that from
+ Ns_GetRow), but it needs some attention.
+
+
+ Solution: Redo ns_sets so that the ns_set structure itself is not in the OWN_POOL,
+ if we have one; then we can clear the pool containing the real_table
+ without flushing and potentially invalidating the ns_set structure
+ itself. (Right now, we make the correct but dicey assumption that ---
+ *without* ALLOC_USE_MALLOC --- we will reallocate the ns_set structure
+ right on top of its original location, so that pointers to it will
+ remain valid).
+
+
- Database drivers. It would be nice if we could use exactly the
+ same drivers as AOLserver and dynamically load them.
+
+
(25)Inappropriate ioctl for device:
- I keep seeing
+ this message in my error log (on Linux). Harmless but annoying.
+
+ -
+ Does the scheduler die? It sometimes trips spuriously when its parent
+ is being debugged, but I haven't got it to die when running "au naturel".
+ Still, the location when it does coredump is suspicious --- a sprintf
+ into interp->result.
+
+
-
+ ns_server active --- requires extended status; and won't give full URLs
+ even then (you only get the first few dozen bytes of the request, so
+ long request lines will be truncated). Currently stubbed out.
+
+
-
+ ns_write header reparsing (hey rst, why? - mayoff)
+
+
-
+ run registered procs in an ordinary response handler, not (like
+ filters) in fixups
+
+
-
+ Look at diffs between aolserver 3.0b61 and 3.0 final; anything new we want
+ to drag in?
+
+
- ns_returnfile needs if-modified-since support.
+
+
-
+ Figure out "invalid method" errors when a POST to an ADP script
+ encounters a tcl syntax error...
+
+
- properly seed the random number generator
+
+
+
+
+
$Header: /usr/local/cvsroot/mod_nsd/Attic/README.html,v 1.1 2001/04/13 21:09:32 ppetru Exp $
+ mod_aolserver@arsdigita.com
+
+
+
Index: mod_nsd/TODO
===================================================================
RCS file: /usr/local/cvsroot/mod_nsd/Attic/TODO,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ mod_nsd/TODO 13 Apr 2001 21:09:32 -0000 1.1
@@ -0,0 +1,60 @@
+Does the scheduler die? It sometimes trips spuriously when its parent
+ is being debugged, but I haven't got it to die when running "au naturel".
+ Still, the location when it does coredump is suspicious --- a sprintf
+ into interp->result, which may not always point to allocated storage
+ in tcl 8.x...
+
+Check ns_conn url and ns_url2file for behavior wrt PATH_INFO; fix if needed.
+
+Remaining db stuff:
+
+ *) Compile and test oracle driver.
+
+ *) With my Ns_Sets, you need an Ns_SetTrunc(.., 0) to actually reclaim
+ storage for anything that's been put in the set in the meantime (due
+ to use of the Apache pools). The result is that right now, a select
+ consumes the storage required for all rows retrieved before it is
+ finally reclaimed. It may be easier to fix this in the set code than
+ the driver (I'd need something that wipes the values without affecting
+ the keys; I could then just call that from Ns_GetRow), but it needs
+ some attention. See also the ns_set related item below.
+
+ *) run CheckPools after each request. (Obviously doesn't work
+ to run it as a scheduled proc, as that would only check the
+ pools in the scheduler process...).
+
+ *) Write the support code for Apache modules that load db modules
+
+Interaction with virtual servers needs testing.
+
+ns_server active --- requires extended status; and won't give full URLs
+ even then (you only get the first few dozen bytes of the request, so
+ long request lines will be truncated). Currently stubbed out.
+
+Redo ns_sets so that the ns_set structure itself is not in the OWN_POOL,
+ if we have one; then we can clear the pool containing the real_table
+ without flushing and potentially invalidating the ns_set structure
+ itself. (Right now, we make the correct but dicey assumption that ---
+ *without* ALLOC_USE_MALLOC --- we will reallocate the ns_set structure
+ right on top of its original location, so that pointers to it will
+ remain valid).
+
+ns_write header reparsing
+
+run registered procs in an ordinary response handler, not (like
+ filters) in fixups
+
+Look at diffs between aolserver 3b61 and 3rc1; anything new we want
+ to drag in? (Don't want to keep the sprintf(interp->result, ...)
+ crap if they haven't...)
+
+ns_returnfile needs if-modified-since support.
+
+properly seed the random number generator
+
+Figure out "invalid method" errors when a POST to an ADP script
+ encounters a tcl syntax error...
+
+???Switch ns_share to namespace implementation
+
+
Index: mod_nsd/aclocal.m4
===================================================================
RCS file: /usr/local/cvsroot/mod_nsd/aclocal.m4,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ mod_nsd/aclocal.m4 13 Apr 2001 21:09:32 -0000 1.1
@@ -0,0 +1,317 @@
+
+dnl ########################################################################
+dnl Find the Apache extension builder.
+
+AC_DEFUN(MAS_PATH_APXS, [
+
+ AC_SUBST(APXS)
+ AC_SUBST(APACHE_BASE)
+
+ AC_CACHE_CHECK([for apxs], mas_cv_path_apxs, [
+ mas_cv_path_apxs=no
+
+ AC_ARG_WITH(apxs, [ --with-apxs=PATH path to apxs program], [
+ mas_cv_path_apxs="$withval"
+ if test ! -x "$mas_cv_path_apxs"; then
+ AC_MSG_ERROR([$mas_cv_path_apxs is not an executable file.])
+ fi
+ ])
+
+ if test "$mas_cv_path_apxs" = no; then
+ AC_PATH_PROG(mas_cv_path_apxs, apxs, no)
+ fi
+
+ if test "$mas_cv_path_apxs" = no; then
+ AC_MSG_ERROR([apxs not found. Make sure it is in your PATH.])
+ fi
+
+ ])
+
+ APXS="$mas_cv_path_apxs"
+ apache_include_path="`$APXS -q includedir`"
+ if test x"$apache_include_path" != x; then
+ CPPFLAGS="$CPPFLAGS -I`$APXS -q includedir`"
+ fi
+ APACHE_BASE="`$APXS -q prefix`"
+])
+
+dnl ########################################################################
+dnl Find the MM library.
+
+AC_DEFUN(MAS_ADD_MM_TO_FLAGS,[
+ if test x"$1" != x; then
+ CPPFLAGS="$CPPFLAGS -I$1/include"
+ LIBS="$LIBS -L$1/lib"
+ fi
+ LIBS="$LIBS -lmm"
+])
+
+AC_DEFUN(MAS_TRY_MM_PATH,[
+ saved_cppflags="$CPPFLAGS"
+ saved_libs="$LIBS"
+ MAS_ADD_MM_TO_FLAGS($1)
+ AC_TRY_CPP([#include ],[
+ AC_TRY_LINK_FUNC(mm_malloc,[mas_cv_path_mm="$1"])
+ ])
+ LIBS="$saved_libs"
+ CPPFLAGS="$saved_cppflags"
+])
+
+AC_DEFUN(MAS_PATH_MM,[
+ AC_MSG_CHECKING([for MM library])
+
+ AC_CACHE_VAL(mas_cv_path_mm,[
+ mas_cv_path_mm=no
+
+ AC_ARG_WITH(mm, [ --with-mm=DIR path where MM is installed, e.g. /usr/local],
+ [
+ MAS_TRY_MM_PATH($withval)
+ ])
+
+ if test "$mas_cv_path_mm" = no; then
+ MAS_TRY_MM_PATH()
+ fi
+
+ if test "$mas_cv_path_mm" = no; then
+ MAS_TRY_MM_PATH(/usr/local)
+ fi
+ ])
+
+ if test "$mas_cv_path_mm" = no; then
+ AC_MSG_ERROR([MM library not found])
+ fi
+
+ MAS_ADD_MM_TO_FLAGS($mas_cv_path_mm)
+
+ AC_MSG_RESULT($mas_cv_path_mm)
+])
+
+dnl ########################################################################
+dnl Figure out where PostgreSQL lives.
+
+AC_DEFUN(MAS_ADD_POSTGRES_TO_FLAGS,[
+ if test x"$1" != x; then
+ CPPFLAGS="$CPPFLAGS -I$1/include"
+ LIBS="$LIBS -L$1/lib"
+ else
+ CPPFLAGS="$CPPFLAGS -I/usr/include/pgsql"
+ fi
+ LIBS="$LIBS -lpq"
+])
+
+AC_DEFUN(MAS_TRY_POSTGRES_PATH,[
+ MAS_ADD_POSTGRES_TO_FLAGS($1)
+ AC_TRY_CPP([#include ],[
+ AC_TRY_LINK([],[PQsetdbLogin();],[mas_cv_path_postgres="$1"])
+ ])
+])
+
+AC_DEFUN(MAS_PATH_POSTGRES, [
+
+ AC_MSG_CHECKING([for PostgreSQL])
+
+ AC_CACHE_VAL(mas_cv_path_postgres,[
+ mas_cv_path_postgres=no
+
+ AC_ARG_WITH(postgres, [ --with-postgres=DIR path to PostgreSQL installation if using PSQL driver],
+ [
+ saved_cppflags="$CPPFLAGS"
+ saved_libs="$LIBS"
+ MAS_TRY_POSTGRES_PATH($withval)
+ CPPFLAGS="$saved_cppflags"
+ LIBS="$saved_libs"
+ ])
+
+ if test "$mas_cv_path_postgres" = no; then
+ saved_cppflags="$CPPFLAGS"
+ saved_libs="$LIBS"
+ MAS_TRY_POSTGRES_PATH()
+ CPPFLAGS="$saved_cppflags"
+ LIBS="$saved_libs"
+ fi
+
+ if test "$mas_cv_path_postgres" = no; then
+ saved_cppflags="$CPPFLAGS"
+ saved_libs="$LIBS"
+ MAS_TRY_POSTGRES_PATH(/usr/local)
+ CPPFLAGS="$saved_cppflags"
+ LIBS="$saved_libs"
+ fi
+ ])
+
+ if test "$mas_cv_path_postgres" = no; then
+ AC_MSG_ERROR([PostgreSQL not found])
+ fi
+
+ MAS_ADD_POSTGRES_TO_FLAGS($mas_cv_path_postgres)
+
+ AC_MSG_RESULT($mas_cv_path_postgres)
+])
+
+dnl ########################################################################
+dnl Figure out where Oracle lives.
+
+AC_DEFUN(MAS_ADD_ORACLE_TO_FLAGS,[
+ if test x"$1" != x; then
+ CPPFLAGS="$CPPFLAGS -I$1/rdbms/demo -I$1/rdbms/public -I$1/network/public -I$1/plsql/public"
+ LIBS="$LIBS -L$1/lib"
+ fi
+ LIBS="$LIBS -lclntsh -lcore8 -lcommon8 -lgeneric8 -lclient8"
+])
+
+AC_DEFUN(MAS_TRY_ORACLE_PATH,[
+ MAS_ADD_ORACLE_TO_FLAGS($1)
+ AC_TRY_CPP([#include ],[
+ AC_TRY_LINK([],[OCIInitialize();],[mas_cv_path_oracle="$1"])
+ ])
+])
+
+AC_DEFUN(MAS_PATH_ORACLE, [
+
+ AC_MSG_CHECKING([for Oracle8])
+
+ AC_CACHE_VAL(mas_cv_path_oracle,[
+ mas_cv_path_oracle=no
+
+ AC_ARG_WITH(ora8, [ --with-ora8=DIR path to Oracle8 installation if using Oracle driver
+ [${ORACLE_HOME-none}]],
+ [
+ saved_cppflags="$CPPFLAGS"
+ saved_libs="$LIBS"
+ MAS_TRY_ORACLE_PATH($withval)
+ CPPFLAGS="$saved_cppflags"
+ LIBS="$saved_libs"
+ ])
+
+ if test "$mas_cv_path_oracle" = no; then
+ saved_libs="$LIBS"
+ MAS_TRY_ORACLE_PATH()
+ LIBS="$saved_libs"
+ fi
+
+ if test "$mas_cv_path_oracle" = no; then
+ saved_cppflags="$CPPFLAGS"
+ saved_libs="$LIBS"
+ MAS_TRY_ORACLE_PATH($ORACLE_HOME)
+ CPPFLAGS="$saved_cppflags"
+ LIBS="$saved_libs"
+ fi
+ ])
+
+ if test "$mas_cv_path_oracle" = no; then
+ AC_MSG_ERROR([Oracle8 not found])
+ fi
+
+ MAS_ADD_ORACLE_TO_FLAGS($mas_cv_path_oracle)
+
+ AC_MSG_RESULT($mas_cv_path_oracle)
+])
+
+dnl ########################################################################
+dnl Find Tcl.
+
+AC_DEFUN(SC_PATH_TCLCONFIG, [
+ #
+ # Ok, lets find the tcl configuration
+ # First, look for one uninstalled.
+ # the alternative search directory is invoked by --with-tcl
+ #
+
+ if test x"${no_tcl}" = x ; then
+ # we reset no_tcl in case something fails here
+ no_tcl=true
+ AC_ARG_WITH(tcl, [ --with-tcl=DIR directory containing Tcl configuration (tclConfig.sh)], with_tclconfig=${withval})
+ AC_MSG_CHECKING([for Tcl configuration])
+ AC_CACHE_VAL(ac_cv_c_tclconfig,[
+
+ # First check to see if --with-tclconfig was specified.
+ if test x"${with_tclconfig}" != x ; then
+ if test -f "${with_tclconfig}/tclConfig.sh" ; then
+ ac_cv_c_tclconfig=`(cd ${with_tclconfig}; pwd)`
+ else
+ AC_MSG_ERROR([${with_tclconfig} directory doesn't contain tclConfig.sh])
+ fi
+ fi
+
+ # then check for a private Tcl installation
+ if test x"${ac_cv_c_tclconfig}" = x ; then
+ for i in \
+ ../tcl \
+ `ls -dr ../tcl[[8-9]].[[0-9]]* 2>/dev/null` \
+ ../../tcl \
+ `ls -dr ../../tcl[[8-9]].[[0-9]]* 2>/dev/null` \
+ ../../../tcl \
+ `ls -dr ../../../tcl[[8-9]].[[0-9]]* 2>/dev/null` ; do
+ if test -f "$i/unix/tclConfig.sh" ; then
+ ac_cv_c_tclconfig=`(cd $i/unix; pwd)`
+ break
+ fi
+ done
+ fi
+
+ # check in a few common install locations
+ if test x"${ac_cv_c_tclconfig}" = x ; then
+ for i in `ls -d ${prefix}/lib 2>/dev/null` \
+ `ls -d /usr/local/lib 2>/dev/null` ; do
+ if test -f "$i/tclConfig.sh" ; then
+ ac_cv_c_tclconfig=`(cd $i; pwd)`
+ break
+ fi
+ done
+ fi
+
+ # check in a few other private locations
+ if test x"${ac_cv_c_tcliconfig}" = x ; then
+ for i in \
+ ${srcdir}/../tcl \
+ `ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]]* 2>/dev/null` ; do
+ if test -f "$i/unix/tclConfig.sh" ; then
+ ac_cv_c_tclconfig=`(cd $i/unix; pwd)`
+ break
+ fi
+ done
+ fi
+ ])
+
+ if test x"${ac_cv_c_tclconfig}" = x ; then
+ TCL_BIN_DIR="# no Tcl configs found"
+ AC_MSG_WARN(Can't find Tcl configuration definitions)
+ exit 0
+ else
+ no_tcl=
+ TCL_BIN_DIR=${ac_cv_c_tclconfig}
+ AC_MSG_RESULT(found $TCL_BIN_DIR/tclConfig.sh)
+ fi
+ fi
+])
+
+AC_DEFUN(SC_LOAD_TCLCONFIG, [
+ AC_MSG_CHECKING([for existence of $TCL_BIN_DIR/tclConfig.sh])
+
+ if test -f "$TCL_BIN_DIR/tclConfig.sh" ; then
+ AC_MSG_RESULT([loading])
+ . $TCL_BIN_DIR/tclConfig.sh
+ else
+ AC_MSG_RESULT([file not found])
+ fi
+
+ #
+ # The eval is required to do the TCL_DBGX substitution in the
+ # TCL_LIB_FILE variable
+ #
+
+ eval TCL_LIB_FILE=${TCL_LIB_FILE}
+ eval TCL_LIB_FLAG=${TCL_LIB_FLAG}
+ eval "TCL_LIBS=\"${TCL_LIBS}\""
+ eval "TCL_LIB_SPEC=\"${TCL_LIB_SPEC}\""
+
+ AC_SUBST(TCL_BIN_DIR)
+ AC_SUBST(TCL_SRC_DIR)
+ AC_SUBST(TCL_LIB_FILE)
+ AC_SUBST(TCL_LIBS)
+ AC_SUBST(TCL_LIB_SPEC)
+ AC_SUBST(TCL_SHLIB_SUFFIX)
+
+ CPPFLAGS="$CPPFLAGS -I$TCL_PREFIX/include"
+])
+
Index: mod_nsd/adp.c
===================================================================
RCS file: /usr/local/cvsroot/mod_nsd/adp.c,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ mod_nsd/adp.c 13 Apr 2001 21:09:32 -0000 1.1
@@ -0,0 +1,2113 @@
+/*
+ * mod_aolserver aolserver emulation --- Copyright 2000 Robert S. Thau.
+ * This file derived from the actual aolserver code, and is distributed
+ * in accord with its license, as follows:
+ *
+ * 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.lcs.mit.edu/.
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * adp.c --
+ *
+ * Support for AOLserver Dynamic Pages.
+ *
+ * TODO: ns_adp_include -sameframe is dead; should issue a warning
+ * you can't debug ns_adp_parse -string "adp"
+ */
+
+#include "nsd.h"
+#include /* for ap_rflush */
+
+/*
+ * The following structure maintains an ADP call frame. PushFrame
+ * is used to save the previous state of the per-thread AdpData
+ * structure in a Frame allocated on the stack and PopFrame restores
+ * the previous state from the Frame.
+ */
+
+typedef struct {
+ int argc;
+ char **argv;
+ char *cwd;
+ int length;
+ Ns_DString cwdBuf;
+} Frame;
+
+/*
+ * The following structure is used as a unique key for compiled pages
+ * in the cache.
+ */
+
+#ifdef WIN32
+#define CACHE_KEYS TCL_STRING_KEYS
+#else
+typedef struct Key {
+ dev_t dev;
+ ino_t ino;
+} Key;
+#define CACHE_KEYS ((sizeof(Key))/(sizeof(int)))
+#endif
+
+/*
+ * Local functions defined in this file.
+ */
+
+static void Parse(char *filename, Ns_DString *out, char *pagein);
+#ifdef NOTDEF
+static Ns_OpProc AdpProc;
+#endif
+static void (DelAdpData)(void *);
+static void ParsePage(Ns_DString *, char *page);
+static int ParseFile(Tcl_Interp *, char *file, size_t size, Ns_DString *);
+static void PushFrame(Frame *framePtr, char *file, int argc, char **argv);
+static void PopFrame(Frame *framePtr);
+#ifdef NOTDEF
+static int DebugInit(Tcl_Interp *interp, char *host, char *port,
+ char *procs);
+static Ns_TclInterpInitProc EnableCmds;
+#endif
+static void TextChunk(Ns_DString *dsPtr, char *text);
+static void SetMimeType(AdpData *adPtr, char *mimeType);
+
+/*
+ * Static global variables
+ */
+
+static Ns_Tls adKey;
+#ifdef NOTDEF
+static Ns_AdpParserProc *defParserProc = NULL;
+#endif
+static Ns_AdpParserProc *defParserProc = FancyParsePage;
+static Tcl_HashTable extensionsTable;
+static Tcl_HashTable parsersTable;
+#ifdef NOTDEF
+static Ns_Cache *sharedCachePtr;
+#endif
+
+/*
+ * Global variables within this source directory
+ */
+
+Ns_ModLogHandle nsAdpModLogHandle;
+
+#ifdef NOTDEF
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ns_AdpRegisterParser --
+ *
+ * Sets the ADP parser.
+ *
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Ns_AdpRegisterParser(char *extension, Ns_AdpParserProc *newParserProc)
+{
+ Tcl_HashEntry *hePtr;
+ int new;
+
+ if (Ns_InfoServersStarted() == NS_TRUE) {
+ Ns_ModLog(Error, nsAdpModLogHandle,
+ "attempt to register ADP parser after server startup.");
+ return NS_ERROR;
+ }
+ hePtr = Tcl_CreateHashEntry(&parsersTable, extension, &new);
+ Tcl_SetHashValue(hePtr, (void *) newParserProc);
+
+ return NS_OK;
+}
+#endif
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NsAdpInit --
+ *
+ * Initialize the ADP interface.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * ADP processor is register as required, Tcl commands are added, and
+ * a simple cache is initialized.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+NsAdpInit(void)
+{
+#ifdef NOTDEf
+ Ns_Set *set;
+ char *path, *map;
+ int i;
+#endif
+
+ /*
+ * register the sub-realm
+ */
+
+ NsModLogRegSubRealm("adp", &nsAdpModLogHandle);
+
+ /*
+ * Initialize the ADP core.
+ */
+
+ Ns_TlsAlloc(&adKey, DelAdpData);
+
+#ifdef NOTDEF
+ if (nsconf.adp.cache && !nsconf.adp.threadcache) {
+ sharedCachePtr = Ns_CacheCreateSz("adp", CACHE_KEYS,
+ nsconf.adp.cachesize, ns_free);
+ }
+
+ /*
+ * Add the Tcl commands
+ */
+
+ Ns_TclInitInterps(nsServer, EnableCmds, NULL);
+
+ /*
+ * Register ADP for any requested URLs.
+ */
+
+ path = Ns_ConfigPath(nsServer, NULL, "adp", NULL);
+ map = NULL;
+ set = Ns_ConfigGetSection(path);
+ for (i = 0; set != NULL && i < Ns_SetSize(set); ++i) {
+ char *key;
+
+ key = Ns_SetKey(set, i);
+ if (!strcasecmp(key, "map")) {
+ map = Ns_SetValue(set, i);
+ Ns_RegisterRequest(nsServer, "GET", map, AdpProc, NULL,
+ NULL, 0);
+ Ns_RegisterRequest(nsServer, "HEAD", map, AdpProc, NULL,
+ NULL, 0);
+ Ns_RegisterRequest(nsServer, "POST", map, AdpProc, NULL,
+ NULL, 0);
+ Ns_ModLog(Notice, nsAdpModLogHandle, "mapped %s", map);
+ }
+ }
+ if (map == NULL) {
+ Ns_ModLog(Warning, nsAdpModLogHandle,
+ "no Map configuration - disabled");
+ }
+#endif
+
+ /*
+ * Initialize the hash table of parsers
+ */
+
+ Tcl_InitHashTable(&parsersTable, TCL_STRING_KEYS);
+ Tcl_InitHashTable(&extensionsTable, TCL_STRING_KEYS);
+ NsAdpFancyInit("ignored", "args");
+}
+
+#ifdef NOTDEF
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NsAdpParsers --
+ *
+ * Load ADP parser mappings.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Registers requests for each parser.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+NsAdpParsers(void)
+{
+ char *path;
+ Tcl_HashEntry *hePtr;
+ int size = 0;
+ Ns_Set *setPtr;
+ int i, new;
+ Ns_AdpParserProc *parserProc;
+
+ path = Ns_ConfigPath(nsServer, NULL, "adp", "parsers", NULL);
+
+ NsAdpFancyInit(nsServer, path);
+ Ns_AdpRegisterParser("adp", ParsePage);
+
+ if (path != NULL) {
+ setPtr = Ns_ConfigGetSection(path);
+ size = Ns_SetSize(setPtr);
+ }
+
+ hePtr = Tcl_FindHashEntry(&parsersTable, nsconf.adp.defaultparser);
+ parserProc = (Ns_AdpParserProc *) Tcl_GetHashValue(hePtr);
+ defParserProc = parserProc;
+
+ hePtr = Tcl_CreateHashEntry(&extensionsTable, ".adp", &new);
+ Tcl_SetHashValue(hePtr, defParserProc);
+
+ for (i=0; i < size; i++) {
+ char *parser;
+ char *ext;
+
+ parser = Ns_SetKey(setPtr, i);
+ ext = Ns_SetValue(setPtr, i);
+
+ hePtr = Tcl_FindHashEntry(&parsersTable, parser);
+ if (hePtr == NULL) {
+ Ns_ModLog(Notice, nsAdpModLogHandle,
+ "invalid parser '%s'", parser);
+ continue;
+ }
+
+ parserProc = (Ns_AdpParserProc *) Tcl_GetHashValue(hePtr);
+ hePtr = Tcl_CreateHashEntry(&extensionsTable, ext, &new);
+ Tcl_SetHashValue(hePtr, parserProc);
+ }
+}
+#endif
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NsAdpGetData --
+ *
+ * Return the per-thread ADP context structure.
+ *
+ * Results:
+ * Pointer to AdpData.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+AdpData *
+NsAdpGetData(void)
+{
+ AdpData *adPtr;
+
+ adPtr = (AdpData *) Ns_TlsGet(&adKey);
+ if (adPtr == NULL) {
+ adPtr = ns_calloc(1, sizeof(AdpData));
+ adPtr->mimeType = NULL;
+ Ns_DStringInit(&adPtr->output);
+ Ns_TlsSet(&adKey, adPtr);
+ }
+
+ return adPtr;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NsAdpEval --
+ *
+ * Evaluate a list of chunks from an ADP.
+ *
+ * Results:
+ * TCL_OK or TCL_ERROR.
+ *
+ * Side effects:
+ * Depends on script chunks.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+NsAdpEval(Tcl_Interp *interp, char *file, char *chunks)
+{
+ int chunk, n, code, fd;
+ register char *ch = chunks;
+ Ns_DString ds;
+ char buf[10], *script, debugfile[255];
+ AdpData *adPtr;
+
+ adPtr = NsAdpGetData();
+ if (Ns_CheckStack() != NS_OK) {
+ interp->result = "danger: stack grown too large (recursive adp?)";
+ adPtr->exception = ADP_OVERFLOW;
+ return TCL_ERROR;
+ }
+
+ if (file == NULL) {
+ file = "";
+ }
+ Ns_DStringInit(&ds);
+ code = TCL_OK;
+ chunk = 1;
+
+ while (*ch && adPtr->exception == ADP_OK) {
+ n = strlen(ch);
+ if (*ch++ == 't') {
+ Ns_DStringNAppend(&adPtr->output, ch, n-1);
+ } else {
+ script = ch;
+ if (adPtr->debugLevel > 0) {
+ Ns_DStringTrunc(&ds, 0);
+ sprintf(buf, "%d", adPtr->debugLevel);
+ Ns_DStringVarAppend(&ds,
+ "#\n"
+ "# level: ", buf, "\n", NULL);
+ sprintf(buf, "%d", chunk);
+ Ns_DStringVarAppend(&ds,
+ "# chunk: ", buf, "\n"
+ "# file: ", file, "\n"
+ "#\n\n", ch, NULL);
+ sprintf(debugfile, "/tmp/adp%d.%d.XXXXXX",
+ adPtr->debugLevel, chunk);
+ mktemp(debugfile);
+ fd = open(debugfile, O_WRONLY|O_TRUNC|O_CREAT, 0644);
+ if (fd < 0) {
+ Ns_ModLog(Error, nsAdpModLogHandle,
+ "could not open %s: %s", debugfile,
+ strerror(errno));
+ } else {
+ write(fd, ds.string, ds.length);
+ close(fd);
+ Ns_DStringTrunc(&ds, 0);
+ Ns_DStringVarAppend(&ds, "source ", debugfile, NULL);
+ script = ds.string;
+ }
+ }
+ code = NsTclEval(interp, script);
+ if (code != TCL_OK &&
+ code != TCL_RETURN &&
+ adPtr->exception == ADP_OK) {
+ NsAdpLogError(interp, file, chunk);
+ }
+ if (code == TCL_ERROR && adPtr->exception == ADP_RETURN) {
+ adPtr->exception = ADP_OK;
+ code = TCL_OK;
+ }
+ if (script != ch) {
+ unlink(debugfile);
+ }
+ ++chunk;
+ }
+ ch += n;
+ NsAdpFlush(adPtr);
+ }
+ NsAdpFlush(adPtr);
+ Ns_DStringFree(&ds);
+
+ return code;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NsAdpStreamOn --
+ *
+ * Turn on streaming for the current connection.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None unless streaming is not already enabled in which case
+ * headers and any pending content is immediately sent to the
+ * client. Note that no content-length header will be sent
+ * which has the side effect of disabling connection: keep-alive
+ * for the client.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+NsAdpStreamOn(void)
+{
+ AdpData *adPtr;
+
+ adPtr = NsAdpGetData();
+ if (adPtr->conn == NULL) {
+ return;
+ }
+ if (adPtr->fStream != NS_TRUE) {
+ adPtr->fStream = NS_TRUE;
+ Ns_ConnSetRequiredHeaders(adPtr->conn, "text/html", 0);
+ Ns_ConnFlushHeaders(adPtr->conn, 200);
+ }
+ NsAdpFlush(adPtr);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NsAdpFlush --
+ *
+ * Flush current output.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None unless streaming is enabled in which case content is
+ * written directly out the connection. Also, output is disabled
+ * during inlined evalution where the output buffer is used to
+ * accumulate the inlined evaluation result before being copied
+ * to the interp.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+NsAdpFlush(AdpData *adPtr)
+{
+ if (adPtr->fStream == NS_TRUE &&
+ adPtr->evalLevel == 0 &&
+ adPtr->output.length > 0) {
+
+ Ns_WriteConn(adPtr->conn, adPtr->output.string,
+ adPtr->output.length);
+ ap_rflush (Tcl_request_rec);
+ Ns_DStringTrunc(&adPtr->output, 0);
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NsTclAdpEvalCmd --
+ *
+ * Process the Tcl _ns_adp_eval command.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * Page string is parsed and evaluated at current Tcl level in a
+ * new ADP call frame.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+NsTclAdpEvalCmd(ClientData dummy, Tcl_Interp *interp, int argc,
+ char **argv)
+{
+ AdpData *adPtr;
+ Frame frame;
+ Ns_DString ds;
+ int code;
+ int offset = 0;
+ int parser = -1;
+ Tcl_HashEntry *hePtr;
+ Ns_AdpParserProc *parserProc;
+
+ if (argc >= 2) {
+ if (!strcmp(argv[1], "-parser")) {
+ offset += 2;
+ parser = 1;
+ }
+ }
+ if (argc < 2 + offset) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " ?-parser parser? page ?arg ...?\"", NULL);
+ return TCL_ERROR;
+ }
+
+ if (parser != -1) {
+ hePtr = Tcl_FindHashEntry(&parsersTable, argv[parser]);
+ if (hePtr == NULL) {
+ Tcl_AppendResult(interp, "invalid parser \"", parser, "\"", NULL);
+ return TCL_ERROR;
+ }
+ parserProc = (Ns_AdpParserProc *) Tcl_GetHashValue(hePtr);
+ } else {
+ parserProc = defParserProc;
+ }
+
+ /*
+ * Increment the inline eval level to ensure flushing is disabled,
+ * push a frame, execute the code, and then more any result to the
+ * interp from the output buffer.
+ */
+
+ Ns_DStringInit(&ds);
+ adPtr = NsAdpGetData();
+ ++adPtr->evalLevel;
+ PushFrame(&frame, NULL, argc-1-offset, argv+1+offset);
+ Parse(NULL, &ds, argv[1]);
+ code = NsAdpEval(interp, argv[0], ds.string);
+ if (adPtr->output.length > frame.length) {
+ Tcl_SetResult(interp, adPtr->output.string + frame.length,
+ TCL_VOLATILE);
+ Ns_DStringTrunc(&adPtr->output, frame.length);
+ }
+ PopFrame(&frame);
+ --adPtr->evalLevel;
+
+ Ns_DStringFree(&ds);
+
+ return code;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NsTclIncludeCmd --
+ *
+ * Process the Tcl _ns_adp_include and _ns_adp_parse commands.
+ * This routines is the core ADP execution engine.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * Parse page for the given file is fetched from the cache and
+ * evaluated using the Tcl76 or Tcl81 code engine.
+ *
+ *----------------------------------------------------------------------
+ */
+
+#ifdef NOTDEF
+Page *
+NsAdpCopyShared(Ns_DString *dsPtr, struct stat *stPtr)
+{
+ Page *pagePtr;
+
+ pagePtr = ns_malloc(sizeof(Page) + dsPtr->length);
+ pagePtr->mtime = stPtr->st_mtime;
+ pagePtr->size = stPtr->st_size;
+ pagePtr->length = dsPtr->length + 1;
+ memcpy(pagePtr->chunks, dsPtr->string, pagePtr->length);
+ return pagePtr;
+}
+#endif
+
+int
+NsTclIncludeCmd(ClientData parse, Tcl_Interp *interp, int argc,
+ char **argv)
+{
+ struct stat st;
+ Ns_DString file, *dsPtr;
+ AdpData *adPtr;
+ Frame frame;
+#ifdef NOTDEF
+ Page *pagePtr;
+ Ns_Entry *ePtr;
+ int new, status;
+ char *p, *key;
+ Ns_Cache *cachePtr;
+#else
+ int status;
+#endif
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " file ?args ...?\"", NULL);
+ return TCL_ERROR;
+ }
+
+#ifdef NOTDEF
+ pagePtr = NULL;
+#endif
+ status = TCL_ERROR;
+ dsPtr = Ns_DStringPop();
+ Ns_DStringInit(&file);
+ adPtr = NsAdpGetData();
+
+ /*
+ * Construct the full, normalized path to the ADP file.
+ */
+
+ if (Ns_PathIsAbsolute(argv[1])) {
+ Ns_NormalizePath(&file, argv[1]);
+ } else {
+ Ns_MakePath(dsPtr, adPtr->cwd, argv[1], NULL);
+ Ns_NormalizePath(&file, dsPtr->string);
+ Ns_DStringTrunc(dsPtr, 0);
+ }
+
+ /*
+ * Check for TclPro debugging.
+ */
+
+ if (adPtr->debugLevel > 0) {
+ ++adPtr->debugLevel;
+ }
+#ifdef NOTDEF
+ else if (nsconf.adp.enabledebug != NS_FALSE &&
+ adPtr->debugFile != NULL &&
+ (p = strrchr(file.string, '/')) != NULL &&
+ Tcl_StringMatch(p+1, adPtr->debugFile)) {
+
+ Ns_Conn *conn;
+ Ns_Set *hdrs;
+ char *host, *port, *procs;
+
+ conn = Ns_TclGetConn(interp);
+ hdrs = Ns_ConnGetQuery(conn);
+ host = Ns_SetIGet(hdrs, "dhost");
+ port = Ns_SetIGet(hdrs, "dport");
+ procs = Ns_SetIGet(hdrs, "dprocs");
+ if (DebugInit(interp, host, port, procs) != TCL_OK) {
+ Ns_ConnReturnNotice(conn, 200, "Debug Init Failed",
+ interp->result);
+ adPtr->exception = ADP_ABORT;
+ goto done;
+ }
+ }
+#endif
+
+ /*
+ * Determine the cache to use (if any).
+ */
+
+#ifdef NOTDEF
+ if (adPtr->debugLevel > 0) {
+ cachePtr = NULL;
+ } else if (!nsconf.adp.threadcache) {
+ cachePtr = sharedCachePtr;
+ } else {
+ if (adPtr->cachePtr == NULL) {
+ char name[30];
+
+ sprintf(name, "adpObj.%d", Ns_ThreadId());
+ adPtr->cachePtr = Ns_CacheCreateSz(name, CACHE_KEYS, nsconf.adp.cachesize,
+ (Ns_Callback *) NsAdpFreePrivate);
+ }
+ cachePtr = adPtr->cachePtr;
+ }
+#endif
+
+ /*
+ * Verify the file is an existing, ordinary file and then either
+ * parse directly or fetch through the cache.
+ */
+
+ if (stat(file.string, &st) != 0) {
+ Tcl_AppendResult(interp, "could not stat \"",
+ file.string, "\": ", Tcl_PosixError(interp), NULL);
+ } else if (S_ISREG(st.st_mode) == 0) {
+ Tcl_AppendResult(interp, "not an ordinary file: ", file.string,
+ NULL);
+ } else /* if (cachePtr == NULL) */ {
+
+ /*
+ * Parse directly from file.
+ */
+
+ status = ParseFile(interp, file.string, st.st_size, dsPtr);
+ }
+#ifdef NOTDEF
+ else {
+#ifdef WIN32
+ key = file.string;
+#else
+ Key ukey;
+
+ ukey.dev = st.st_dev;
+ ukey.ino = st.st_ino;
+ key = (char *) &ukey;
+#endif
+ if (cachePtr != sharedCachePtr) {
+
+ /*
+ * Fetch from private, free-threaded cache.
+ */
+
+ ePtr = Ns_CacheCreateEntry(cachePtr, key, &new);
+ if (!new) {
+ pagePtr = Ns_CacheGetValue(ePtr);
+ if (pagePtr->mtime != st.st_mtime || pagePtr->size != st.st_size) {
+ Ns_CacheUnsetValue(ePtr);
+ new = 1;
+ } else {
+ status = TCL_OK;
+ }
+ }
+ if (new) {
+ status = ParseFile(interp, file.string, st.st_size, dsPtr);
+ if (status != TCL_OK) {
+ Ns_CacheDeleteEntry(ePtr);
+ } else {
+ pagePtr = NsAdpCopyPrivate(dsPtr, &st);
+ Ns_CacheSetValueSz(ePtr, pagePtr, pagePtr->size);
+ }
+ }
+ } else {
+
+ /*
+ * Fetch from shared, interlocked cache.
+ */
+
+ Ns_CacheLock(cachePtr);
+ ePtr = Ns_CacheCreateEntry(cachePtr, key, &new);
+ if (!new) {
+ while (ePtr != NULL && (pagePtr = Ns_CacheGetValue(ePtr)) == NULL) {
+ Ns_CacheWait(cachePtr);
+ ePtr = Ns_CacheFindEntry(cachePtr, key);
+ }
+ if (pagePtr == NULL) {
+ Tcl_AppendResult(interp, "wait failed for file: ", file.string, NULL);
+ } else if (pagePtr->mtime != st.st_mtime || pagePtr->size != st.st_size) {
+ Ns_CacheUnsetValue(ePtr);
+ new = 1;
+ } else {
+ Ns_DStringNAppend(dsPtr, pagePtr->chunks, pagePtr->length);
+ status = TCL_OK;
+ }
+ }
+ if (new) {
+ Ns_CacheUnlock(cachePtr);
+ status = ParseFile(interp, file.string, st.st_size, dsPtr);
+ Ns_CacheLock(cachePtr);
+ if (status != TCL_OK) {
+ Ns_CacheDeleteEntry(ePtr);
+ } else {
+ pagePtr = NsAdpCopyShared(dsPtr, &st);
+ Ns_CacheSetValueSz(ePtr, pagePtr, pagePtr->size);
+ }
+ Ns_CacheBroadcast(cachePtr);
+ }
+ Ns_CacheUnlock(cachePtr);
+ }
+ }
+#endif
+
+ /*
+ * If valid chunks where parsed or copied, push a new call frame, run
+ * the chunks, and pop the frame.
+ */
+
+ if (status == TCL_OK) {
+ PushFrame(&frame, file.string, argc-1, argv+1);
+#ifdef NOTDEF
+ if (cachePtr == NULL || cachePtr == sharedCachePtr) {
+#endif
+ status = NsAdpEval(interp, file.string, dsPtr->string);
+#ifdef NOTDEF
+ } else {
+ status = NsAdpRunPrivate(interp, file.string, pagePtr);
+ }
+#endif
+ if (parse && status == TCL_OK &&
+ adPtr->output.length > frame.length) {
+
+ Tcl_SetResult(interp, adPtr->output.string + frame.length,
+ TCL_VOLATILE);
+ Ns_DStringTrunc(&adPtr->output, frame.length);
+ }
+ PopFrame(&frame);
+ NsAdpFlush(adPtr);
+ }
+ if (adPtr->debugLevel > 0) {
+ --adPtr->debugLevel;
+ }
+
+#ifdef NOTDEF
+done:
+#endif
+ Ns_DStringFree(&file);
+ Ns_DStringPush(dsPtr);
+
+ return status;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NsTclPutsCmd --
+ *
+ * Process the Tcl ns_adp_puts command to append output.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * Output buffer is extended with given text.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+NsTclPutsCmd(ClientData ignored, Tcl_Interp *interp, int argc, char **argv)
+{
+ AdpData *adPtr;
+
+ if (argc != 2 && argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " ?-nonewline? string\"", NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3 &&
+ strcmp(argv[1], "-nonewline") != 0) {
+
+ Tcl_AppendResult(interp, "invalid flag \"",
+ argv[1], "\": expected -nonewline", NULL);
+ return TCL_ERROR;
+ }
+
+ adPtr = NsAdpGetData();
+ Ns_DStringAppend(&adPtr->output, argv[argc-1]);
+ if (argc != 3) {
+ Ns_DStringNAppend(&adPtr->output, "\n", 1);
+ }
+ NsAdpFlush(adPtr);
+
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NsTclDirCmd --
+ *
+ * Process the Tcl ns_adp_dir command to return the current ADP
+ * directory.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+NsTclDirCmd(ClientData ignored, Tcl_Interp *interp, int argc, char **argv)
+{
+ AdpData *adPtr;
+
+ if (argc != 1) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], "\"", NULL);
+ return TCL_ERROR;
+ }
+ adPtr = NsAdpGetData();
+ if (adPtr->cwd != NULL && *adPtr->cwd) {
+ Tcl_SetResult(interp, adPtr->cwd, TCL_VOLATILE);
+ } else {
+ interp->result = "/";
+ }
+
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NsTclBreakCmd --
+ *
+ * Process the Tcl ns_adp_break and ns_adp_abort commands to halt
+ * page generation.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * Break or abort exception is noted and will be handled in
+ * AdpProc.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+NsTclBreakCmd(ClientData clientData, Tcl_Interp *interp, int argc,
+ char **argv)
+{
+ AdpData *adPtr;
+
+ if (argc != 1 && argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " ?retval?\"", NULL);
+ return TCL_ERROR;
+ }
+ adPtr = NsAdpGetData();
+ adPtr->exception = (int) clientData;
+ if (argc == 2) {
+ Tcl_AppendResult(interp, argv[1], NULL);
+ }
+ return TCL_ERROR;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NsTclTellCmd --
+ *
+ * Process the Tcl ns_adp_tell commands to return the current
+ * offset within the output buffer.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+NsTclTellCmd(ClientData ignored, Tcl_Interp *interp, int argc, char **argv)
+{
+ AdpData *adPtr;
+
+ if (argc != 1) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], "\"", NULL);
+ return TCL_ERROR;
+ }
+ adPtr = NsAdpGetData();
+ sprintf(interp->result, "%d", adPtr->output.length);
+
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NsTclTruncCmd --
+ *
+ * Process the Tcl ns_adp_trunc commands to truncate the output
+ * buffer to the given length.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * Output buffer is truncated.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+NsTclTruncCmd(ClientData ignored, Tcl_Interp *interp, int argc,
+ char **argv)
+{
+ AdpData *adPtr;
+ int length;
+
+ if (argc != 1 && argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " ?length?\"", NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 1) {
+ length = 0;
+ } else {
+ if (Tcl_GetInt(interp, argv[1], &length) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (length < 0) {
+ Tcl_AppendResult(interp, "invalid length: ", argv[1], NULL);
+ return TCL_ERROR;
+ }
+ }
+ adPtr = NsAdpGetData();
+ Ns_DStringTrunc(&adPtr->output, length);
+
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NsTclDumpCmd --
+ *
+ * Process the Tcl ns_adp_dump commands to return the entire text
+ * of the output buffer.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+NsTclDumpCmd(ClientData ignored, Tcl_Interp *interp, int argc, char **argv)
+{
+ AdpData *adPtr;
+
+ if (argc != 1) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], "\"", NULL);
+ return TCL_ERROR;
+ }
+ adPtr = NsAdpGetData();
+ Tcl_SetResult(interp, adPtr->output.string, TCL_VOLATILE);
+
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NsTclArgcCmd --
+ *
+ * Process the Tcl ns_adp_args commands to return the number of
+ * arguments in the current ADP frame.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+NsTclArgcCmd(ClientData ignored, Tcl_Interp *interp, int argc, char **argv)
+{
+ AdpData *adPtr;
+
+ if (argc != 1) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], "\"", NULL);
+ return TCL_ERROR;
+ }
+ adPtr = NsAdpGetData();
+ sprintf(interp->result, "%d", adPtr->argc);
+
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NsTclArgvCmd --
+ *
+ * Process the Tcl ns_adp_args commands to return an argument (or
+ * the entire list of arguments) within the current ADP frame.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+NsTclArgvCmd(ClientData ignored, Tcl_Interp *interp, int argc, char **argv)
+{
+ AdpData *adPtr;
+ int i;
+
+ if (argc != 1 && argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " ?index?\"", NULL);
+ return TCL_ERROR;
+ }
+ adPtr = NsAdpGetData();
+ if (adPtr->argv != NULL) {
+ if (argc == 1) {
+ for (i = 0; i < adPtr->argc; ++i) {
+ Tcl_AppendElement(interp, adPtr->argv[i]);
+ }
+ } else {
+ if (Tcl_GetInt(interp, argv[1], &i) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (i > adPtr->argc) {
+ i = adPtr->argc;
+ }
+ Tcl_SetResult(interp, adPtr->argv[i], TCL_VOLATILE);
+ }
+ }
+
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NsTclBindCmd --
+ *
+ * Process the Tcl ns_adp_bind_args commands to copy arguements
+ * from the current frame into local variables.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * One or more local variables are created.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+NsTclBindCmd(ClientData ignored, Tcl_Interp *interp, int argc, char **argv)
+{
+ AdpData *adPtr;
+ int i;
+ char *arg;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " varName ?varName ...?\"", NULL);
+ return TCL_ERROR;
+ }
+ adPtr = NsAdpGetData();
+ if (adPtr->argc == 0) {
+ Tcl_AppendResult(interp, "not in an ADP", NULL);
+ return TCL_ERROR;
+ }
+ if (adPtr->argc != argc) {
+ char buf[sizeof(int) * 3 + 1];
+
+ sprintf(buf, "%d", adPtr->argc - 1);
+ Tcl_AppendResult(interp, "wrong # args: this ADP was passed ",
+ buf, " parameters", NULL);
+ return TCL_ERROR;
+ }
+
+ for (i = 1; i < argc; ++i) {
+ if (adPtr->argv != NULL && i < adPtr->argc) {
+ arg = adPtr->argv[i];
+ } else {
+ arg = "";
+ }
+ if (Tcl_SetVar(interp, argv[i], arg, TCL_LEAVE_ERR_MSG) == NULL) {
+ return TCL_ERROR;
+ }
+ }
+
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ExcepetionCmd --
+ *
+ * Process the Tcl ns_adp_exception commands to return the current
+ * exception state, ok, abort, or break.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+NsTclExceptionCmd(ClientData ignored, Tcl_Interp *interp, int argc,
+ char **argv)
+{
+ AdpData *adPtr;
+ char *exception;
+
+ if (argc != 1 && argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " ?varName?\"", NULL);
+ return TCL_ERROR;
+ }
+ adPtr = NsAdpGetData();
+ if (adPtr->exception == ADP_OK) {
+ Tcl_SetResult(interp, "0", TCL_STATIC);
+ } else {
+ Tcl_SetResult(interp, "1", TCL_STATIC);
+ }
+ if (argc == 2) {
+ switch (adPtr->exception) {
+ case ADP_OK:
+ exception = "ok";
+ break;
+ case ADP_BREAK:
+ exception = "break";
+ break;
+ case ADP_ABORT:
+ exception = "abort";
+ break;
+ case ADP_OVERFLOW:
+ exception = "overflow";
+ break;
+ case ADP_RETURN:
+ exception = "return";
+ break;
+ default:
+ exception = "unknown";
+ break;
+ }
+ if (Tcl_SetVar(interp, argv[1], exception,
+ TCL_LEAVE_ERR_MSG) == NULL) {
+ return TCL_ERROR;
+ }
+ }
+
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NsTclStreamCmd --
+ *
+ * Process the Tcl ns_adp_stream commands to enable streaming
+ * output mode.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See comments for NsAdpStreamOn.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+NsTclStreamCmd(ClientData ignored, Tcl_Interp *interp, int argc,
+ char **argv)
+{
+ if (argc != 1) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], NULL);
+ return TCL_ERROR;
+ }
+ NsAdpStreamOn();
+
+ return TCL_OK;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NsTclDebugCmd --
+ *
+ * Process the Tcl ns_adp_debug command to connect to the TclPro
+ * debugger if not already connected.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See comments for DebugInit().
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+NsTclDebugCmd(ClientData ignored, Tcl_Interp *interp, int argc,
+ char **argv)
+{
+ Tcl_AppendResult (interp, argv[0], " not supported. Sorry!");
+ return TCL_ERROR;
+#ifdef NOTDEF
+ AdpData *adPtr;
+ char *host, *port, *procs;
+
+ if (argc > 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " ?procs? ?host? ?port?\"", NULL);
+ return TCL_ERROR;
+ }
+ procs = (argc > 1) ? argv[1] : NULL;
+ host = (argc > 2) ? argv[2] : NULL;
+ port = (argc > 3) ? argv[3] : NULL;
+
+ if (DebugInit(interp, host, port, procs) != TCL_OK) {
+ Tcl_SetResult(interp, "could not initialize debugger", TCL_STATIC);
+ return TCL_ERROR;
+ }
+
+ adPtr = NsAdpGetData();
+ sprintf(interp->result, "%d", adPtr->debugLevel);
+
+ return TCL_OK;
+#endif
+}
+
+#ifdef NOTDEF
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EnableCmds --
+ *
+ * Enable the ADP Tcl commands and create ns_adp_include and
+ * ns_adp_parse commands.
+ *
+ * Results:
+ * NS_OK.
+ *
+ * Side effects:
+ * ADP commands are created in the parent Tcl interp.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+EnableCmds(Tcl_Interp *interp, void *ignored)
+{
+ char incWrapper[] =
+ "proc ns_adp_include args {\n\teval _ns_adp_include $args\n}";
+
+ Tcl_CreateCommand(interp, "_ns_adp_include",
+ NsTclIncludeCmd, (ClientData) 0, NULL);
+ Tcl_GlobalEval(interp, incWrapper);
+
+ return NS_OK;
+}
+#endif
+
+#ifdef NOTDEF
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AdpProc --
+ *
+ * Check for a normal file and call Ns_AdpRequest.
+ *
+ * Results:
+ * A standard AOLserver request result.
+ *
+ * Side effects:
+ * Depends on code embedded within page.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+AdpProc(void *parserPtr, Ns_Conn *conn)
+{
+ Ns_DString file;
+ int status;
+
+ Ns_DStringInit(&file);
+ Ns_UrlToFile(&file, NULL, conn->request->url);
+ if (access(file.string, R_OK) != 0) {
+ status = Ns_ConnReturnNotFound(conn);
+ } else {
+ status = Ns_AdpRequest(conn, file.string);
+ }
+ Ns_DStringFree(&file);
+ return status;
+}
+#endif
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Ns_AdpRequest -
+ *
+ * Invoke a file for an ADP request.
+ *
+ * Results:
+ * A standard AOLserver request result.
+ *
+ * Side effects:
+ * Depends on code embedded within page.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Ns_AdpRequest(Ns_Conn *conn, char *file)
+{
+ Tcl_Interp *interp;
+ AdpData *adPtr;
+ int status;
+ char *argv[3];
+ char *mimeType;
+ Frame frame;
+#ifdef NOTDEF
+ Ns_Set *setPtr;
+#endif
+
+ /*
+ * Push a new call frame and execute the file.
+ */
+
+ interp = Ns_GetConnInterp(conn);
+ adPtr = NsAdpGetData();
+ adPtr->conn = conn;
+ adPtr->fStream = NS_FALSE;
+#ifdef NOTDEF
+ if (nsconf.adp.enabledebug &&
+ STREQ(conn->request->method, "GET") &&
+ (setPtr = Ns_ConnGetQuery(conn)) != NULL) {
+ adPtr->debugFile = Ns_SetIGet(setPtr, "debug");
+ }
+ mimeType = Ns_GetMimeType(file);
+#else
+ mimeType = (char *)conn->content_type; /* cast loses a 'const' */
+#endif
+ if ((mimeType == NULL) || (strcmp(mimeType, "*/*") == 0)) {
+ mimeType = "text/html";
+ }
+ SetMimeType(adPtr, mimeType);
+ argv[0] = "_ns_adp_include";
+#ifdef NOTDEF
+ argv[1] = nsconf.adp.startpage ? nsconf.adp.startpage : file;
+#else
+ argv[1] = file;
+#endif
+ argv[2] = NULL;
+ PushFrame(&frame, file, 0, NULL);
+
+#ifdef NOTDEF
+ /*
+ * Set the old conn variable for backwards compatibility.
+ */
+
+ Tcl_SetVar2(interp, "conn", NULL, NsTclConnId(conn), TCL_GLOBAL_ONLY);
+#endif
+ Tcl_ResetResult(interp);
+
+ /*
+ * Ignore error - will be reported in NsAdpEval.
+ */
+
+ (void) NsTclIncludeCmd(NULL, interp, 2, argv);
+
+ switch (adPtr->exception) {
+ case ADP_ABORT:
+
+ /*
+ * Abort is normally used after a call to a
+ * ns_return function so no response is sent here.
+ */
+
+ status = NS_OK;
+ break;
+
+ case ADP_OVERFLOW:
+ Ns_ModLog(Error, nsAdpModLogHandle,
+ "stack overflow: %s", file);
+ status = Ns_ConnReturnInternalError(conn);
+ break;
+
+ default:
+#ifdef NOTDEF
+ if (nsconf.adp.enableexpire) {
+ Ns_ConnSetHeaders(conn, "Expires", "now");
+ }
+ if (Ns_ConnResponseStatus(conn) == 0) {
+ status = Ns_ConnReturnData(conn, 200,
+ adPtr->output.string,
+ adPtr->output.length,
+ adPtr->mimeType);
+ } else {
+ status = NS_OK;
+ }
+#endif
+ if (conn->sent_bodyct == 0) {
+ status = Ns_ConnReturnData(conn, 200,
+ adPtr->output.string,
+ adPtr->output.length,
+ adPtr->mimeType);
+ }
+ else {
+ status = NS_OK;
+ }
+ break;
+ }
+ PopFrame(&frame);
+
+ /*
+ * Cleanup the per-thead ADP context.
+ */
+
+ Ns_DStringTrunc(&adPtr->output, 0);
+ adPtr->exception = ADP_OK;
+ adPtr->depth = 0;
+ adPtr->argc = 0;
+ adPtr->argv = NULL;
+ adPtr->cwd = NULL;
+ adPtr->debugLevel = 0;
+ adPtr->debugInit = 0;
+ adPtr->debugFile = NULL;
+ SetMimeType(adPtr, NULL);
+
+ return status;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DelAdpData --
+ *
+ * AdpData TLS cleanup procedure.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * AdpData structure is free'ed from per-thread memory pool.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DelAdpData(void *arg)
+{
+ AdpData *adPtr = arg;
+
+ Ns_DStringFree(&adPtr->output);
+#ifdef NOTDEF
+ if (adPtr->cachePtr != NULL) {
+ Ns_CacheDestroy(adPtr->cachePtr);
+ }
+#endif
+ if (adPtr->mimeType != NULL) {
+ ns_free(adPtr->mimeType);
+ }
+ ns_free(adPtr);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ParsePage --
+ *
+ * Parse an ADP page string into text/script chunks.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Chunks are parsed into given Ns_DString.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ParsePage(Ns_DString *dsPtr, char *page)
+{
+ register char *s, *e;
+ register char *t = page;
+
+ while ((s = strstr(t, "<%")) != NULL &&
+ (e = strstr(s, "%>")) != NULL) {
+
+ *s = '\0';
+ TextChunk(dsPtr, t);
+ s += 2;
+ Ns_DStringNAppend(dsPtr, "s", 1);
+ if (*s == '=') {
+ Ns_DStringAppend(dsPtr, "ns_adp_puts -nonewline ");
+ ++s;
+ }
+ *e = '\0';
+ Ns_DStringNAppend(dsPtr, s, e-s+1);
+ t = e+2;
+ }
+ TextChunk(dsPtr, t);
+}
+
+static void
+TextChunk(Ns_DString *dsPtr, char *text)
+{
+ Ns_DStringNAppend(dsPtr, "t", 1);
+ Ns_DStringNAppend(dsPtr, text, strlen(text)+1);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PushFrame --
+ *
+ * Push an ADP call frame on the ADP stack.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The given Frame is initialized, the current working directory
+ * is determined from the absolute filename (if not NULL), the
+ * previous state of the per-thread AdpData structure is saved
+ * and then updated with the current call's arguments.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+PushFrame(Frame *framePtr, char *file, int argc, char **argv)
+{
+ register AdpData *adPtr;
+ register char *slash;
+
+ /*
+ * Save current AdpData state.
+ */
+
+ adPtr = NsAdpGetData();
+ framePtr->cwd = adPtr->cwd;
+ framePtr->length = adPtr->output.length;
+ framePtr->argc = adPtr->argc;
+ framePtr->argv = adPtr->argv;
+ adPtr->argc = argc;
+ adPtr->argv = argv;
+ ++adPtr->depth;
+
+ /*
+ * If file is not NULL it indicates a call from
+ * AdpProc or NsTclIncludeCmd. If so, update the
+ * current working directory based on the
+ * absolute file pathname.
+ */
+
+ Ns_DStringInit(&framePtr->cwdBuf);
+ if (file != NULL) {
+ slash = strrchr(file, '/');
+ Ns_DStringNAppend(&framePtr->cwdBuf, file, slash - file);
+ adPtr->cwd = framePtr->cwdBuf.string;
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PopFrame --
+ *
+ * Pop a previously pushed ADP call frame from the ADP stack.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Previous state of the per-thread AdpData structure is restored
+ * and the Frame is free'ed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+PopFrame(Frame *framePtr)
+{
+ register AdpData *adPtr;
+
+ adPtr = NsAdpGetData();
+ adPtr->argc = framePtr->argc;
+ adPtr->argv = framePtr->argv;
+ adPtr->cwd = framePtr->cwd;
+ --adPtr->depth;
+ Ns_DStringFree(&framePtr->cwdBuf);
+}
+
+#ifdef NOTDEF
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DebugInit --
+ *
+ * Initialize the debugger by calling the debug init proc with
+ * the hostname and port of the debugger and a pattern of procs
+ * to auto-instrument.
+ *
+ * Results:
+ * TCL_OK if debugger initialized, TCL_ERROR otherwise.
+ *
+ * Side effects:
+ * Interp is marked for delete on next deallocation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+DebugInit(Tcl_Interp *interp, char *host, char *port, char *procs)
+{
+ AdpData *adPtr;
+ Tcl_DString ds;
+ int code;
+
+ code = TCL_OK;
+ adPtr = NsAdpGetData();
+ if (!adPtr->debugInit) {
+ Ns_TclMarkForDelete(interp);
+ Tcl_DStringInit(&ds);
+ Tcl_DStringAppendElement(&ds, nsconf.adp.debuginit);
+ Tcl_DStringAppendElement(&ds, procs ? procs : "");
+ Tcl_DStringAppendElement(&ds, host ? host : "");
+ Tcl_DStringAppendElement(&ds, port ? port : "");
+ code = NsTclEval(interp, ds.string);
+ Tcl_DStringFree(&ds);
+ if (code != TCL_OK) {
+ Ns_TclLogError(interp);
+ return TCL_ERROR;
+ }
+ if (Tcl_LinkVar(interp, "ns_adp_output",
+ (char *) &adPtr->output.string,
+ TCL_LINK_STRING | TCL_LINK_READ_ONLY) != TCL_OK) {
+ Ns_TclLogError(interp);
+ }
+ adPtr->debugInit = 1;
+ adPtr->debugLevel = 1;
+ }
+
+ return code;
+}
+#endif
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Parse --
+ *
+ * Figure out which parser to use and then parse a page, putting
+ * the result into the dstring.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Appends to 'out'. If filename is null, it will use the default
+ * parser.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+Parse(char *filename, Ns_DString *out, char *pagein)
+{
+#ifdef NOTDEF
+ char *ext;
+ Tcl_HashEntry *hePtr;
+ Ns_AdpParserProc *parserProc;
+
+ if (filename == NULL) {
+ filename = "";
+ }
+
+ ext = strrchr(filename, '.');
+
+ if (ext == NULL) {
+ ext = "";
+ }
+
+ hePtr = Tcl_FindHashEntry(&extensionsTable, ext);
+ if (hePtr == NULL) {
+ parserProc = defParserProc;
+ } else {
+ parserProc = (Ns_AdpParserProc *) Tcl_GetHashValue(hePtr);
+ }
+ (*parserProc)(out, pagein);
+#else
+ FancyParsePage(out, pagein);
+#endif
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ParseFile --
+ *
+ * Read and parse text from a file.
+ *
+ * Results:
+ * TCL_OK or TCL_ERROR.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ParseFile(Tcl_Interp *interp, char *file, size_t size, Ns_DString *dsPtr)
+{
+ int fd, status;
+ size_t n;
+ char *p;
+
+ status = TCL_ERROR;
+ fd = open(file, O_RDONLY);
+ if (fd < 0) {
+ Tcl_AppendResult(interp, "could not open \"",
+ file, "\": ", Tcl_PosixError(interp), NULL);
+ } else {
+ p = ns_malloc(size + 1);
+ n = read(fd, p, size);
+ close(fd);
+ if (n < 0) {
+ Tcl_AppendResult(interp, "read() of \"", file,
+ "\" failed: ", Tcl_PosixError(interp), NULL);
+ } else if (n != size) {
+ Tcl_AppendResult(interp, "incomplete read from: ",
+ file, NULL);
+ } else {
+ p[n] = '\0';
+ Parse(file, dsPtr, p);
+ status = TCL_OK;
+ }
+ ns_free(p);
+ }
+ return status;
+}
+
+
+void
+NsAdpLogError(Tcl_Interp *interp, char *file, int chunk)
+{
+ Ns_DString ds;
+#ifdef NOTDEF
+ char *eargv[4];
+#endif
+ AdpData *adPtr;
+
+ Ns_DStringInit(&ds);
+ Ns_DStringAppend(&ds, "\n invoked from within chunk: ");
+ Ns_DStringPrintf(&ds, "%d", chunk);
+ Ns_DStringAppend(&ds, " of adp: ");
+ Ns_DStringAppend(&ds, file);
+ Tcl_AddErrorInfo(interp, ds.string);
+ Ns_TclLogError(interp);
+ Ns_DStringFree(&ds);
+ adPtr = NsAdpGetData();
+#ifdef NOTDEF
+ if (nsconf.adp.errorpage != NULL && adPtr->errorLevel == 0) {
+ ++adPtr->errorLevel;
+ eargv[0] = "";
+ eargv[1] = nsconf.adp.errorpage;
+ eargv[2] = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
+ if (eargv[2] == NULL) {
+ eargv[2] = interp->result;
+ }
+ eargv[3] = NULL;
+ (void) NsTclIncludeCmd(NULL, interp, 3, eargv);
+ --adPtr->errorLevel;
+ }
+#endif
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NsTclAdpParseCmd --
+ *
+ * Process the ns_adp_parse command to evaluate strings or
+ * ADP files at the current call frame level.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See comments for NsTclAdpEvalCmd and NsTclIncludeCmd.
+ *
+ *----------------------------------------------------------------------
+ */
+
+#define NUM_STATIC_ARGS 10
+
+int
+NsTclAdpParseCmd(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
+{
+ int i;
+ int filearg;
+ int isstring = NS_TRUE;
+ int retval;
+ char **pargv, *pargvStatic[NUM_STATIC_ARGS+1];
+ int pargc;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " ?switches? arg ?arg1? ?arg2? ?...?\"", NULL);
+ return TCL_ERROR;
+ }
+
+ filearg = 1;
+ for (i = 1; i < argc; i++) {
+ if (STRIEQ(argv[i], "-file")) {
+ filearg++;
+ isstring = NS_FALSE;
+ } else if (STRIEQ(argv[i], "-string")) {
+ filearg++;
+ isstring = NS_TRUE;
+ } else if (STRIEQ(argv[i], "-global")) {
+ Tcl_SetResult(interp,
+ "deprecated -global switch passed to ns_adp_parse",
+ TCL_STATIC);
+ return TCL_ERROR;
+ } else if (STRIEQ(argv[i], "-local")) {
+ filearg++;
+ } else {
+ break;
+ }
+ }
+
+ /*
+ * Construct new arguments and call either NsTclAdpEvalCmd for strings
+ * or NsTclIncludeCmd (with ClientData set to 1) for files.
+ */
+
+ pargc = argc - filearg + 1;
+ if (pargc <= NUM_STATIC_ARGS) {
+ pargv = pargvStatic;
+ } else {
+ pargv = ns_malloc(sizeof(char *) * (pargc + 1));
+ }
+ pargv[0] = argv[0];
+ for (i = 0; i < pargc; i++) {
+ pargv[i+1] = argv[filearg++];
+ }
+ if (isstring) {
+ retval = NsTclAdpEvalCmd(NULL, interp, pargc, pargv);
+ } else {
+ retval = NsTclIncludeCmd((ClientData) 1, interp, pargc, pargv);
+ }
+ if (pargv != pargvStatic) {
+ ns_free(pargv);
+ }
+ return retval;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SetMimeType --
+ *
+ * Sets the mime type for this adp context.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Updates the mime type for this adp context, using ns_strcopy.
+ * Existing mime type is freed if not NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+SetMimeType(AdpData *adPtr, char *mimeType)
+{
+ if (adPtr->mimeType) {
+ ns_free(adPtr->mimeType);
+ }
+ adPtr->mimeType = ns_strcopy(mimeType);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NsTclAdpMimeCmd --
+ *
+ * Process the ns_adp_mime command to set or get the mime type
+ * returned upon completion of the parsed file.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * Potentially updates the mime type for this adp page.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+NsTclAdpMimeCmd(ClientData dummy, Tcl_Interp *interp, int argc, char **argv)
+{
+ AdpData *adPtr;
+
+ if (argc != 1 && argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " ?mimeType?\"", NULL);
+ return TCL_ERROR;
+ }
+
+ adPtr = NsAdpGetData();
+ if (argc == 2) {
+ SetMimeType(adPtr, argv[1]);
+ }
+ Tcl_SetResult(interp, adPtr->mimeType, TCL_VOLATILE);
+
+ return TCL_OK;
+}
+
+/* And an addition... */
+
+void add_adp_commands (Tcl_Interp *interp)
+{
+ Tcl_CreateCommand (interp, "ns_puts", NsTclPutsCmd, NULL, NULL);
+ Tcl_CreateCommand (interp, "ns_adp_puts", NsTclPutsCmd, NULL, NULL);
+ Tcl_CreateCommand (interp, "ns_adp_dir", NsTclDirCmd, NULL, NULL);
+ Tcl_CreateCommand (interp, "ns_adp_break", NsTclBreakCmd,
+ (ClientData)ADP_BREAK, NULL);
+ Tcl_CreateCommand (interp, "ns_adp_return", NsTclBreakCmd,
+ (ClientData)ADP_RETURN, NULL);
+ Tcl_CreateCommand (interp, "ns_adp_abort", NsTclBreakCmd,
+ (ClientData)ADP_ABORT, NULL);
+ Tcl_CreateCommand (interp, "ns_adp_exception", NsTclExceptionCmd,
+ NULL, NULL);
+ Tcl_CreateCommand (interp, "ns_adp_argc", NsTclArgcCmd, NULL, NULL);
+ Tcl_CreateCommand (interp, "ns_adp_argv", NsTclArgvCmd, NULL, NULL);
+ Tcl_CreateCommand (interp, "ns_adp_bind_args", NsTclBindCmd, NULL, NULL);
+ Tcl_CreateCommand (interp, "ns_adp_tell", NsTclTellCmd, NULL, NULL);
+ Tcl_CreateCommand (interp, "ns_adp_trunc", NsTclTruncCmd, NULL, NULL);
+ Tcl_CreateCommand (interp, "ns_adp_dump", NsTclDumpCmd, NULL, NULL);
+ Tcl_CreateCommand (interp, "ns_adp_eval", NsTclAdpEvalCmd, NULL, NULL);
+ Tcl_CreateCommand (interp, "ns_adp_parse", NsTclAdpParseCmd, NULL, NULL);
+ Tcl_CreateCommand (interp, "ns_adp_stream", NsTclStreamCmd, NULL, NULL);
+ Tcl_CreateCommand (interp, "ns_adp_mimetype", NsTclAdpMimeCmd, NULL, NULL);
+}
Index: mod_nsd/adp.h
===================================================================
RCS file: /usr/local/cvsroot/mod_nsd/adp.h,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ mod_nsd/adp.h 13 Apr 2001 21:09:32 -0000 1.1
@@ -0,0 +1,56 @@
+typedef void (Ns_AdpParserProc)(Ns_DString *, char *);
+
+typedef struct AdpData {
+ int exception;
+ int depth;
+ int argc;
+ char **argv;
+ char *cwd;
+ char *mimeType;
+ int evalLevel;
+ int errorLevel;
+ int debugLevel;
+ int debugInit;
+ char *debugFile;
+ Ns_DString output;
+ Ns_Conn *conn;
+ int fStream;
+ Ns_AdpParserProc *parserProc;
+ /* Ns_Cache *cachePtr; */
+} AdpData;
+
+#define ADP_OK 0
+#define ADP_BREAK 1
+#define ADP_ABORT 2
+#define ADP_OVERFLOW 3
+#define ADP_RETURN 4
+
+void NsAdpInit (void);
+void NsAdpFlush(AdpData *adPtr);
+int Ns_AdpRequest(Ns_Conn *conn, char *file);
+AdpData *NsAdpGetData ();
+void add_adp_commands (Tcl_Interp *interp);
+extern Ns_ModLogHandle nsAdpModLogHandle;
+void FancyParsePage(Ns_DString *outPtr, char *in);
+void NsAdpFancyInit(char *hServer, char *path);
+
+void NsAdpLogError(Tcl_Interp *interp, char *file, int chunk);
+
+ns_tcl_command NsTclParseHeaderCmd;
+ns_tcl_command NsTclPutsCmd;
+ns_tcl_command NsTclDirCmd;
+ns_tcl_command NsTclBreakCmd;
+ns_tcl_command NsTclExceptionCmd;
+ns_tcl_command NsTclArgcCmd;
+ns_tcl_command NsTclArgvCmd;
+ns_tcl_command NsTclBindCmd;
+ns_tcl_command NsTclTellCmd;
+ns_tcl_command NsTclTruncCmd;
+ns_tcl_command NsTclDumpCmd;
+ns_tcl_command NsTclAdpEvalCmd;
+ns_tcl_command NsTclAdpParseCmd;
+ns_tcl_command NsTclStreamCmd;
+ns_tcl_command NsTclDebugCmd;
+ns_tcl_command NsTclAdpMimeCmd;
+ns_tcl_command NsTclRegisterTagCmd;
+ns_tcl_command NsTclRegisterAdpCmd;
Index: mod_nsd/adpfancy.c
===================================================================
RCS file: /usr/local/cvsroot/mod_nsd/adpfancy.c,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ mod_nsd/adpfancy.c 13 Apr 2001 21:09:32 -0000 1.1
@@ -0,0 +1,1119 @@
+/*
+ * mod_aolserver aolserver emulation --- Copyright 2000 Robert S. Thau.
+ * This file derived from the actual aolserver code, and is distributed
+ * in accord with its license, as follows:
+ *
+ * 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.lcs.mit.edu/.
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+
+/*
+ * adpfancy.c --
+ *
+ * Support for registered tags within ADPs, the ");
+ if (end == NULL) {
+ Ns_ModLog(Warning, nsAdpModLogHandle,
+ "unterminated script");
+ AddTextChunk(outPtr, oldtop, strlen(oldtop));
+ Ns_SetFree(params);
+ break;
+ } else {
+ StartScript(outPtr);
+ if (stream != NULL && strcasecmp(stream, "on") == 0) {
+ AppendChunk(outPtr, "ns_adp_stream\n");
+ }
+ NAppendChunk(outPtr, top, end-top);
+ EndChunk(outPtr);
+ top = end + 9;
+ }
+ } else {
+ /*
+ * Not a server-side script, so add as text.
+ */
+
+ AddTextChunk(outPtr, tag.string, tag.length);
+ }
+ Ns_SetFree(params);
+ } else if ((rtPtr = GetRegTag(tag.string + 1)) != NULL) {
+
+ /*
+ * It is a registered tag. In this case, we generate
+ * a bolus of tcl code that will call it.
+ */
+
+ int i;
+ char *end = NULL;
+
+ params = TagToSet(tag.string);
+
+ /*
+ * If it requires an endtag then ensure that there
+ * is one. If not, warn and spew text.
+ */
+
+ if (rtPtr->endtag &&
+ ((end = BalancedEndTag(top, rtPtr)) == NULL)) {
+
+ Ns_ModLog(Warning, nsAdpModLogHandle,
+ "unterminated registered tag %s", rtPtr->tag);
+ AddTextChunk(outPtr, oldtop, strlen(oldtop));
+ Ns_SetFree(params);
+ break;
+ }
+
+ /*
+ * Write Tcl code to put all the parameters into a set, then
+ * call the proc with that set (and the input, if any).
+ */
+
+ StartScript(outPtr);
+ AppendChunk(outPtr, "set _ns_tempset [ns_set create \"\"]\n");
+ for (i=0; i < Ns_SetSize(params); i++) {
+ AppendChunk(outPtr, "ns_set put $_ns_tempset \"");
+ AppendTclEscaped(outPtr, Ns_SetKey(params, i));
+ AppendChunk(outPtr, "\" \"");
+ AppendTclEscaped(outPtr, Ns_SetValue(params, i));
+ AppendChunk(outPtr, "\"\n");
+ }
+ AppendChunk(outPtr, "ns_puts -nonewline [");
+ if (rtPtr->procname) {
+ /*
+ * This uses the old-style registered procedure
+ */
+ AppendChunk(outPtr, rtPtr->procname);
+ } else {
+ /*
+ * This uses the new and improved registered ADP.
+ */
+ AppendChunk(outPtr, "ns_adp_eval \"");
+ AppendTclEscaped(outPtr, rtPtr->adpstring);
+ AppendChunk(outPtr, "\" ");
+ }
+ AppendChunk(outPtr, " ");
+
+ /*
+ * Backwards compatibility is broken here because a conn is
+ * never passed
+ */
+ if (end != NULL) {
+ /*
+ * This takes an endtag, so pass it content (the text between
+ * the start and end tags).
+ */
+ AppendChunk(outPtr, "\"");
+ NAppendTclEscaped(outPtr, top, end-top-1);
+ AppendChunk(outPtr, "\" ");
+ }
+ AppendChunk(outPtr, "$_ns_tempset]\n");
+ EndChunk(outPtr);
+
+ /*
+ * Advance top past the end of the close tag
+ * (if there is no closetag, top should already be
+ * properly advanced thanks get ReadToken)
+ */
+ if (end != NULL) {
+ while (*end != '\0' && *end != '>') {
+ end++;
+ }
+ if (*end == '>') {
+ end++;
+ }
+ top = end;
+ }
+ Ns_SetFree(params);
+ } else {
+ /*
+ * It's just a chunk of text.
+ */
+
+ AddTextChunk(outPtr, tag.string, tag.length);
+ }
+ Ns_DStringTrunc(&tag, 0);
+
+ oldtop = top;
+ }
+#ifdef NOTDEF
+ if (nsconf.adp.taglocks) {
+ Ns_RWLockUnlock(&tlock);
+ }
+#endif
+ Ns_DStringFree(&tag);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TagToSet --
+ *
+ * Given a tag such as