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. + + + +

Prerequisites

+ +In order to use this software, you will need these other packages: + + + +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. + +

Compiling and Installing

+ +mod_aolserver uses GNU autoconf to configure the build process. You can +type ./configure --help to see your options. The important +ones are these: + + + +

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. + +

Configuring Apache

+ +mod_aolserver provides two request handlers: + + + +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

+ +

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. + +

Limitations, Bugs and To-Do List

+ +Send bug reports and other feedback to +mod_aolserver@arsdigita.com. + +

+Here are the known limitations, bugs, and to-do tasks. + +

+ +
+
$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