You are viewing a plain text version of this content. The canonical link for it is here.
Posted to axkit-dev@xml.apache.org by ma...@apache.org on 2002/01/13 21:45:13 UTC

cvs commit: xml-axkit/t 00basic.t 01provider.t 02language.t 03stylechooser.t 04plugins.t 05mediachooser.t test_module.pl

matts       02/01/13 12:45:12

  Added:       .        AxKit.xs CONTRIB Changes INSTALL MANIFEST
                        Makefile.PL README SUPPORT TODO axconfig.c
                        axconfig.h getstyles.c getstyles.h typemap
               axkit.org faq.xml features.xml index.xml install.xml
                        license.xml mailinglist.xml news.xml sidebar.xml
                        support.xml
               axkit.org/docs associating_stylesheets.xml index.xml
                        introduction.dkb provider-howto.dkb quick_start.dkb
               axkit.org/docs/presentations/tpc2001 ax_logo.png redbg.png
                        tpc-axkit.axp
               axkit.org/docs/xpathscript guide.dkb
               axkit.org/docs/xsp guide.dkb index.xml
               axkit.org/examples cv.css cv.xml cv.xps cv_example.xml
                        docbook.dkb resume.dtd
               axkit.org/img arrow.gif arrow_sel.gif banner2.gif
                        banner3.gif banner_axkit.gif banner_bg.gif
                        banner_end.gif bg.gif bullet.gif corner_logo.jpg
                        corner_logo.png nav_bot.gif nav_left.gif
                        nav_right.gif nav_top.gif pix.gif rcorner_br.gif
                        rcorner_wh.gif vsep.gif
               axkit.org/stylesheets axkit.css axkit_html.css
                        body_print_html.xps body_tag_html.xps
                        docbook_example.xml docbook_print.xps
                        docbook_screen.xps docbook_tags.xps error.xps
                        error.xsl irc.xps navbar_html.xps rdf_html.xps
                        sidebar_html.xps spacer.xps webpage_html.xps
               axkit.org/stylesheets/new rdf_html.xps sidebar_html.xps
                        webpage_html.xps
               examples .htaccess default.notxslt default.xps docbook.dkb
                        docbook_print.xps docbook_screen.xps
                        docbook_tags.xps index.xml
               lib/Apache/AxKit Cache.pm CharsetConv.pm CharsetConv.xs
                        ConfigReader.pm Exception.pm Language.pm
                        Makefile.PL Provider.pm typemap
               lib/Apache/AxKit/Language AxPoint.pm LibXSLT.pm NotXSLT.pm
                        PassiveTeX.pm Query.pm Sablot.pm XMLNewsNITF.pm
                        XMLNewsRDF.pm XPathScript.pm XSLT.pm XSP.pm
               lib/Apache/AxKit/Language/XSP TaglibHelper.pm
               lib/Apache/AxKit/MediaChooser WAPCheck.pm
               lib/Apache/AxKit/Plugin Fragment.pm Passthru.pm
                        QueryStringCache.pm
               lib/Apache/AxKit/Provider File.pm Filter.pm Scalar.pm
               lib/Apache/AxKit/StyleChooser Cookie.pm FileSuffix.pm
                        PathInfo.pm QueryString.pm UserAgent.pm
               t        00basic.t 01provider.t 02language.t
                        03stylechooser.t 04plugins.t 05mediachooser.t
                        test_module.pl
  Log:
  Initial file checkin
  
  Revision  Changes    Path
  1.1                  xml-axkit/AxKit.xs
  
  Index: AxKit.xs
  ===================================================================
  /* $Id: AxKit.xs,v 1.1 2002/01/13 20:45:08 matts Exp $ */
  
  #ifdef __cplusplus
  extern "C" {
  #endif
  #ifdef WIN32
  #define _INC_DIRENT
  #define DIR void
  #endif
  #include <EXTERN.h>
  #include <perl.h>
  #include <XSUB.h>
  #include "axconfig.h"
  #include "getstyles.h"
  #ifdef __cplusplus
  }
  #endif
  
  SV * error_str = NULL;
  
  #ifdef HAVE_LIBXML2
  char * axBuildURI(pool *p, const char *URI, const char *base)
  {
      return ap_pstrdup(p, (char *)xmlBuildURI(URI, base));
  }
  #else
  char * axBuildURI(pool *p, const char *URI, const char *base)
  {
      if (URI[0] != '/') {
          return ap_pstrdup(p, ap_make_full_path(p, ap_make_dirstr_parent(p, base), URI));
      }
      else {
          return (char*)URI;
      }
  }
  #endif
  
  pool *
  get_startup_pool(void)
  {
      SV *sv = perl_get_sv("Apache::__POOL", FALSE);
      if(sv) {
          IV tmp = SvIV((SV*)SvRV(sv));
          return (pool *)tmp;
      }
      return NULL;
  }
  
  int
  call_method_int(SV * obj, char * method)
  {
      dSP;
      int cnt;
      int results = -1;
      
      ENTER;
      SAVETMPS;
      
      PUSHMARK(SP);
      EXTEND(SP, 1);
      PUSHs(obj);
      PUTBACK;
      
      cnt = perl_call_method(method, G_SCALAR);
      
      SPAGAIN;
      
      if (cnt != 1) {
          croak("read method call failed");
      }
      
      results = POPi;
      
      FREETMPS;
      LEAVE;
      
      return results;
  }
  
  SV *
  call_method_sv(SV * obj, char * method)
  {
      dSP;
      int cnt;
      STRLEN n_a;
      SV * results;
      ENTER;
      SAVETMPS;
      
      PUSHMARK(SP);
      EXTEND(SP, 1);
      PUSHs(obj);
      PUTBACK;
      
      cnt = perl_call_method(method, G_SCALAR);
      
      SPAGAIN;
      
      if (cnt != 1) {
          croak("read method call failed");
      }
      
      results = NEWSV(0,0);
      sv_catsv(results, POPs);
      
      FREETMPS;
      LEAVE;
      
      /* SvREFCNT_inc(results); */
      
      return results;
  }
  
  #define BUFSIZE 1024
  
  MODULE = AxKit		PACKAGE = AxKit
  
  PROTOTYPES: DISABLE
  
  BOOT:
      # warn("AxKit: BOOT\n");
      if (!ap_find_linked_module(ap_find_module_name(&XS_AxKit))) {
          # warn("AxKit: add_module()\n");
          ap_add_module(&XS_AxKit);
      }
      ap_register_cleanup(get_startup_pool(), NULL,
              remove_module_cleanup, null_cleanup);
  
  void
  END ()
      CODE:
          # warn("AxKit: END\n");
          if (ap_find_linked_module(ap_find_module_name(&XS_AxKit))) {
              # warn("AxKit: remove_module()\n");
              ap_remove_module(&XS_AxKit);
          }
  
  SV *
  get_config (r)
          Apache  r
      PREINIT:
          axkit_dir_config * cfg;
          HV * config;
      CODE:
          cfg = (axkit_dir_config *)
                  ap_get_module_config(r->per_dir_config, &XS_AxKit);
          
          if (!cfg) {
              XSRETURN_UNDEF;
          }
          
          config = ax_get_config(cfg);
          RETVAL = newRV_noinc((SV*)config);
      OUTPUT:
          RETVAL
  
  void
  load_module (name)
          char * name
      CODE:
          maybe_load_module(name);
  
  void
  reconsecrate (obj, class)
          SV * obj
          char * class
      CODE:
          maybe_load_module(class);
          sv_bless(obj, gv_stashpv(class, FALSE));
  
  char *
  build_uri (r, uri, base)
          Apache r
          char * uri
          char * base
      CODE:
          RETVAL = axBuildURI(r->pool, uri, base);
      OUTPUT:
          RETVAL
  
  void
  Debug (level, ...)
          int level
      PREINIT:
          STRLEN n_a;
          request_rec * r;
          SV * str;
          int debuglevel;
          axkit_dir_config * cfg;
      CODE:
          r = perl_request_rec(NULL);
          if (r == NULL) {
              return;
          }
          cfg = (axkit_dir_config *)
                  ap_get_module_config(r->per_dir_config, &XS_AxKit);
          if (level > cfg->debug_level) {
              return;
          }
          str = NEWSV(0, 256);
          sv_setpvn(str, "", 0);
          if (items > 1) {
              int i;
              char * last;
              for (i = 1; i < (items - 1); i++) {
                  sv_catpv(str, SvPV(ST(i), n_a));
              }
              last = SvPV(ST(items - 1), n_a);
              if (last[strlen(last)] == '\n') {
                  sv_catpvn(str, last, strlen(last) - 1);
              }
              else {
                  sv_catpv(str, last);
              }
          }
          ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r, "[AxKit] %s", SvPV(str, n_a));
          SvREFCNT_dec(str);
  
  #ifdef HAVE_LIBXML2
  
  MODULE = AxKit		PACKAGE = Apache::AxKit::Provider
  
  PROTOTYPES: DISABLE
  
  SV *
  _new(class, r, ...)
          char * class
          SV * r
      PREINIT:
          HV * hash;
          SV * alternate;
          STRLEN n_a;
          int item_id;
          SV * cfg;
          SV * key;
          int cnt;
          SV * obj;
          AV * item_store;
      CODE:
          hash = newHV();
          hv_store(hash, "apache", 6, r, 0);
          
          obj = newRV_noinc((SV*)hash);
          sv_bless(obj, gv_stashpv(class, 0));
          
          item_store = newAV();
          for (item_id = 2; item_id < items; item_id++) {
              av_push(item_store, ST(item_id));
          }
          
          if (alternate = call_method_sv(perl_get_sv("AxKit::Cfg", FALSE), "ProviderClass")) {
              SV * tmp;
              sv_bless(obj, gv_stashsv(alternate, 0));
              SvREFCNT_dec(alternate);
          }
          {
              dSP;
              ENTER;
              SAVETMPS;
              
              PUSHMARK(SP);
              EXTEND(SP, (items + 1));
              PUSHs(obj);
              for (item_id = 0; item_id <= av_len(item_store); item_id++) {
                  PUSHs(*av_fetch(item_store, item_id, 0));
              }
              PUTBACK;
              
              cnt = perl_call_method("init", G_VOID);
              
              SPAGAIN;
              
              if (cnt != 0) {
                  croak("init method call failed");
              }
              
              POPs;
              
              FREETMPS;
              LEAVE;
          }
          key = call_method_sv(obj, "key");
          {
              dSP;
              ENTER;
              SAVETMPS;
              
              PUSHMARK(SP);
              EXTEND(SP, 1);
              PUSHs(key);
              PUTBACK;
              
              cnt = perl_call_pv("AxKit::add_depends", G_VOID);
              
              SPAGAIN;
              
              if (cnt != 1) {
                  croak("add_depends method call failed");
              }
              
              POPs;
              
              FREETMPS;
              LEAVE;
          }
          SvREFCNT_dec(key);
          SvREFCNT_dec(item_store);
          RETVAL = obj;
      OUTPUT:
          RETVAL
  
  SV *
  xs_get_styles_fh(r, ioref)
          Apache  r
          SV * ioref
      PREINIT:
          axkit_xml_bits results;
          xmlParserCtxtPtr ctxt;
          char buffer[BUFSIZE];
          int read_length;
          
          SV * tbuff;
          SV * tsize;
          int done = 0;
          int ret;
          AV * return_array;
      CODE:
          results.apache = r;
          results.xml_stylesheet = newAV();
          results.start_element = 0;
          results.start_attribs = 0;
          results.dtd = 0;
          results.publicid = 0;
          
          ret = -1;
          
          error_str = newSVpv("", 0);
          
          xmlInitParser();
          
          xmlDoValidityCheckingDefaultValue = 0;
          xmlSubstituteEntitiesDefaultValue = 0;
          xmlLoadExtDtdDefaultValue = 0;
          
          read_length = read_perl(ioref, buffer, 4);
          if (read_length > 0) {
              ctxt = xmlCreatePushParserCtxt(axkitSAXHandler, 
                          NULL, buffer, read_length, "filename");
              ctxt->userData = (void*)&results;
              
              while(read_length = read_perl(ioref, buffer, BUFSIZE)) {
                  xmlParseChunk(ctxt, buffer, read_length, 0);
              }
              ret = xmlParseChunk(ctxt, buffer, 0, 1);
              
              xmlFreeParserCtxt(ctxt);
          }
          
          sv_2mortal(error_str);
          
          xmlCleanupParser();
          
          if (ret == -1) {
              croak("xmlParse couldn't read file!");
          }
          
          if (ret != XML_ERR_OK && ret != XML_ERR_UNDECLARED_ENTITY) {
              STRLEN len;
              croak("xmlParse returned error: %d, %s", ret, SvPV(error_str, len));
          }
          
          return_array = newAV();
          av_push(return_array, newRV_noinc((SV*)results.xml_stylesheet));
          av_push(return_array, newSVpv(results.start_element, 0));
          av_push(return_array, newRV_noinc((SV*)results.start_attribs));
          
          if (results.dtd != NULL) {
              av_push(return_array, newSVpv(results.dtd, 0));
          }
          else {
              av_push(return_array, NEWSV(1,0));
          }
          
          if (results.publicid != NULL) {
              av_push(return_array, newSVpv(results.publicid, 0));
          }
          else {
              av_push(return_array, NEWSV(1,0));
          }
          
          RETVAL = newRV_noinc((SV*)return_array);
          
      OUTPUT:
          RETVAL
  
  SV *
  xs_get_styles_str(r, xmlstring)
          Apache  r
          SV * xmlstring
      PREINIT:
          axkit_xml_bits results;
          xmlParserCtxtPtr ctxt;
          int ret;
          STRLEN len;
          char * ptr;
          AV * return_array;
      CODE:
          results.apache = r;
          results.xml_stylesheet = newAV();
          results.start_element = 0;
          results.dtd = 0;
          results.publicid = 0;
          
          ptr = SvPV(xmlstring, len);
          
          error_str = newSVpv("", 0);
          
          xmlInitParser();
          
          xmlDoValidityCheckingDefaultValue = 0;
          xmlSubstituteEntitiesDefaultValue = 0;
          xmlLoadExtDtdDefaultValue = 0;
          
          if (!ptr || len < 4) {
              XSRETURN_UNDEF;
          }
          
          ret = xmlSAXUserParseMemory(axkitSAXHandler, (void*)&results, ptr, len);
          
          sv_2mortal(error_str);
          
          xmlCleanupParser();
          
          if (ret != XML_ERR_OK && ret != XML_ERR_UNDECLARED_ENTITY) {
              croak("xmlParse returned error: %d, %s", ret, SvPV(error_str, len));
          }
          
          return_array = newAV();
          av_push(return_array, newRV_noinc((SV*)results.xml_stylesheet));
          av_push(return_array, newSVpv(results.start_element, 0));
          av_push(return_array, newRV_noinc((SV*)results.start_attribs));
          
          if (results.dtd != NULL) {
              av_push(return_array, newSVpv(results.dtd, 0));
          }
          else {
              av_push(return_array, NEWSV(1,0));
          }
          
          if (results.publicid != NULL) {
              av_push(return_array, newSVpv(results.publicid, 0));
          }
          else {
              av_push(return_array, NEWSV(1,0));
          }
          
          RETVAL = newRV_noinc((SV*)return_array);
          
      OUTPUT:
          RETVAL
  
  #endif /* HAVE_LIBXML2 */
  
  
  
  
  1.1                  xml-axkit/CONTRIB
  
  Index: CONTRIB
  ===================================================================
  The following people have contributed to AxKit:
  
  Kip Hampton (moral support, Util taglib, docs, StyleChoosers)
  Vincent Cagnard (Apache::Filter support (although it was re-done))
  Owen Stenseth (output filtering single point of exit, and others)
  Honza Pazdziora (many small patches)
  Brian Wheeler (Filter fixes, XPathScript improvements, AxAddDynamicProcessor)
  
  If I've missed anyone, please correct this list as my memory
  is poor.
  
  If you wish to contribute to AxKit, please join the axkit-dev
  mailing list, by sending a mail to: 
  
      axkit-dev-subscribe@xml.apache.org
  
  The list will show you all check-ins and allow you to discuss
  the features you want, and how to go about implementing them. Please
  bring things up there before randomly sending in a patch.
  
  Also note that all code submitted to inclusion in the AxKit project
  becomes the intellectual property of the Apache Software
  Foundation, and you may have no claims on code submitted to
  the project. However we will list you as a contributor for larger
  patches.
  
  Please also see the development guidelines on http://xml.apache.org/
  
  
  
  
  1.1                  xml-axkit/Changes
  
  Index: Changes
  ===================================================================
  1.4
   - Config directives no longer implemented by Apache::ExtUtils
   - New TaglibHelper module, makes writing XSP taglibs almost trivial.
   - Build checks for iconv library - should make things smoother on *BSD
   - Known bug in Filter provider is fixed
   - Major memory leak cleanup
   - Added LibXSLT language module (for use with XML::LibXSLT). This is
     over twice as fast as XML::Sablotron, and more compliant.
   - Added AxAddURIProcessor config directive
   - Added AxLogDeclines config directive (replaces PerlSetVar equivalent)
   - Added import_templates() to XPathScript
   - XSP now uses SAX to generate code
   - Sablot.pm allows you to set the outgoing mime type
   - Many fixes to AxKit.xs compiled directives (hopefully making it work
     for more people now)
   - Removed eval{} stuff around XML parsing in Provider.pm as it seemed to
     be causing segfaults in Perl 5.6.1
   - Fixes to LibXSLT language module to make it work with new versions of
     XML::LibXSLT (and the core libxslt library).
   - Cleaned up error handling, and error stylesheets significantly
   - Removed Storable from XPathScript (to get control of the segfaults)
   - Implemented has_changed() for all "cache" bits
   - Improved error handling with an AxStackTrace config directive
     allowing the Error Stylesheet to get a full stack trace.
   - Doc fixes to stop pod2man complaining.
   - AxKit now adds AxKit/Version to your Server string (for netcraft!)
   - Switched xml_string to pnotes so you can have binary nulls in the output
     (e.g. for PDFs).
   - Added some configuration tidbits to INSTALL
   - Much better (though not 100% there) test harness code
   - Major fixes to Makefile.PL (to work better on *BSD and Win32)
   - XML::Parser no longer needed if you have libxml2 installed. This will
     allow AxKit to work with Apaches that have expat enabled!
   - XSP pages can implement a has_changed() function, which allows you to
     control the caching of the results (can have significant performance
     increases on dynamic pages)
   - Providers responsible for returning declined (this allows for non-file
     providers to not end up in a 404 when doing e.g. passthru)
   - Added AxNoCache option to turn off caching on purpose.
   - split AxKit.xs into more managable files
   - Better error messages from CharsetConv (iconv)
   - Added a PDF slideshow builder called AxPoint
   - Sablot fix for changing content-type.
   - Much cleaner handling of character set conversions
   - AxKit::Apache->request() added (similar to Apache->request())
   - Cache maintains content-type more sanely.
   - Many, many minor bug fixes.
  
  1.3
      - Re-written XSP engine
      - Much better dependencies checking
      - XPathScript adds import_template() function and other minor changes
      - Better shared memory usage when using directives in httpd.conf
      - All XSP namespaces updated to Apache normalised versions (these are
        http://apache.org/xsp/<module>/<version> although these may well
        change again shortly)
  
  
  1.2
      - XPathScript can interpolate "{xpath}" in $t hash
      - reduced required modules list
      - Removed Sablotron from AxKit.pm due to some segfaults
      - Fixed segfaults in aborted conversions with iconv
      - Added AxResetProcessors
      - Improved Apache::Filter support
      - Much improved installer
      - Many XSP changes/fixes. Now uses XML::XPath DOM tree.
      - Memoize methods that call stat() in Cache and Providers
      - Many miscellaneous improvements
      
  1.1
  
      - Uses iconv rather than Unicode::* modules to do character
        set conversions
      - Fix for AxMediaType/AxStyleName bug (Owen Stenseth)
      - Fix for compilation under mod_perl 1.24_01
      - Fix for relative URI's and file:// sub requests
      - Minor stylesheet cache bug fixed
      - Updated Filter provider for Apache::Filter >= 1.13
      - Fix for filehandle leak
      - Fix for Virtual Host Stash bug.
  
  
  
  1.1                  xml-axkit/INSTALL
  
  Index: INSTALL
  ===================================================================
  Installation of AxKit has been designed to be as simple as
  possible (but we might not always get it right!).
  
  If you do not have Apache and mod_perl installed, please scroll
  down to "INSTALLATION OF MOD_PERL/APACHE".
  
  CPAN SHELL WAY
  
  The easiest way to install it is from the CPAN shell:
  
   $ su
   $ perl -MCPAN -e shell
   cpan> install AxKit
  
  This will download AxKit, and install any pre-requisite
  modules before installing it. You will also be asked questions
  regarding optional modules that you may wish to install.
  
  WITHOUT THE CPAN SHELL
  
  If you do not have network access (due to firewalls), installation
  is also fairly simple, it follows the common perl installation
  idiom:
  
   $ perl Makefile.PL
   $ make
   $ make test
   $ su
   $ make install
  
  The installer will check pre-requisite modules and automatically
  install anything that is an absolute requirement before running.
  
  INSTALLATION OF MOD_PERL/APACHE
  
  Note that AxKit WILL NOT install Apache and mod_perl for you. That
  is simply because these are extremely large and complex packages,
  and you should install them yourself. However some people have
  experienced problems with their installation of mod_perl (for
  example segmentation faults). So here is my recipe for a stable
  installation:
  
   Download mod_perl from http://perl.apache.org/
   Download Apache from http://www.apache.org/httpd.html
   Extract both archives in a suitable location (I use /tmp)
   Do not extract one archive in the other's directory - this
     will cause you problems.
   
   Now cd to the mod_perl directory, and enter the following:
   
   $ perl Makefile.PL \
   > EVERYTHING=1 \
   > USE_APACI=1 \
   > DYNAMIC=1 \
   > APACHE_PREFIX=/opt/apache \
   > APACHE_SRC=../apache_1.3.12/src \
   > DO_HTTPD=1 \
   > APACI_ARGS="--enable-module=so --enable-shared=info 
   > --enable-shared=proxy --enable-shared=rewrite 
   > --enable-shared=log_agent"
   $ make
   $ su
   $ make install
  
  Note that in the above, the $ and > are part of your shell prompt,
  not part of the commands to enter!
  
  Also note that the paths for the APACHE_SRC and APACHE_PREFIX should
  be fixed to the version of apache you downloaded, and the location
  you wish to install Apache into, respectively.
  
  ADVANCED INSTALLATION TIPS
  
  The following options can be passed on the command line to AxKit's
  Makefile.PL:
  
    DEBUG=1
    
      This option turns on lots of output from the running of Makefile.PL,
      use it in case you have problems installing AxKit and wish to
      send email to axkit-users@axkit.org with your problem.
  
    EXPAT_OPTS="..."
    
      Setting this option is only relevant if you do not already have
      libexpat installed. It lists options to be passed to libexpat's
      ./configure command. Such options might be "--prefix=/usr" which
      will install libexpat in /usr/lib, rather than the default
      location (which will likely be /usr/local/lib).
  
    LIBS="-L/path/to/expat/lib -lexpat"
    
      Allows you to set your library search path.
  
    INC="-I/path/to/expat/include"
    
      Allows you to set the include search path.
  
    NO_DIRECTIVES=1
    
      This option turns off AxKit's apache configuration directives,
      which means you will have to set these via PerlSetVar instead.
      
      This is useful because sometimes AxKit and mod_php can cause
      conflicts when using configuration directives (and results
      in segfaults). This is hopefully fixed in mod_perl 1.25.
  
      Note that you *must* manually remove all traces of AxKit from
      your /usr/lib/perl5/site_perl directory before re-running
      Makefile.PL with NO_DIRECTIVES=1 because unfortunately this
      causes AxKit to be installed in a completely different location,
      yet mod_perl will pick up the original copy compiled with
      directives, which is probably not what you want at all.
  
  CONFIGURATION
  
  In general you should read the AxKit Quick Start guide on
  http://axkit.org/, but for the impatient, read on.
  
  Once you have AxKit installed, you need to configure Apache to
  associate XML files with AxKit.  In doing so, there are a few
  considerations to make.  If your installation is only going to be
  handling XML files (ex: no PHP or cgi requests) then it's safe to
  include the following directives in your httpd.conf:
  
      # note the PerlModule line has to be *outside* any
      # <Files>, <Location>, <Directory> or <VirtualHost>
      # sections.
      PerlModule AxKit
      
      SetHandler axkit
      
      AxCacheDir /tmp/axkit_cache/
      AxAddStyleMap text/xsl Apache::AxKit::Language::LibXSLT
  
  If, however, you are using other content handler modules, such
  as PHP or mod_cgi to send content to the browser, you will more
  than likely want to use AddHandler:
  
      PerlModule AxKit
  
      AddHandler axkit .xml
      AddHandler axkit .xsp
      
  Another symptom that you want to wrap your configuration in a conditional
  block is if you turn on debugging (see perldoc AxKit for AxDebugLevel),
  request a test cgi program, receive the plain text version of the test 
  cgi and see something similar to the following in your error logs:
  
        [warn] [AxKit] : handler called for /cgi-bin/test-cgi
        [warn] [AxKit] : checking if we process this resource
        [warn] [AxKit] [DECLINED] '/usr/local/www/cgi-bin/test-cgi' not 
        recognized as XML
  
  GETTING HELP
  
  If you are still stuck getting this to work, first of all, see the
  AxKit FAQ at http://axkit.org/faq.xml - especially if your Apache
  is segfaulting when using AxKit.
  
  Then please send problem reports to axkit-users@axkit.org along with
  information about what version of the following items you are running:
  
      AxKit
      mod_perl
      Apache
      Perl
      Operating System
  
  However please note that installation problems regarding mod_perl,
  Sablotron, or Apache should be directed to the appropriate places
  for those modules, not to the axkit-users mailing list. Thank you.
  
  Please do not send email direct to matt@sergeant.org for problems
  with AxKit. Thank you.
  
  IF EVERYTHING WORKS
  
  Congratulations! You have a running AxKit. Now please visit
  http://axkit.org/docs/quick_start.dkb for a guide to your first pages
  with AxKit.
  
  
  
  
  1.1                  xml-axkit/MANIFEST
  
  Index: MANIFEST
  ===================================================================
  MANIFEST
  README
  Makefile.PL
  AxKit.pm
  AxKit.xs
  AxKit.spec
  axconfig.h
  axconfig.c
  getstyles.h
  getstyles.c
  CONTRIB
  INSTALL
  SUPPORT
  Changes
  typemap
  t/00basic.t
  t/01provider.t
  t/02language.t
  t/03stylechooser.t
  t/04plugins.t
  t/05mediachooser.t
  t/test_module.pl
  lib/Apache/AxKit/StyleChooser/QueryString.pm
  lib/Apache/AxKit/StyleChooser/PathInfo.pm
  lib/Apache/AxKit/StyleChooser/FileSuffix.pm
  lib/Apache/AxKit/StyleChooser/Cookie.pm
  lib/Apache/AxKit/StyleChooser/UserAgent.pm
  lib/Apache/AxKit/MediaChooser/WAPCheck.pm
  lib/Apache/AxKit/Plugin/Passthru.pm
  lib/Apache/AxKit/Plugin/Fragment.pm
  lib/Apache/AxKit/Plugin/QueryStringCache.pm
  lib/Apache/AxKit/Provider.pm
  lib/Apache/AxKit/Provider/File.pm
  lib/Apache/AxKit/Provider/Scalar.pm
  lib/Apache/AxKit/Provider/Filter.pm
  lib/Apache/AxKit/Language.pm
  lib/Apache/AxKit/Language/XPathScript.pm
  lib/Apache/AxKit/Language/AxPoint.pm
  lib/Apache/AxKit/Language/Sablot.pm
  lib/Apache/AxKit/Language/LibXSLT.pm
  lib/Apache/AxKit/Language/PassiveTeX.pm
  lib/Apache/AxKit/Language/XMLNewsRDF.pm
  lib/Apache/AxKit/Language/XMLNewsNITF.pm
  lib/Apache/AxKit/Language/XSP.pm
  lib/Apache/AxKit/Language/XSP/TaglibHelper.pm
  lib/Apache/AxKit/ConfigReader.pm
  lib/Apache/AxKit/Cache.pm
  lib/Apache/AxKit/Exception.pm
  lib/Apache/AxKit/CharsetConv.pm
  lib/Apache/AxKit/CharsetConv.xs
  lib/Apache/AxKit/Makefile.PL
  lib/Apache/AxKit/typemap
  examples/.htaccess
  examples/index.xml
  examples/default.xps
  examples/default.notxslt
  examples/docbook_tags.xps
  examples/docbook_screen.xps
  examples/docbook_print.xps
  examples/docbook.dkb
  axkit.org/index.xml
  axkit.org/faq.xml
  axkit.org/features.xml
  axkit.org/install.xml
  axkit.org/license.xml
  axkit.org/news.xml
  axkit.org/mailinglist.xml
  axkit.org/support.xml
  axkit.org/sidebar_new.xml
  axkit.org/sidebar.xml
  axkit.org/img/arrow.gif
  axkit.org/img/arrow_sel.gif
  axkit.org/img/banner2.gif
  axkit.org/img/banner3.gif
  axkit.org/img/banner_axkit.gif
  axkit.org/img/banner_bg.gif
  axkit.org/img/banner_end.gif
  axkit.org/img/bg.gif
  axkit.org/img/bullet.gif
  axkit.org/img/corner_logo.jpg
  axkit.org/img/corner_logo.png
  axkit.org/img/nav_bot.gif
  axkit.org/img/nav_left.gif
  axkit.org/img/nav_right.gif
  axkit.org/img/nav_top.gif
  axkit.org/img/pix.gif
  axkit.org/img/rcorner_br.gif
  axkit.org/img/rcorner_wh.gif
  axkit.org/img/vsep.gif
  axkit.org/docs/associating_stylesheets.xml
  axkit.org/docs/index.xml
  axkit.org/docs/introduction.dkb
  axkit.org/docs/provider-howto.dkb
  axkit.org/docs/quick_start.dkb
  axkit.org/docs/xpathscript/guide.dkb
  axkit.org/docs/xsp/guide.dkb
  axkit.org/docs/xsp/index.xml
  axkit.org/docs/xsp/sqltaglib.dkb
  axkit.org/docs/presentations/tpc2001/tpc-axkit.axp
  axkit.org/docs/presentations/tpc2001/redbg.png
  axkit.org/docs/presentations/tpc2001/ax_logo.png
  axkit.org/examples/cv.xml
  axkit.org/examples/cv_example.xml
  axkit.org/examples/cv.css
  axkit.org/examples/cv.xps
  axkit.org/examples/docbook.dkb
  axkit.org/examples/resume.dtd
  axkit.org/stylesheets/axkit.css
  axkit.org/stylesheets/axkit_html.css
  axkit.org/stylesheets/body_print_html.xps
  axkit.org/stylesheets/body_tag_html.xps
  axkit.org/stylesheets/docbook_example.xml
  axkit.org/stylesheets/docbook_print.xps
  axkit.org/stylesheets/docbook_screen.xps
  axkit.org/stylesheets/docbook_tags.xps
  axkit.org/stylesheets/error.xps
  axkit.org/stylesheets/error.xsl
  axkit.org/stylesheets/irc.xps
  axkit.org/stylesheets/navbar_html.xps
  axkit.org/stylesheets/new/rdf_html.xps
  axkit.org/stylesheets/new/sidebar_html.xps
  axkit.org/stylesheets/new/webpage_html.xps
  axkit.org/stylesheets/rdf_html.xps
  axkit.org/stylesheets/sidebar_html.xps
  axkit.org/stylesheets/webpage_html.xps
  axkit.org/stylesheets/spacer.xps
  expat-1.95.1/Changes
  expat-1.95.1/COPYING
  expat-1.95.1/MANIFEST
  expat-1.95.1/Makefile.in
  expat-1.95.1/README
  expat-1.95.1/aclocal.m4
  expat-1.95.1/config.hin
  expat-1.95.1/configure
  expat-1.95.1/configure.in
  expat-1.95.1/conftools/config.guess
  expat-1.95.1/conftools/config.sub
  expat-1.95.1/conftools/install-sh
  expat-1.95.1/conftools/ltconfig
  expat-1.95.1/conftools/ltmain.sh
  expat-1.95.1/conftools/missing
  expat-1.95.1/conftools/mkinstalldirs
  expat-1.95.1/doc/reference.html
  expat-1.95.1/doc/style.css
  expat-1.95.1/examples/Makefile.in
  expat-1.95.1/examples/outline.c
  expat-1.95.1/lib/Makefile.in
  expat-1.95.1/lib/ascii.h
  expat-1.95.1/lib/asciitab.h
  expat-1.95.1/lib/expat.dsp
  expat-1.95.1/lib/expat.h
  expat-1.95.1/lib/iasciitab.h
  expat-1.95.1/lib/latin1tab.h
  expat-1.95.1/lib/nametab.h
  expat-1.95.1/lib/utf8tab.h
  expat-1.95.1/lib/xmlparse.c
  expat-1.95.1/lib/xmlrole.c
  expat-1.95.1/lib/xmlrole.h
  expat-1.95.1/lib/xmltok.c
  expat-1.95.1/lib/xmltok.h
  expat-1.95.1/lib/xmltok_impl.c
  expat-1.95.1/lib/xmltok_impl.h
  expat-1.95.1/lib/xmltok_ns.c
  expat-1.95.1/lib/winconfig.h
  expat-1.95.1/xmlwf/Makefile.in
  expat-1.95.1/xmlwf/codepage.c
  expat-1.95.1/xmlwf/codepage.h
  expat-1.95.1/xmlwf/ct.c
  expat-1.95.1/xmlwf/filemap.h
  expat-1.95.1/xmlwf/readfilemap.c
  expat-1.95.1/xmlwf/unixfilemap.c
  expat-1.95.1/xmlwf/wfcheck.c
  expat-1.95.1/xmlwf/wfcheck.h
  expat-1.95.1/xmlwf/wfcheckmessage.c
  expat-1.95.1/xmlwf/win32filemap.c
  expat-1.95.1/xmlwf/xmlfile.c
  expat-1.95.1/xmlwf/xmlfile.h
  expat-1.95.1/xmlwf/xmlmime.c
  expat-1.95.1/xmlwf/xmlmime.h
  expat-1.95.1/xmlwf/xmltchar.h
  expat-1.95.1/xmlwf/xmlurl.h
  expat-1.95.1/xmlwf/xmlwf.c
  expat-1.95.1/xmlwf/xmlwf.dsp
  expat-1.95.1/xmlwf/xmlwin32url.cxx
  XML-Parser-2.30/samples/xmlstats
  XML-Parser-2.30/samples/REC-xml-19980210.xml
  XML-Parser-2.30/samples/canontst.xml
  XML-Parser-2.30/samples/xmlfilter
  XML-Parser-2.30/samples/canonical
  XML-Parser-2.30/samples/ctest.dtd
  XML-Parser-2.30/samples/xmlcomments
  XML-Parser-2.30/t/ext.ent
  XML-Parser-2.30/t/encoding.t
  XML-Parser-2.30/t/stream.t
  XML-Parser-2.30/t/foo.dtd
  XML-Parser-2.30/t/partial.t
  XML-Parser-2.30/t/cdata.t
  XML-Parser-2.30/t/namespaces.t
  XML-Parser-2.30/t/file.t
  XML-Parser-2.30/t/finish.t
  XML-Parser-2.30/t/astress.t
  XML-Parser-2.30/t/external_ent.t
  XML-Parser-2.30/t/defaulted.t
  XML-Parser-2.30/t/ext2.ent
  XML-Parser-2.30/t/skip.t
  XML-Parser-2.30/t/parament.t
  XML-Parser-2.30/t/decl.t
  XML-Parser-2.30/Parser/Encodings/windows-1250.enc
  XML-Parser-2.30/Parser/Encodings/euc-kr.enc
  XML-Parser-2.30/Parser/Encodings/Japanese_Encodings.msg
  XML-Parser-2.30/Parser/Encodings/x-sjis-unicode.enc
  XML-Parser-2.30/Parser/Encodings/iso-8859-2.enc
  XML-Parser-2.30/Parser/Encodings/iso-8859-3.enc
  XML-Parser-2.30/Parser/Encodings/iso-8859-4.enc
  XML-Parser-2.30/Parser/Encodings/iso-8859-5.enc
  XML-Parser-2.30/Parser/Encodings/x-euc-jp-jisx0221.enc
  XML-Parser-2.30/Parser/Encodings/iso-8859-7.enc
  XML-Parser-2.30/Parser/Encodings/iso-8859-8.enc
  XML-Parser-2.30/Parser/Encodings/iso-8859-9.enc
  XML-Parser-2.30/Parser/Encodings/x-sjis-cp932.enc
  XML-Parser-2.30/Parser/Encodings/x-euc-jp-unicode.enc
  XML-Parser-2.30/Parser/Encodings/x-sjis-jdk117.enc
  XML-Parser-2.30/Parser/Encodings/big5.enc
  XML-Parser-2.30/Parser/Encodings/README
  XML-Parser-2.30/Parser/Encodings/x-sjis-jisx0221.enc
  XML-Parser-2.30/Parser/LWPExternEnt.pl
  XML-Parser-2.30/Expat/Expat.xs
  XML-Parser-2.30/Expat/typemap
  XML-Parser-2.30/Expat/Makefile.PL
  XML-Parser-2.30/Expat/encoding.h
  XML-Parser-2.30/Expat/Expat.pm
  XML-Parser-2.30/MANIFEST
  XML-Parser-2.30/Changes
  XML-Parser-2.30/Makefile.PL
  XML-Parser-2.30/README
  XML-Parser-2.30/Parser.pm
  Digest-MD5-2.12/MD2/MD2.pm
  Digest-MD5-2.12/MD2/MD2.xs
  Digest-MD5-2.12/MD2/t/badfile.t
  Digest-MD5-2.12/MD2/t/md2.t
  Digest-MD5-2.12/MD2/rfc1319.txt
  Digest-MD5-2.12/MD2/typemap
  Digest-MD5-2.12/MD2/Makefile.PL
  Digest-MD5-2.12/SHA1/Makefile.PL
  Digest-MD5-2.12/SHA1/t/sha1.t
  Digest-MD5-2.12/SHA1/t/badfile.t
  Digest-MD5-2.12/SHA1/t/sha.t
  Digest-MD5-2.12/SHA1/SHA1.pm
  Digest-MD5-2.12/SHA1/SHA1.xs
  Digest-MD5-2.12/SHA1/lib/SHA.pm
  Digest-MD5-2.12/SHA1/fip180-1.html
  Digest-MD5-2.12/SHA1/fip180-1.gif
  Digest-MD5-2.12/SHA1/typemap
  Digest-MD5-2.12/t/digest.t
  Digest-MD5-2.12/t/badfile.t
  Digest-MD5-2.12/t/rfc2202.t
  Digest-MD5-2.12/t/md5.t
  Digest-MD5-2.12/t/files.t
  Digest-MD5-2.12/t/md5-aaa.t
  Digest-MD5-2.12/rfc2104.txt
  Digest-MD5-2.12/MD5.pm
  Digest-MD5-2.12/lib/Digest.pm
  Digest-MD5-2.12/lib/Digest/HMAC_MD5.pm
  Digest-MD5-2.12/lib/Digest/HMAC_SHA1.pm
  Digest-MD5-2.12/lib/Digest/HMAC.pm
  Digest-MD5-2.12/lib/MD5.pm
  Digest-MD5-2.12/hints/dec_osf.pl
  Digest-MD5-2.12/hints/irix_6.pl
  Digest-MD5-2.12/MANIFEST
  Digest-MD5-2.12/Changes
  Digest-MD5-2.12/MD5.xs
  Digest-MD5-2.12/typemap
  Digest-MD5-2.12/examples/twdigest.pl
  Digest-MD5-2.12/examples/mddriver.pl
  Digest-MD5-2.12/Makefile.PL
  Digest-MD5-2.12/README
  Digest-MD5-2.12/rfc1321.txt
  Compress-Zlib-1.09/t/examples.t
  Compress-Zlib-1.09/t/zlib.t
  Compress-Zlib-1.09/README
  Compress-Zlib-1.09/examples/gzgrep
  Compress-Zlib-1.09/examples/gzcat
  Compress-Zlib-1.09/examples/filtdef
  Compress-Zlib-1.09/examples/filtinf
  Compress-Zlib-1.09/examples/gzstream
  Compress-Zlib-1.09/MANIFEST
  Compress-Zlib-1.09/ANNOUNCE
  Compress-Zlib-1.09/Zlib.xs
  Compress-Zlib-1.09/typemap
  Compress-Zlib-1.09/config.in
  Compress-Zlib-1.09/Makefile.PL
  Compress-Zlib-1.09/Makefile.NT
  Compress-Zlib-1.09/Zlib.pm
  Error-0.13/t/02order.t
  Error-0.13/t/01throw.t
  Error-0.13/Error.pm
  Error-0.13/MANIFEST
  Error-0.13/ChangeLog
  Error-0.13/Makefile.PL
  Error-0.13/Error.ppd
  Error-0.13/example
  Error-0.13/README
  libapreq-0.31_03/CREDITS
  libapreq-0.31_03/Changes
  libapreq-0.31_03/INSTALL
  libapreq-0.31_03/LICENSE
  libapreq-0.31_03/MANIFEST
  libapreq-0.31_03/Makefile.PL
  libapreq-0.31_03/Makefile.am
  libapreq-0.31_03/README
  libapreq-0.31_03/ToDo
  libapreq-0.31_03/acconfig.h
  libapreq-0.31_03/acinclude.m4
  libapreq-0.31_03/aclocal.m4
  libapreq-0.31_03/config.guess
  libapreq-0.31_03/config.sub
  libapreq-0.31_03/configure.in
  libapreq-0.31_03/install-sh
  libapreq-0.31_03/libapreq.pod
  libapreq-0.31_03/libtool
  libapreq-0.31_03/ltconfig
  libapreq-0.31_03/ltmain.sh
  libapreq-0.31_03/mkinstalldirs
  libapreq-0.31_03/typemap
  libapreq-0.31_03/Cookie/Cookie.pm
  libapreq-0.31_03/Cookie/Cookie.xs
  libapreq-0.31_03/Cookie/Makefile.PL
  libapreq-0.31_03/Request/Makefile.PL
  libapreq-0.31_03/Request/Request.pm
  libapreq-0.31_03/Request/Request.xs
  libapreq-0.31_03/c/Makefile.PL
  libapreq-0.31_03/c/Makefile.am
  libapreq-0.31_03/c/Makefile.noperl
  libapreq-0.31_03/c/apache_cookie.c
  libapreq-0.31_03/c/apache_cookie.h
  libapreq-0.31_03/c/apache_multipart_buffer.c
  libapreq-0.31_03/c/apache_multipart_buffer.h
  libapreq-0.31_03/c/apache_request.c
  libapreq-0.31_03/c/apache_request.h
  libapreq-0.31_03/c/libapreq_config.h.in
  libapreq-0.31_03/c/stamp-h.in
  libapreq-0.31_03/c/Makefile.in
  libapreq-0.31_03/eg/c/testapreq/Makefile.apxs
  libapreq-0.31_03/eg/c/testapreq/Makefile.tmpl
  libapreq-0.31_03/eg/c/testapreq/mod_testapreq.c
  libapreq-0.31_03/eg/c/testapreq/mod_testapreq.module
  libapreq-0.31_03/eg/perl/cookie.pl
  libapreq-0.31_03/eg/perl/file_upload.pl
  libapreq-0.31_03/lib/Apache/libapreq.pm
  libapreq-0.31_03/Makefile.in
  libapreq-0.31_03/configure
  
  
  
  1.1                  xml-axkit/Makefile.PL
  
  Index: Makefile.PL
  ===================================================================
  # $Id: Makefile.PL,v 1.1 2002/01/13 20:45:08 matts Exp $
  
  package AxKit;
  # file Makefile.PL
  
  use ExtUtils::MakeMaker;
  
  use Text::Wrap ();
  use Cwd;
  
  require 5.005;
  
  sub wrap ($)
  {
      my $text = shift;
      $text =~ s/^\s+//;
      $text =~ s/\s+$//;
      Text::Wrap::wrap('', '', $text) . "\n\n";
  }
  
  my @DIRS = ('./lib/Apache/AxKit', './Apache-MimeXML');
  my $from_dir = cwd;
  my $expat_dir = "expat-1.95.1";
  
  ######################################################
  # Standard bits required for have_library and friends
  my %config;
  
  $|=1; # flush output
  
  while($_ = shift @ARGV) {
      my ($k, $v) = split /=/, $_, 2;
      $config{$k} = $v;
  }
  
  $DEBUG = delete $config{DEBUG};
  ######################################################
  
  if (!have_module('mod_perl', '1.17')) {
      die wrap(<<DEATH);
  AxKit requires mod_perl to be able to even run. You do not appear to
  have mod_perl installed (or your mod_perl is earlier than version 1.17).
  
  You can download mod_perl from http://perl.apache.org/
  
  DEATH
  }
  
  require Apache::src;
  
  my $install_expat = 0;
  
  unless (have_module('XML::Parser', '2.27')) {
      # XML::Parser not installed
      if (!have_library("expat")) {
          print "compiling expat... ";
          eval {
              my $expat_opts = delete $config{EXPAT_OPTS};
              xsystem("cd $expat_dir && ./configure $expat_opts && make");
              $install_expat = 1;
              $config{"EXPATINCPATH"} = MM->catdir($from_dir, $expat_dir, 'lib');
              $config{"EXPATLIBPATH"} = MM->catdir($from_dir, $expat_dir, 'lib', '.libs');
              print "ok\n";
          };
          if ($@) {
              print "failed\n";
              die "Compilation of expat failed. Re-run with DEBUG=1 to see why.\n";
          }
      }
      push @DIRS, 'XML-Parser-2.30';
  }
  
  unless (have_module('Digest::MD5', '2.09')) {
      push @DIRS, 'Digest-MD5-2.12';
  }
  
  unless (have_module('Compress::Zlib')) {
      push @DIRS, 'Compress-Zlib-1.09';
  }
  
  unless (have_module('Error', '0.13')) {
      push @DIRS, 'Error-0.13';
  }
  
  unless (have_module('Apache::Request', '0.31_03')) {
      push @DIRS, 'libapreq-0.31_03';
  }
  
  test_module('XML::XPath', '1.00', <<"REASON");
  XML::XPath 1.00 or higher is required for XPathScript
  to work. You don't have to install it, but you won't be able
  to use XPathScript stylesheets without it.
  REASON
  
  test_module('HTTP::GHTTP', '1.00', <<"REASON");
  HTTP::GHTTP is used for retrieving remote URIs within AxKit.
  We use HTTP::GHTTP because it is significantly faster and lighter
  than LWP, however AxKit will use LWP if it cannot locate
  HTTP::GHTTP.
  REASON
  
  
  test_module('XML::Sablotron', '0.40', <<"REASON");
  XML::Sablotron 0.40 or higher is required for our Sablotron 
  XSLT processor to work. You don't have to install it, 
  but you won't be able to use Apache::AxKit::Language::Sablot without it.
  REASON
  
  test_module('XML::LibXML', '1.31', <<"REASON");
  XML::LibXML 1.31 or higher is required for the XSP engine
  to work. You don't have to install it, but you won't
  be able to use XSP pages without it.
  REASON
          
  test_module('XML::LibXSLT', '1.31', <<"REASON");
  XML::LibXSLT 1.31 or higher is required for our LibXSLT 
  XSLT processor to work. You don't have to install it, 
  but you won't be able to use Apache::AxKit::Language::LibXSLT without it.
  REASON
  
  
  eval {
      print "running xml2-config... ";
      my $libs = backtick('xml2-config --libs');
      $config{LIBS} .= " $libs" if $libs;
      my $inc = backtick('xml2-config --cflags');
      $config{INC} .= " $inc" if $inc;
      print "ok\n";
  };
  if ($@) {
      print "failed (will try to find libxml2 anyway)\n";
  }
  
  if (have_library('xml2')) {
      $config{DEFINE} .= ' -DHAVE_LIBXML2';
      $config{LIBS} .= ' -lxml2 -lz';
  }
  elsif (!$is_Win32) {
      my $apache;
      
      print <<EOT;
  
  We will now check if your apache has XML symbols in it, to be sure that
  AxKit will work under your version of Apache. If you do not wish to 
  perform this check, please just enter 0 (zero) below.
  
  EOT
  
      while (!is_apache($apache)) {
          $apache = prompt("Path to apache httpd?", "/usr/local/apache/bin/httpd");
          last if $apache eq "0";
      }
      
      if ($apache ne "0") {
          print "checking for expat symbols in apache... ";
          eval {
              my $symbols = backtick("grep -i xml $apache");
              if ($symbols) {
                  die wrap(<<DEATH);
  Your Apache has XML symbols in it. This means it will segfault when you
  try and use it with XML::Parser. Your options are to either recompile
  Apache, or to install libxml2 from http://www.xmlsoft.org and re-install
  AxKit. However if you do not recompile Apache without expat/xml support,
  you will not be able to use XPathScript, XSP, or any other modules that
  make use of XML::Parser. Please see the AxKit FAQ on http://axkit.org
  for more details.
  DEATH
              }
          };
          print "ok\n";
      }
  }
          
  if ($is_Win32) {
      if (!have_module('mod_perl', '1.24_01')) {
          die wrap(<<DEATH);
  AxKit on Win32 requires mod_perl greater than 1.24_01 in order to 
  get the paths to the Apache and mod_perl build directories.
  
  DEATH
      }
      
      require Apache::MyConfig;
      $config{INC} .= qq{ -I"$Apache::MyConfig::Setup{MODPERL_INC}/../.." }
          . qq{ -I"$Apache::MyConfig::Setup{APACHE_INC}" }
          . qq{ -I"$Apache::MyConfig::Setup{APACHE_INC}/../os/win32" };
      $config{CCFLAGS} = $Config{ccflags} . 
          ' -D_WINSOCK2API_ -D_MSWSOCK_ -D_INC_SIGNAL -D_INC_MALLOC ';
      $config{LIBS} = 
          qq{ -L"$Apache::MyConfig::Setup{APACHE_LIB}" -lApacheCore } .
          qq{ -L"$Apache::MyConfig::Setup{MODPERL_LIB}" -lmod_perl};
  }
  
  $config{INC} .= ' ' . Apache::src->new->inc;
  
  if ($DEBUG) {
      print "calling WriteMakefile with config:\n";
      foreach my $k (keys %config) {
          print "$k = $config{$k}\n";
      }
  }
  
  foreach my $k (keys %config) {
      push @ARGV, "$k=$config{$k}";
  }
  
  %config = () if $] > 5.00560; 
  rm_f($DEVNULL) if $is_Win32;
  
  WriteMakefile(
          'NAME' => __PACKAGE__,
          'VERSION_FROM' => 'AxKit.pm',
          'AUTHOR' => 'AxKit.com Limited - http://axkit.com/',
          'ABSTRACT' => 'AxKit is an XML Application Server for mod_perl',
          'DIR' => [ @DIRS ],
          'OBJECT' => '$(O_FILES)',
          %config,
  );
  
  #################################################################
  # Functions
  #################################################################
  
  use Config;
  use Cwd;
  use Symbol;
  use File::Spec;
  
  use vars qw/$DEVNULL $is_Win32/;
  
  BEGIN {
      $is_Win32 = ($^O =~ /Win32/);
      if ($is_Win32) {
          $DEVNULL = 'DEVNULL';
      }
      else {
          $DEVNULL = eval { File::Spec->devnull };
          if ($@) { $DEVNULL = '/dev/null' }
      }
  }
  
  sub rm_f {
      my @files = @_;
      my @realfiles;
      foreach (@files) {
          push @realfiles, glob($_);
      }
      if (@realfiles) {
          chmod(0777, @realfiles);
          unlink(@realfiles);
      }
  }
  
  sub rm_fr {
      my @files = @_;
      my @realfiles;
      foreach (@files) {
          push @realfiles, glob($_);
      }
      foreach my $file (@realfiles) {
          if (-d $file) {
              # warn("$file is a directory\n");
              rm_fr("$file/*");
              rm_fr("$file/.exists");
              rmdir($file) || die "Couldn't remove $file: $!";
          }
          else {
              # warn("removing $file\n");
              chmod(0777, $file);
              unlink($file);
          }
      }
  }
  
  sub xsystem {
      my $command = shift;
      if ($DEBUG) {
          print $command, "\n";
          if (system($command) != 0) {
              die "system call to '$command' failed";
          }
          return 1;
      }
      open(OLDOUT, ">&STDOUT");
      open(OLDERR, ">&STDERR");
      open(STDOUT, ">$DEVNULL");
      open(STDERR, ">$DEVNULL");
      my $retval = system($command);
      open(STDOUT, ">&OLDOUT");
      open(STDERR, ">&OLDERR");
      if ($retval != 0) {
          die "system call to '$command' failed";
      }
      return 1;
  }
  
  sub backtick {
      my $command = shift;
      if ($DEBUG) {
          print $command, "\n";
          my $results = `$command`;
          chomp $results;
          if ($? != 0) {
              die "backticks call to '$command' failed";
          }
          return $results;
      }
      open(OLDOUT, ">&STDOUT");
      open(OLDERR, ">&STDERR");
      open(STDOUT, ">$DEVNULL");
      open(STDERR, ">$DEVNULL");
      my $results = `$command`;
      my $retval = $?;
      open(STDOUT, ">&OLDOUT");
      open(STDERR, ">&OLDERR");
      if ($retval != 0) {
          die "backticks call to '$command' failed";
      }
      chomp $results;
      return $results;
  }
  
  sub try_link0 {
      my ($src, $opt) = @_;
      my $cfile = gensym();
      # local $config{LIBS};
      # $config{LIBS} .= $opt;
      unless (mkdir(".testlink", 0777)) {
          rm_fr(".testlink");
          mkdir(".testlink", 0777) || die "Cannot create .testlink dir: $!";
      }
      chdir(".testlink");
      open($cfile, ">Conftest.xs") || die "Cannot write to file Conftest.xs: $!";
  print $cfile <<EOT;
  #ifdef __cplusplus
  extern "C" {
  #endif
  #include <EXTERN.h>
  #include <perl.h>
  #include <XSUB.h>
  #ifdef __cplusplus
  }
  #endif
  
  EOT
      print $cfile $src;
      print $cfile <<EOT;
  
  MODULE = Conftest          PACKAGE = Conftest
  
  PROTOTYPES: DISABLE
  
  EOT
      close($cfile);
      open($cfile, ">Conftest.pm") || die "Cannot write to file Conftest.pm: $!";
      print $cfile <<'EOT';
  package Conftest;
  $VERSION = 1.0;
  require DynaLoader;
  @ISA = ('DynaLoader');
  bootstrap Conftest $VERSION;
  1;
  EOT
      close($cfile);
      open($cfile, ">Makefile.PL") || die "Cannot write to file Makefile.PL: $!";
      print $cfile <<'EOT';
  use ExtUtils::MakeMaker;
  my %config;
  while($_ = shift @ARGV) {
      my ($k, $v) = split /=/, $_, 2;
      $config{$k} = $v;
  }
  WriteMakefile(NAME => "Conftest", VERSION_FROM => "Conftest.pm", %config);
  EOT
      close($cfile);
      open($cfile, ">test.pl") || die "Cannot write to file test.pl: $!";
      print $cfile <<EOT;
  use Test; BEGIN { plan tests => 1; } END { ok(\$loaded) }
  use Conftest; \$loaded++;
  EOT
      close($cfile);
      xsystem("$^X Makefile.PL " . join(' ', map { "'$_=$config{$_}'" } keys %config));
      xsystem("$Config{make} test 'OTHERLDFLAGS=$opt'");
  }
  
  sub try_link {
      my $start_dir = cwd();
      my $result = eval {
          try_link0(@_);
      };
      warn $@ if $DEBUG && $@;
      chdir($start_dir);
      rm_fr(".testlink");
      return $result;
  }
  
  sub have_library {
      my ($lib, $func) = (@_, "blank");
      printf("checking for %s() in -l%s... ", $func, $lib) if $func ne "blank";
      printf("looking for -l%s... ", $lib) if $func eq "blank";
  
      my $result;
      if ($func) {
          my $libs = $is_Win32 ? " $lib.lib  " : "-l$lib";
          if ($is_Win32) {
              $result = try_link(<<"SRC", $libs);
  #include <windows.h>
  #include <winsock.h>
  blank() { return 0; }
  int t() { ${func}(); return 0; }
  SRC
              unless ($result) {
                  $result = try_link(<<"SRC", $libs);
  #include <windows.h>
  #include <winsock.h>
  blank() { return 0; }
  int t() { void ((*p)()); p = (void ((*)()))${func}; return 0; }
  SRC
              }
          }
          else {
  
              $result = try_link(<<"SRC", $libs);
  blank() { return 0; }
  int t() { ${func}(); return 0; }
  SRC
          }
      }
  
      unless ($result) {
          print "no\n";
          return 0;
      }
  
      if ($func ne "main") {
          $config{DEFINE} .= uc(" -Dhave_$func");
      }
  
      print "yes\n";
      return 1;
  }
  
  sub have_module {
      my ($module, $version) = (@_, 0);
      printf("checking for module %s >= version %s... ", $module, $version);
      
      print "eval(\"package Foo; use $module $version;\")\n" if $DEBUG;
      eval "package Foo; use $module $version;";
      if ($@) {
          print $@ if $DEBUG;
          print "no\n";
          return 0;
      }
      print "yes\n";
      return 1;
  }
  
  sub test_module {
      my ($module, $version, $reason) = @_;
      unless (have_module($module, $version)) {
          if ($INC{'CPAN.pm'}) {
              if (prompt("Add $module to list of modules to install?", 'Y') =~ /^y/i) {
                  $config{PREREQ_PM}{$module} = $version;
              }
          }
          else {
              print wrap($reason);
          }
          return 0;
      }
      return 1;
  }
  
  sub MY::install {
      package MY;
      my $self = shift;
  
      my $install = $self->SUPER::install(@_);
      
      if ($install_expat) {
          $install =~ s/(install :: .*)$/$1 expat_install/m;
          $install .= <<INSTALL;
  
  expat_install :
  \t\@cd $expat_dir && \$(SHELL) -c \$(MAKE) install
  
  INSTALL
      }
      
      return $install;
  }
  
  sub is_apache {
      my $path = shift;
      return unless $path;
      if (-e $path && -x _) {
          my $version = eval { backtick("$path -v") };
          if (!$@) {
              $version =~ /Server version: Apache/
                  && return 1;
          }
      }
      warn("No apache httpd found at $path\n");
      return;
  }
  
  
  
  1.1                  xml-axkit/README
  
  Index: README
  ===================================================================
  INTRODUCTION
  
  AxKit is an XML Application Server for Apache. It provides on-the-fly
  conversion from XML to any format, such as HTML, WAP or text using
  either W3C standard techniques, or flexible custom code. AxKit also
  uses a built-in Perl interpreter to provide some amazingly powerful
  techniques for XML transformation. 
  
  The emphasis with AxKit is on separation of content from presentation.
  The pipelining technique that AxKit uses allows content to be converted
  to a presentable format in stages, allowing certain platforms to see
  data differently to others. AxKit allows web designers to focus on web
  site design, content developers to work on a purely content basis, and
  webmasters to focus on their core competencies. 
  
  AxKit is based on plugin components. This allows the web site developer
  to create a completely custom XML Application Server based around the
  AxKit API. AxKit can either automatically provide caching facilities,
  or you can create your own cache handler, so that XML transformations
  (which can be time consuming) only happen when required. 
  
  The toolkit also provides ability to build component based web sites,
  and dynamic content. Database integration is fully supported, allowing
  either deliver of XML from a database, or XML generation from a
  database query. Dynamic web components can be built using the Perl
  language, making the possibilities as infinite as CGI scripts, without
  the potential mess that CGI programming can cause. 
  
  HOME PAGE
  
  The web site for AxKit is http://axkit.org/. There you will find lots
  more documentation and example code. The complete source for axkit.org
  is included in this distribution in the axkit.org directory.
  
  
  
  1.1                  xml-axkit/SUPPORT
  
  Index: SUPPORT
  ===================================================================
  What to do if it all goes pear shaped...
  ========================================
  
  If you have trouble getting AxKit working how you want it to work, then
  please subscribe to and use the axkit-users mailing list. Details of this
  are available at http://axkit.org/mailinglist.xml
  
  If your problem is more serious, for example if AxKit segfaults, then
  please follow these instructions:
  
  Edit your AxKit's Makefile, find the OPTIMIZE line, and add -g to it.
  
  Recompile AxKit (run "make test install")
  
  Make sure apache is stopped.
  
  run:
  
  $ gdb /path/to/apache/bin/httpd
  gdb version blah blah...
  (gdb) run -X
    
  Now make a request on your server. When it segfaults, gdb will stop and
  tell you where... but I need more info... so type:
      
  (gdb) bt
     
  That will give you a backtrace.
  
  Now email that to axkit-users@axkit.org, along with your Perl version,
  Apache version, and AxKit version. It may also be helpful to include your
  list of LoadModule lines from httpd.conf, and the section where you have
  all the Ax* directives.
  
  But please try the latest release of AxKit before sending it in - and if
  you're testing a beta, please try CVS first (see the CONTRIB file for
  details).
  
  
  
  
  1.1                  xml-axkit/TODO
  
  	<<Binary file>>
  
  
  1.1                  xml-axkit/axconfig.c
  
  Index: axconfig.c
  ===================================================================
  /* $Id: axconfig.c,v 1.1 2002/01/13 20:45:08 matts Exp $ */
  
  #ifndef WIN32
  #include <modules/perl/mod_perl.h>
  #include <httpd.h>
  #include <http_config.h>
  #endif
  #include "axconfig.h"
  
  #ifdef WIN32
  #define ax_preload_module(name)
  #else
  static void ax_preload_module(char **name)
  {
      if(ind(*name, ' ') >= 0) return;
      if(**name == '-' && ++*name) return;
      if(**name == '+') ++*name;
      else if(!PERL_AUTOPRELOAD) return;
      if(!PERL_RUNNING()) return;
  
      maybe_load_module(*name);
  }
  #endif
  
  extern SV *error_str;
  
  static SV *module2file(char *name)
  {
      SV *sv = newSVpv(name,0);
      char *s;
      for (s = SvPVX(sv); *s; s++) {
          if (*s == ':' && s[1] == ':') {
              *s = '/';
              Move(s+2, s+1, strlen(s+2)+1, char);
              --SvCUR(sv);
          }
      }
      sv_catpvn(sv, ".pm", 3);
      return sv;
  }
  
  static I32 module_is_loaded(SV *key)
  {
      I32 retval = FALSE;
      if((key && hv_exists_ent(GvHV(incgv), key, FALSE)))
          retval = TRUE;
      return retval;
  }
  
  void
  maybe_load_module(char * name)
  {
      STRLEN len;
      SV * sv_file = module2file(name);
      char * ch_file = SvPV(sv_file, len);
      
      if(!module_is_loaded(sv_file)) {
          perl_require_pv(ch_file);
          if (SvTRUE(ERRSV)) {
              SvREFCNT_dec(sv_file);
              croak("AxKit::load_module failed: %s", SvPV(ERRSV, len));
          }
      }
      SvREFCNT_dec(sv_file);
  }
  
  void
  ax_cleanup_av(void * av_v)
  {
      AV * my_av = (AV*)av_v;
      /* warn("cleanup_av : %d : %d\n", SvREFCNT((SV*)my_av), my_av); */
      SvREFCNT_dec((SV*)my_av);
  }
  
  void
  ax_cleanup_hv(void * hv_v)
  {
      HV * my_hv = (HV*)hv_v;
      /* warn("cleanup_hv : %d : %d\n", SvREFCNT((SV*)my_hv), my_hv); */
      SvREFCNT_dec((SV*)my_hv);
  }
  
  axkit_dir_config *
  new_axkit_dir_config (pool *p)
  {
      axkit_dir_config *new = 
          (axkit_dir_config *) ap_palloc(p, sizeof(axkit_dir_config));
      
      new->translate_output = -1;
      new->gzip_output = -1;
      new->log_declines = -1;
      new->stack_trace = -1;
      new->no_cache = -1;
      new->debug_level = -1;
      new->dependency_checks = -1;
      new->reset_processors = 0;
      new->reset_output_transformers = 0;
      new->reset_plugins = 0;
      
      new->cache_dir = 0;
      new->config_reader_module = 0;
      new->provider_module = 0;
      new->styleprovider_module = 0;
      new->default_style = 0;
      new->default_media = 0;
      new->cache_module = 0;
      new->output_charset = 0;
      
      /* complex types */
      new->type_map = NULL;
      new->processors = NULL;
      new->dynamic_processors = NULL;
      new->xsp_taglibs = NULL;
      new->current_styles = NULL;
      new->current_medias = NULL;
      new->error_stylesheet = NULL;
      new->output_transformers = NULL;
      new->current_plugins = NULL;
      
  /*
      warn("[AxKit] created new dir_config:\n"
          "location: %d\n"
          "new.translate_output: %d\n"
          "new.gzip_output: %d\n"
          "new.log_declines: %d\n"
          "new.stack_trace: %d\n"
          "new.reset_processors: %d\n"
          "new.cache_dir: %d\n"
          "new.config_reader_module: %d\n"
          "new.provider_module: %d\n"
          "new.default_style: %d\n"
          "new.default_media: %d\n"
          "new.cache_module: %d\n"
          "new.output_charset: %d\n"
          "new.debug_level: %d\n"
          "new.type_map: %d\n"
          "new.processors: %d\n"
          "new.dynamic_processors: %d\n"
          "new.xsp_taglibs: %d\n"
          "new.current_styles: %d\n"
          "new.current_medias: %d\n"
          "new.error_stylesheet: %d\n"
          "new.output_transformers: %d\n"
          ,
          new,
          new->translate_output,
          new->gzip_output,
          new->log_declines,
          new->stack_trace,
          new->reset_processors,
          new->cache_dir,
          new->config_reader_module,
          new->provider_module,
          new->default_style,
          new->default_media,
          new->cache_module,
          new->output_charset,
          new->debug_level,
          new->type_map,
          new->processors,
          new->dynamic_processors,
          new->xsp_taglibs,
          new->current_styles,
          new->current_medias,
          new->error_stylesheet,
          new->output_transformers
          );
  */
      
      return new;
  }
  
  void *
  create_axkit_dir_config (pool *p, char *dummy)
  {
      axkit_dir_config *new = new_axkit_dir_config(p);
      
      new->type_map = newHV();
      hv_store(new->type_map, "application/x-xpathscript", 25,
              newSVpv("Apache::AxKit::Language::XPathScript", 0), 0);
      hv_store(new->type_map, "application/x-xsp", 17,
              newSVpv("Apache::AxKit::Language::XSP", 0), 0);
      ap_register_cleanup(p, (void*)new->type_map, ax_cleanup_hv, ap_null_cleanup);
      
      new->processors = newHV();
      ap_register_cleanup(p, (void*)new->processors, ax_cleanup_hv, ap_null_cleanup);
      
      new->dynamic_processors = newAV();
      ap_register_cleanup(p, (void*)new->dynamic_processors, ax_cleanup_av, ap_null_cleanup);
      
      new->xsp_taglibs = newHV();
      ap_register_cleanup(p, (void*)new->xsp_taglibs, ax_cleanup_hv, ap_null_cleanup);
      
      new->output_transformers = newAV();
      ap_register_cleanup(p, (void*)new->output_transformers, ax_cleanup_av, ap_null_cleanup);
      
      new->current_styles = newAV();
      av_push(new->current_styles, newSVpv("#default", 0));
      ap_register_cleanup(p, (void*)new->current_styles, ax_cleanup_av, ap_null_cleanup);
      
      new->current_medias = newAV();
      av_push(new->current_medias, newSVpv("screen", 0));
      ap_register_cleanup(p, (void*)new->current_medias, ax_cleanup_av, ap_null_cleanup);
      
      new->error_stylesheet = newAV();
      ap_register_cleanup(p, (void*)new->error_stylesheet, ax_cleanup_av, ap_null_cleanup);
  
      new->current_plugins = newAV();
      ap_register_cleanup(p, (void*)new->current_plugins, ax_cleanup_av, ap_null_cleanup);
      
      /* warn("create dir config: %d\n", new); */
      
      return new;
  }
  
  void
  store_in_hv2 (HV * my_hv, SV * one, SV * two, SV * value)
  {
      char * key1;
      char * key2;
      STRLEN len;
      HV * sub1;
      AV * sub2;
      
      key1 = SvPV(one, len);
      if (!hv_exists(my_hv, key1, len)) {
          sub1 = newHV();
          hv_store(my_hv, key1, len, newRV_noinc((SV*)sub1), 0);
      }
      else {
          SV ** sub1p = hv_fetch(my_hv, key1, len, 0);
          if (sub1p == NULL) {
              croak("shouldn't happen");
          }
          sub1 = (HV*)SvRV(*sub1p);
      }
      
      key2 = SvPV(two, len);
      if (!hv_exists(sub1, key2, len)) {
          sub2 = newAV();
          hv_store(sub1, key2, len, newRV_noinc((SV*)sub2), 0);
      }
      else {
          SV ** sub2p = hv_fetch(sub1, key2, len, 0);
          if (sub2p == NULL) {
              croak("shouldn't happen");
          }
          sub2 = (AV*)SvRV(*sub2p);
      }
      
      /* warn("adding processor in %s/%s with refs: %d\n", key1, key2, SvREFCNT(value)); */
      
      av_push(sub2, value);
  }
  
  static void *
  merge_axkit_dir_config (pool *p, void *parent_dirv, void *subdirv)
  {
      axkit_dir_config *parent_dir = (axkit_dir_config *)parent_dirv;
      axkit_dir_config *subdir = (axkit_dir_config *)subdirv;
      axkit_dir_config *new = new_axkit_dir_config(p);
      
      /* Brian Wheeler found that sometimes parent is NULL */
      if (parent_dir == NULL) {
          parent_dir = create_axkit_dir_config(p, "");
      }
      
      /* warn("merge : %d with %d\n", parent_dir, subdir); */
      
  /*
      warn("[AxKit] merge: parent dir_config:\n"
          "location: %d\n"
          "parent_dir.translate_output: %d\n"
          "parent_dir.gzip_output: %d\n"
          "parent_dir.log_declines: %d\n"
          "parent_dir.stack_trace: %d\n"
          "parent_dir.reset_processors: %d\n"
          "parent_dir.cache_dir: %d\n"
          "parent_dir.config_reader_module: %d\n"
          "parent_dir.provider_module: %d\n"
          "parent_dir.default_style: %d\n"
          "parent_dir.default_media: %d\n"
          "parent_dir.cache_module: %d\n"
          "parent_dir.output_charset: %d\n"
          "parent_dir.debug_level: %d\n"
          "parent_dir.type_map: %d\n"
          "parent_dir.processors: %d\n"
          "parent_dir.dynamic_processors: %d\n"
          "parent_dir.xsp_taglibs: %d\n"
          "parent_dir.current_styles: %d\n"
          "parent_dir.current_medias: %d\n"
          "parent_dir.error_stylesheet: %d\n"
          "parent_dir.output_transformers: %d\n"
          ,
          parent_dir,
          parent_dir->translate_output,
          parent_dir->gzip_output,
          parent_dir->log_declines,
          parent_dir->stack_trace,
          parent_dir->reset_processors,
          parent_dir->cache_dir,
          parent_dir->config_reader_module,
          parent_dir->provider_module,
          parent_dir->default_style,
          parent_dir->default_media,
          parent_dir->cache_module,
          parent_dir->output_charset,
          parent_dir->debug_level,
          parent_dir->type_map,
          parent_dir->processors,
          parent_dir->dynamic_processors,
          parent_dir->xsp_taglibs,
          parent_dir->current_styles,
          parent_dir->current_medias,
          parent_dir->error_stylesheet,
          parent_dir->output_transformers
          );
      
      warn("[AxKit] created new dir_config:\n"
          "location: %d\n"
          "subdir.translate_output: %d\n"
          "subdir.gzip_output: %d\n"
          "subdir.log_declines: %d\n"
          "subdir.stack_trace: %d\n"
          "subdir.reset_processors: %d\n"
          "subdir.cache_dir: %d\n"
          "subdir.config_reader_module: %d\n"
          "subdir.provider_module: %d\n"
          "subdir.default_style: %d\n"
          "subdir.default_media: %d\n"
          "subdir.cache_module: %d\n"
          "subdir.output_charset: %d\n"
          "subdir.debug_level: %d\n"
          "subdir.type_map: %d\n"
          "subdir.processors: %d\n"
          "subdir.dynamic_processors: %d\n"
          "subdir.xsp_taglibs: %d\n"
          "subdir.current_styles: %d\n"
          "subdir.current_medias: %d\n"
          "subdir.error_stylesheet: %d\n"
          "subdir.output_transformers: %d\n"
          ,
          subdir,
          subdir->translate_output,
          subdir->gzip_output,
          subdir->log_declines,
          subdir->stack_trace,
          subdir->reset_processors,
          subdir->cache_dir,
          subdir->config_reader_module,
          subdir->provider_module,
          subdir->default_style,
          subdir->default_media,
          subdir->cache_module,
          subdir->output_charset,
          subdir->debug_level,
          subdir->type_map,
          subdir->processors,
          subdir->dynamic_processors,
          subdir->xsp_taglibs,
          subdir->current_styles,
          subdir->current_medias,
          subdir->error_stylesheet,
          subdir->output_transformers
          );
  */
  
      /* flat params */
      if (subdir->cache_dir) {
          new->cache_dir = ap_pstrdup(p, subdir->cache_dir);
      }
      else if (parent_dir->cache_dir) {
          new->cache_dir = ap_pstrdup(p, parent_dir->cache_dir);
      }
      
      if (subdir->config_reader_module) {
          new->config_reader_module = ap_pstrdup(p, subdir->config_reader_module);
      }
      else if (parent_dir->config_reader_module) {
          new->config_reader_module = ap_pstrdup(p, parent_dir->config_reader_module);
      }
      
      if (subdir->provider_module) {
          new->provider_module = ap_pstrdup(p, subdir->provider_module);
      }
      else if (parent_dir->provider_module) {
          new->provider_module = ap_pstrdup(p, parent_dir->provider_module);
      }
      
      if (subdir->styleprovider_module) {
          new->styleprovider_module = ap_pstrdup(p, subdir->styleprovider_module);
      }
      else if (parent_dir->styleprovider_module) {
          new->styleprovider_module = ap_pstrdup(p, parent_dir->styleprovider_module);
      }
      
      if (subdir->default_style) {
          new->default_style = ap_pstrdup(p, subdir->default_style);
      }
      else if (parent_dir->default_style) {
          new->default_style = ap_pstrdup(p, parent_dir->default_style);
      }
      
      if (subdir->default_media) {
          new->default_media = ap_pstrdup(p, subdir->default_media);
      }
      else if (parent_dir->default_media) {
          new->default_media = ap_pstrdup(p, parent_dir->default_media);
      }
      
      if (subdir->cache_module) {
          new->cache_module = ap_pstrdup(p, subdir->cache_module);
      }
      else if (parent_dir->cache_module) {
          new->cache_module = ap_pstrdup(p, parent_dir->cache_module);
      }
      
      if (subdir->output_charset) {
          new->output_charset = ap_pstrdup(p, subdir->output_charset);
      }
      else if (parent_dir->output_charset) {
          new->output_charset = ap_pstrdup(p, parent_dir->output_charset);
      }
      
      new->debug_level =
          subdir->debug_level != -1 ? subdir->debug_level :
                                      parent_dir->debug_level;
      
      new->translate_output =
          subdir->translate_output != -1 ? subdir->translate_output :
                                           parent_dir->translate_output;
      
      new->gzip_output =
          subdir->gzip_output != -1 ? subdir->gzip_output :
                                           parent_dir->gzip_output;
  
      new->stack_trace =
          subdir->stack_trace != -1 ? subdir->stack_trace :
                                           parent_dir->stack_trace;
      
      new->log_declines =
          subdir->log_declines != -1 ? subdir->log_declines :
                                       parent_dir->log_declines;
      
      new->no_cache =
          subdir->no_cache != -1 ? subdir->no_cache :
                                       parent_dir->no_cache;
      
      new->dependency_checks =
          subdir->dependency_checks != -1 ? subdir->dependency_checks :
                                       parent_dir->dependency_checks;
      
      
      /* complex types */
      
      {
          /* cfg->error_stylesheet */
          AV * from = NULL;
          new->error_stylesheet = newAV();
          
          if (av_len(subdir->error_stylesheet) >= 0) {
              from = subdir->error_stylesheet;
          }
          else if (av_len(parent_dir->error_stylesheet) >= 0) {
              from = parent_dir->error_stylesheet;
          }
          
          if (from) {
              char *mime;
              char *stylesheet;
              SV ** avitem;
              STRLEN len;
              
              avitem = av_fetch(from, 0, 0);
              mime = ap_pstrdup(p, SvPV(*avitem, len));
              av_push(new->error_stylesheet, newSVpvn(mime, strlen(mime)));
              
              avitem = av_fetch(from, 1, 0);
              stylesheet = ap_pstrdup(p, SvPV(*avitem, len));
              av_push(new->error_stylesheet, newSVpvn(stylesheet, strlen(stylesheet)));
          }
          
          ap_register_cleanup(p, (void*)new->error_stylesheet, ax_cleanup_av, ap_null_cleanup);
      }
      
      {
          /* cfg->type_map */
          char * key;
          I32 len;
          SV * val;
          
          new->type_map = newHV();
          
          hv_iterinit(parent_dir->type_map);
          while (val = hv_iternextsv(parent_dir->type_map, &key, &len)) {
              char * cval;
              STRLEN clen;
              cval = ap_pstrdup(p, SvPV(val, clen));
              hv_store(new->type_map, key, len, newSVpvn(cval, clen), 0);
          }
          
          hv_iterinit(subdir->type_map);
          while (val = hv_iternextsv(subdir->type_map, &key, &len)) {
              char * cval;
              STRLEN clen;
              cval = ap_pstrdup(p, SvPV(val, clen));
              hv_store(new->type_map, key, len, newSVpvn(cval, clen), 0);
          }
          
          ap_register_cleanup(p, (void*)new->type_map, ax_cleanup_hv, ap_null_cleanup);
      }
      
      {
          /* cfg->dynamic_processors */
          new->dynamic_processors = newAV();
          if (av_len(subdir->dynamic_processors) >= 0) {
              I32 key = 0;
              for(key = 0; key <= av_len(subdir->dynamic_processors); key++) {
                  SV ** val = av_fetch(subdir->dynamic_processors, key, 0);
                  if (val != NULL) {
                      char * cval;
                      STRLEN len;
                      cval = ap_pstrdup(p, SvPV(*val, len));
                      av_push(new->dynamic_processors, newSVpvn(cval, strlen(cval)));
                  }
              }
          }
          else {
              I32 key = 0;
              for(key = 0; key <= av_len(parent_dir->dynamic_processors); key++) {
                  SV ** val = av_fetch(parent_dir->dynamic_processors, key, 0);
                  if (val != NULL) {
                      char * cval;
                      STRLEN len;
                      cval = ap_pstrdup(p, SvPV(*val, len));
                      av_push(new->dynamic_processors, newSVpvn(cval, strlen(cval)));
                  }
              }
          }
          
          ap_register_cleanup(p, (void*)new->dynamic_processors, ax_cleanup_av, ap_null_cleanup);
      }
      
      {
          /* cfg->xsp_taglibs */
          new->xsp_taglibs = newHV();
          if (HvKEYS(subdir->xsp_taglibs)) {
              SV * val;
              char * key;
              I32 len;
              hv_iterinit(subdir->xsp_taglibs);
              while (val = hv_iternextsv(subdir->xsp_taglibs, &key, &len)) {
                  hv_store(new->xsp_taglibs, key, len, newSViv(1), 0);
              }
          }
          else {
              SV * val;
              char * key;
              I32 len;
              hv_iterinit(parent_dir->xsp_taglibs);
              while (val = hv_iternextsv(parent_dir->xsp_taglibs, &key, &len)) {
                  hv_store(new->xsp_taglibs, key, len, newSViv(1), 0);
              }
          }
          
          ap_register_cleanup(p, (void*)new->xsp_taglibs, ax_cleanup_hv, ap_null_cleanup);
      }
          
      {
          /* cfg->processors */
          SV * val;
          I32 len;
          char * key;
          
          new->processors = newHV();
          
          if (!subdir->reset_processors) {
              hv_iterinit(parent_dir->processors);
              while (val = hv_iternextsv(parent_dir->processors, &key, &len)) {
                  SV * subval;
                  I32 sublen;
                  char * subkey;
                  HV * subhash = (HV*)SvRV(val);
                  
                  hv_iterinit(subhash);
                  while (subval = hv_iternextsv(subhash, &subkey, &sublen)) {
                      SV * one;
                      SV * two;
                      AV * ary;
                      I32 ary_len;
                      I32 i;
                      
                      one = newSVpvn(ap_pstrdup(p, key), len);
                      two = newSVpvn(ap_pstrdup(p, subkey), sublen);
                      
                      ary = (AV*)SvRV(subval);
                      ary_len = av_len(ary);
                      
                      if (ary_len >= 0) {
                          for (i = 0; i <= ary_len; i++) {
                              SV ** elem = av_fetch(ary, i, 0);
                              if (elem) {
                                  store_in_hv2(new->processors, one, two, newSVsv(*elem));
                              }
                          }
                      }
                      
                      SvREFCNT_dec(one);
                      SvREFCNT_dec(two);
                  }
              }
          }
  
          hv_iterinit(subdir->processors);
          while (val = hv_iternextsv(subdir->processors, &key, &len)) {
              SV * subval;
              I32 sublen;
              char * subkey;
              HV * subhash = (HV*)SvRV(val);
              
              hv_iterinit(subhash);
              while (subval = hv_iternextsv(subhash, &subkey, &sublen)) {
                  SV * one;
                  SV * two;
                  AV * ary;
                  I32 ary_len;
                  I32 i;
                  
                  one = newSVpvn(ap_pstrdup(p, key), len);
                  two = newSVpvn(ap_pstrdup(p, subkey), sublen);
                  
                  ary = (AV*)SvRV(subval);
                  ary_len = av_len(ary);
                  
                  if (ary_len >= 0) {
                      for (i = 0; i <= ary_len; i++) {
                          SV ** elem = av_fetch(ary, i, 0);
                          if (elem) {
                              store_in_hv2(new->processors, one, two, newSVsv(*elem));
                          }
                      }
                  }
                  
                  SvREFCNT_dec(one);
                  SvREFCNT_dec(two);
              }
          }
          
          ap_register_cleanup(p, (void*)new->processors, ax_cleanup_hv, ap_null_cleanup);
      }
      
      {
          /* cfg->output_transformers */
          new->output_transformers = newAV();
          
          if(!subdir->reset_output_transformers) {
              I32 key = 0;
              for(key = 0; key <= av_len(parent_dir->output_transformers); key++) {
                  SV ** val = av_fetch(parent_dir->output_transformers, key, 0);
                  if (val != NULL) {
                      char * cval;
                      STRLEN len;
                      cval = ap_pstrdup(p, SvPV(*val, len));
                      av_push(new->output_transformers, newSVpvn(cval, strlen(cval)));
                  }
              }
          }
          if (av_len(subdir->output_transformers) >= 0) {
              I32 key = 0;
              for(key = 0; key <= av_len(subdir->output_transformers); key++) {
                  SV ** val = av_fetch(subdir->output_transformers, key, 0);
                  if (val != NULL) {
                      char * cval;
                      STRLEN len;
                      cval = ap_pstrdup(p, SvPV(*val, len));
                      av_push(new->output_transformers, newSVpvn(cval, strlen(cval)));
                  }
              }
          }
          
          ap_register_cleanup(p, (void*)new->output_transformers, ax_cleanup_av, ap_null_cleanup);
      }
      
      {
          /* cfg->current_plugins */
          new->current_plugins = newAV();
          
          if(!subdir->reset_plugins) {
              I32 key = 0;
              for(key = 0; key <= av_len(parent_dir->current_plugins); key++) {
                  SV ** val = av_fetch(parent_dir->current_plugins, key, 0);
                  if (val != NULL) {
                      char * cval;
                      STRLEN len;
                      cval = ap_pstrdup(p, SvPV(*val, len));
                      av_push(new->current_plugins, newSVpvn(cval, strlen(cval)));
                  }
              }
          }
          if (av_len(subdir->current_plugins) >= 0) {
              I32 key = 0;
              for(key = 0; key <= av_len(subdir->current_plugins); key++) {
                  SV ** val = av_fetch(subdir->current_plugins, key, 0);
                  if (val != NULL) {
                      char * cval;
                      STRLEN len;
                      cval = ap_pstrdup(p, SvPV(*val, len));
                      av_push(new->current_plugins, newSVpvn(cval, strlen(cval)));
                  }
              }
          }
          
          ap_register_cleanup(p, (void*)new->current_plugins, ax_cleanup_av, ap_null_cleanup);
      }
      
      new->current_styles = newAV();
      av_push(new->current_styles, newSVpv("#default", 0));
      ap_register_cleanup(p, (void*)new->current_styles, ax_cleanup_av, ap_null_cleanup);
      
      new->current_medias = newAV();
      av_push(new->current_medias, newSVpv("screen", 0));
      ap_register_cleanup(p, (void*)new->current_medias, ax_cleanup_av, ap_null_cleanup);
  
  /*
      warn("merge results: %d\n"
          "typemap: %d\n"
          "processors: %d\n"
          "dynamic: %d\n"
          "taglibs: %d\n"
          "c_styles: %d\n"
          "c_medias: %d\n"
          "out_trans: %d\n"
          ,
          new,
          new->type_map,
          new->processors,
          new->dynamic_processors,
          new->xsp_taglibs,
          new->current_styles,
          new->current_medias,
          new->output_transformers
          );
  */
      
      return new;
  }
  
  /* return string is an error. Return NULL if OK */
  
  CHAR_P
  ax_add_type_processor (cmd_parms *cmd, axkit_dir_config *ax,
                              char *mt, char *sty, char *option)
  {
      AV *processor = newAV();
      
      SV **cur_media = av_fetch(ax->current_medias, 0, 0);
      SV **cur_style = av_fetch(ax->current_styles, 0, 0);
      
      SV *type = newSVpv((char*)cmd->info, 0);
      SV *mime = newSVpv(mt, 0);
      SV *style = newSVpv(sty, 0);
      SV *opt_sv = newSVpv(option, 0);
      
      av_push(processor, type);
      av_push(processor, mime);
      av_push(processor, style);
      av_push(processor, opt_sv);
      
      store_in_hv2(ax->processors, *cur_media, *cur_style, newRV_noinc((SV*)processor));
      
      return NULL;
  }
  
  CHAR_P
  ax_add_processor (cmd_parms *cmd, axkit_dir_config *ax, char *mt, char *sty)
  {
      return ax_add_type_processor(cmd, ax, mt, sty, "");
  }
  
  CHAR_P
  ax_add_dynamic_processor (cmd_parms *cmd, axkit_dir_config *ax,
                              char *module)
  {
      SV * mod_sv = newSVpv(module, 0);
      av_push(ax->dynamic_processors, mod_sv);
      
      return NULL;
  }
  
  CHAR_P
  ax_reset_processors (cmd_parms *cmd, axkit_dir_config *ax)
  {
      ax->reset_processors++;
      
      return NULL;
  }
  
  CHAR_P
  ax_error_stylesheet (cmd_parms *cmd, axkit_dir_config *ax, char *mime, char *stylesheet)
  {
      av_push(ax->error_stylesheet, newSVpvn(mime, strlen(mime)));
      av_push(ax->error_stylesheet, newSVpvn(stylesheet, strlen(stylesheet)));
      
      return NULL;
  }
  
  CHAR_P
  ax_media_type (cmd_parms *cmd, axkit_dir_config *ax, char *media)
  {
      const char *pos = media;
      char * nextword;
      int count = 0;
      char line [MAX_STRING_LEN];
      void * oldconf;
      
      char *endp = strrchr(media, '>');
      if (!endp) {
          return "Syntax error: no terminal \">\" sign";
      }
      *endp = '\0';
      
      while (*pos && (nextword = ap_getword_conf(cmd->pool, &pos))) {
          SV *media_sv = newSVpv(nextword, 0);
          if (count++ > 0) {
              return "Syntax error: <AxMediaType> only takes one parameter";
          }
          av_unshift(ax->current_medias, 1);
          av_store(ax->current_medias, 0, media_sv);
      }
      
      oldconf = ap_get_module_config(cmd->server->lookup_defaults, &XS_AxKit);
      ap_set_module_config(cmd->server->lookup_defaults, &XS_AxKit, (void*)ax);
      
      while (!ap_cfg_getline(line, MAX_STRING_LEN, cmd->config_file)) {
          const char *errmsg;
          if (!strcasecmp(line, "</AxMediaType>")) {
              SV * ignore = av_shift(ax->current_medias);
              SvREFCNT_dec(ignore);
              break;
          }
  #ifndef WIN32
          errmsg = (const char *)ap_handle_command(cmd, cmd->server->lookup_defaults, line);
          if (errmsg) {
              return errmsg;
          }
  #endif
      }
      
      ap_set_module_config(cmd->server->lookup_defaults, &XS_AxKit, oldconf);
      
      return NULL;
  }
  
  CHAR_P
  ax_media_type_end (cmd_parms *cmd, axkit_dir_config *ax)
  {
      return "</AxMediaType> with no beginning <AxMediaType> tag";
  }
  
  CHAR_P
  ax_style_name (cmd_parms *cmd, axkit_dir_config *ax, char *style)
  {
      const char *pos = style;
      char * nextword;
      int count = 0;
      char line [MAX_STRING_LEN];
      void * oldconf;
      
      char *endp = strrchr(style, '>');
      if (!endp) {
          return "Syntax error: no terminal \">\" sign";
      }
      *endp = '\0';
      
      while (*pos && (nextword = ap_getword_conf(cmd->pool, &pos))) {
          SV *style_sv = newSVpv(nextword, 0);
          if (count++ > 0) {
              return "Syntax error: <AxStyleName> only takes one parameter";
          }
          av_unshift(ax->current_styles, 1);
          av_store(ax->current_styles, 0, style_sv);
      }
  
      oldconf = ap_get_module_config(cmd->server->lookup_defaults, &XS_AxKit);
      ap_set_module_config(cmd->server->lookup_defaults, &XS_AxKit, (void*)ax);
      
      while (!ap_cfg_getline(line, MAX_STRING_LEN, cmd->config_file)) {
          const char *errmsg;
          if (!strcasecmp(line, "</AxStyleName>")) {
              SV * ignore = av_shift(ax->current_styles);
              SvREFCNT_dec(ignore);
              break;
          }
  #ifndef WIN32
          errmsg = (const char *)ap_handle_command(cmd, cmd->server->lookup_defaults, line);
          if (errmsg) {
              return errmsg;
          }
  #endif
      }
      
      ap_set_module_config(cmd->server->lookup_defaults, &XS_AxKit, oldconf);
      
      return NULL;
  }
  
  CHAR_P
  ax_style_name_end (cmd_parms *cmd, axkit_dir_config *ax)
  {
      return "</AxStyleName> with no beginning <AxStyleName> tag";
  }
  
  CHAR_P
  ax_add_style_map (cmd_parms *cmd, axkit_dir_config *ax,
                      char *mt, char *module)
  {
      SV *module_sv;
      STRLEN len;
      
      ax_preload_module(&module);
      
      len = strlen(mt);
      module_sv = newSVpv(module, 0);
          
      hv_store(ax->type_map, mt, len, module_sv, 0);
      
      return NULL;
  }
  
  CHAR_P
  ax_reset_style_map (cmd_parms *cmd, axkit_dir_config *ax)
  {
      hv_clear(ax->type_map);
      
      return NULL;
  }
  
  CHAR_P
  ax_add_xsp_taglib (cmd_parms *cmd, axkit_dir_config *ax, char *module)
  {
      STRLEN len;
      
      ax_preload_module(&module);
      
      len = strlen(module);
      
      hv_store(ax->xsp_taglibs, module, len, newSViv(1), 0);
      
      return NULL;
  }
  
  CHAR_P
  ax_add_output_transformer (cmd_parms *cmd, axkit_dir_config *ax, char *module)
  {
      SV * mod_sv = newSVpv(module, 0);
      av_push(ax->output_transformers, mod_sv);
      
      return NULL;
  }
  
  CHAR_P
  ax_reset_output_transformers (cmd_parms *cmd, axkit_dir_config *ax)
  {
      ax->reset_output_transformers++;
      return NULL;
  }
  
  CHAR_P
  ax_add_plugin (cmd_parms *cmd, axkit_dir_config *ax, char *module)
  {
      ax_preload_module(&module);
      av_push(ax->current_plugins, newSVpv(module, 0));
      return NULL;
  }
  
  CHAR_P
  ax_reset_plugins (cmd_parms *cmd, axkit_dir_config *ax)
  {
      ax->reset_plugins++;
      return NULL;
  }
  
  static CHAR_P
  ax_set_module_slot (cmd_parms *cmd, char *struct_ptr, char *arg)
  {
      int offset;
      
      ax_preload_module(&arg);
      
      /* warn("ax_set_module_slot: %d\n", arg); */
      
      offset = (int) (long) cmd->info;
      *(char **) (struct_ptr + offset) = arg;
      return NULL;
  }
  
  static CHAR_P
  ax_set_debug_level (cmd_parms *cmd, axkit_dir_config *ax, char *arg)
  {
      ax->debug_level = atoi(arg);
      return NULL;
  }
  
  void axkit_module_init(server_rec *s, pool *p)
  {
      STRLEN len = 0;
      SV * serverstring;
      char * serverstringc;
      serverstring = perl_get_sv("AxKit::ServerString", TRUE | GV_ADDMULTI);
      serverstringc = SvPV(serverstring, len);
      /* warn("Adding server string: %s\n", serverstringc); */
      ap_add_version_component(serverstringc);
  }
  
  static int axkit_handler(request_rec *r)
  {
      int retval;
      SV * handler_sv = newSVpv("AxKit::fast_handler", 0);
      
      SV * cfg = perl_get_sv("AxKit::Cfg", FALSE);
      SV * cache = perl_get_sv("AxKit::Cache", FALSE);
      SV * hs = perl_get_sv("AxKit::HeadersSent", FALSE);
      SV * debuglevel = perl_get_sv("AxKit::DebugLevel", FALSE);
      SV * errorlevel = perl_get_sv("Error::Debug", FALSE);
      
      ENTER;
      
      save_item(cfg);
      save_item(cache);
      save_item(hs);
      save_item(debuglevel);
      save_item(errorlevel);
      
      retval = perl_call_handler(handler_sv, (request_rec *)r, Nullav);
  
      LEAVE;
      
      SvREFCNT_dec(handler_sv);
      
      return retval;
  }
  
  static command_rec axkit_mod_cmds[] = {
  
      { "AxAddProcessor", ax_add_processor,
        (void*)"NORMAL", OR_ALL, TAKE2,
        "a mime type and a stylesheet to use" },
  
      { "AxAddDocTypeProcessor", ax_add_type_processor,
        (void*)"DocType", OR_ALL, TAKE3,
        "a mime type, a stylesheet, and an XML public identifier" },
  
      { "AxAddDTDProcessor", ax_add_type_processor,
        (void*)"DTD", OR_ALL, TAKE3,
        "a mime type, a stylesheet, and a dtd filename" },
  
      { "AxAddDynamicProcessor", ax_add_dynamic_processor,
        NULL, OR_ALL, TAKE1, 
        "a package name" },
  
      { "AxAddRootProcessor", ax_add_type_processor,
        (void*)"Root", OR_ALL, TAKE3, 
        "a mime type, a stylesheet, and a root element" },
  
      { "AxAddURIProcessor", ax_add_type_processor,
        (void*)"URI", OR_ALL, TAKE3,
        "a mime type, a stylesheet, and a Perl regexp to match the URI" },
  
      { "AxResetProcessors", ax_reset_processors,
        NULL, OR_ALL, NO_ARGS,
        "reset the list of processors" },
  
      { "<AxMediaType", ax_media_type,
        NULL, OR_ALL, RAW_ARGS, 
        "Media type block" },
  
      { "</AxMediaType>", ax_media_type_end,
        NULL, OR_ALL, NO_ARGS,
        "End of media type block" },
  
      { "<AxStyleName", ax_style_name,
        NULL, OR_ALL, RAW_ARGS, 
        "Style name block" },
  
      { "</AxStyleName>", ax_style_name_end,
        NULL, OR_ALL, NO_ARGS,
        "End of Style name block" },
  
      { "AxAddStyleMap", ax_add_style_map,
        NULL, OR_ALL, TAKE2,
        "a mime type and a module name to use" },
  
      { "AxResetStyleMap", ax_reset_style_map,
        NULL, OR_ALL, NO_ARGS,
        "reset the styles" },
  
      { "AxCacheDir", ap_set_string_slot,
        (void *)XtOffsetOf(axkit_dir_config, cache_dir),
        OR_ALL, TAKE1,
        "directory to store cache files" },
  
      { "AxConfigReader", ax_set_module_slot,
        (void *)XtOffsetOf(axkit_dir_config, config_reader_module),
        OR_ALL, TAKE1,
        "alternative module to use for reading configuration" },
  
      { "AxProvider", ax_set_module_slot,
        (void *)XtOffsetOf(axkit_dir_config, provider_module),
        OR_ALL, TAKE1,
        "alternative module to use for reading the xml" },
  
      { "AxStyleProvider", ax_set_module_slot,
        (void *)XtOffsetOf(axkit_dir_config, styleprovider_module),
        OR_ALL, TAKE1,
        "alternative module to use for reading the stylesheet" },
  
      { "AxStyle", ap_set_string_slot,
        (void *)XtOffsetOf(axkit_dir_config, default_style),
        OR_ALL, TAKE1,
        "a default stylesheet (title) to use" },
  
      { "AxMedia", ap_set_string_slot,
        (void *)XtOffsetOf(axkit_dir_config, default_media),
        OR_ALL, TAKE1,
        "a default media to use other than screen" },
  
      { "AxAddOutputTransformer", ax_add_output_transformer,
        NULL, OR_ALL, TAKE1, 
        "An output transformer function, qualified with package name." },
  
      { "AxResetOutputTransformers", ax_reset_output_transformers,
        NULL, OR_ALL, NO_ARGS, 
        "Reset list of output transformers." },
  
      { "AxCacheModule", ax_set_module_slot,
        (void *)XtOffsetOf(axkit_dir_config, cache_module),
        OR_ALL, TAKE1,
        "alternative cache module" },
  
      { "AxDebugLevel", ax_set_debug_level,
        NULL, OR_ALL, TAKE1,
        "debug level (0 == none, higher numbers == more debugging)" },
  
      { "AxTranslateOutput", ap_set_flag_slot,
        (void *)XtOffsetOf(axkit_dir_config, translate_output),
        OR_ALL, FLAG,
        "On or Off [default] to automatically change character set on output" },
  
      { "AxOutputCharset", ap_set_string_slot,
        (void *)XtOffsetOf(axkit_dir_config, output_charset),
        OR_ALL, TAKE1,
        "character set used by iconv" },
  
      { "AxGzipOutput", ap_set_flag_slot,
        (void *)XtOffsetOf(axkit_dir_config, gzip_output),
        OR_ALL, FLAG,
        "On or Off [default] to gzip the output" },
  
      { "AxLogDeclines", ap_set_flag_slot,
        (void *)XtOffsetOf(axkit_dir_config, log_declines),
        OR_ALL, FLAG,
        "On or Off [default] to log why AxKit declined to process the resource" },
  
      { "AxStackTrace", ap_set_flag_slot,
        (void *)XtOffsetOf(axkit_dir_config, stack_trace),
        OR_ALL, FLAG,
        "On or Off [default] to maintain a stack trace with exceptions" },
  
      { "AxNoCache", ap_set_flag_slot,
        (void *)XtOffsetOf(axkit_dir_config, no_cache),
        OR_ALL, FLAG,
        "On or Off [default] to not cache results" },
  
      { "AxErrorStylesheet", ax_error_stylesheet,
        NULL, OR_ALL, TAKE2,
        "mime type and Error Stylesheet to use for displaying errors" },
  
      { "AxAddXSPTaglib", ax_add_xsp_taglib,
        NULL, OR_ALL, TAKE1,
        "module that provides a taglib functionality" },
      
      { "AxDependencyChecks", ap_set_flag_slot,
        (void *)XtOffsetOf(axkit_dir_config, dependency_checks),
        OR_ALL, FLAG,
      "On [default] or Off to disable dependency checking" },
  
      { "AxAddPlugin", ax_add_plugin,
          NULL, OR_ALL, TAKE1,
          "module that implements a plugin" },
  
      { "AxResetPlugins", ax_reset_plugins,
          NULL, OR_ALL, NO_ARGS,
          "reset the list of plugins" },
  
      { NULL }
  };
  
  static const handler_rec axkit_handlers[] =
  {
      {"axkit", axkit_handler},
      {NULL}
  };
  
  module MODULE_VAR_EXPORT XS_AxKit = {
      STANDARD_MODULE_STUFF,
      axkit_module_init,            /* module initializer */
      create_axkit_dir_config,      /* per-directory config creator */
      merge_axkit_dir_config,       /* dir config merger */
      NULL,                         /* server config creator */
      NULL,                         /* server config merger */
      axkit_mod_cmds,               /* command table */
      axkit_handlers,               /* [7] list of handlers */
      NULL,                         /* [2] filename-to-URI translation */
      NULL,                         /* [5] check/validate user_id */
      NULL,                         /* [6] check user_id is valid *here* */
      NULL,                         /* [4] check access by host address */
      NULL,                         /* [7] MIME type checker/setter */
      NULL,                         /* [8] fixups */
      NULL,                         /* [10] logger */
      NULL,                         /* [3] header parser */
      NULL,                         /* process initializer */
      NULL,                         /* process exit/cleanup */
      NULL,                         /* [1] post read_request handling */
  };
  
  HV *
  ax_get_config (axkit_dir_config * cfg)
  {
      HV * retval;
      
      retval = newHV();
      if (cfg->cache_dir) {
          hv_store(retval, "CacheDir", 
                  8, (newSVpv(cfg->cache_dir, 0)), 0);
      }
      if (cfg->config_reader_module) {
          hv_store(retval, "ConfigReader",
                  12, (newSVpv(cfg->config_reader_module, 0)), 0);
      }
      if (cfg->provider_module) {
          hv_store(retval, "Provider",
                  8, (newSVpv(cfg->provider_module, 0)), 0);
      }
      if (cfg->styleprovider_module) {
          hv_store(retval, "StyleProvider",
                  13, (newSVpv(cfg->styleprovider_module, 0)), 0);
      }
      if (cfg->default_style) {
          hv_store(retval, "Style",
                  5, (newSVpv(cfg->default_style, 0)), 0);
      }
      if (cfg->default_media) {
          hv_store(retval, "Media",
                  5, (newSVpv(cfg->default_media, 0)), 0);
      }
      if (cfg->cache_module) {
          hv_store(retval, "CacheModule",
                  11, (newSVpv(cfg->cache_module, 0)), 0);
      }
      if (cfg->output_charset) {
          hv_store(retval, "OutputCharset",
                  13, (newSVpv(cfg->output_charset, 0)), 0);
      }
      if (cfg->debug_level) {
          hv_store(retval, "DebugLevel",
                  10, (newSViv(cfg->debug_level)), 0);
      }
      if (cfg->translate_output != -1) {
          hv_store(retval, "TranslateOutput",
                  15, (newSViv(cfg->translate_output)), 0);
      }
      if (cfg->gzip_output != -1) {
          hv_store(retval, "GzipOutput",
                  10, (newSViv(cfg->gzip_output)), 0);
      }
      if (cfg->log_declines != -1) {
          hv_store(retval, "LogDeclines",
                  11, (newSViv(cfg->log_declines)), 0);
      }
      if (cfg->stack_trace != -1) {
          hv_store(retval, "StackTrace",
                  10, (newSViv(cfg->stack_trace)), 0);
      }
      if (cfg->no_cache != -1) {
          hv_store(retval, "NoCache",
                  7, (newSViv(cfg->no_cache)), 0);
      }
      
      if (cfg->dependency_checks != -1) {
          hv_store(retval, "DependencyChecks",
                  16, (newSViv(cfg->dependency_checks)), 0);
      }
      else {
          hv_store(retval, "DependencyChecks",
                  16, (newSViv(1)), 0);
      }
      
      hv_store(retval, "OutputTransformers",
              18, newRV_inc((SV*)cfg->output_transformers), 0);
      hv_store(retval, "ErrorStylesheet",
              15, newRV_inc((SV*)cfg->error_stylesheet), 0);
      hv_store(retval, "StyleMap",
              8, newRV_inc((SV*)cfg->type_map), 0);
      hv_store(retval, "Processors",
              10, newRV_inc((SV*)cfg->processors), 0);
      hv_store(retval, "DynamicProcessors",
              17, newRV_inc((SV*)cfg->dynamic_processors), 0);
      hv_store(retval, "XSPTaglibs",
              10, newRV_inc((SV*)cfg->xsp_taglibs), 0);
      hv_store(retval, "Plugins",
              7, newRV_inc((SV*)cfg->current_plugins), 0);
      
      return retval;
  }
  
  void remove_module_cleanup(void * ignore)
  {
      if (ap_find_linked_module(ap_find_module_name(&XS_AxKit))) {
          ap_remove_module(&XS_AxKit);
      }
      /* make sure BOOT section is re-run on restarts */
      (void)hv_delete(GvHV(incgv), "AxKit.pm", 8, G_DISCARD);
  }
  
  /* Diff for styles being relative to .htaccess:
  Index: axconfig.c
  ===================================================================
  RCS file: /home/cvs/AxKit/axconfig.c,v
  retrieving revision 1.2
  diff -r1.2 axconfig.c
  2a3
  > #define CORE_PRIVATE
  3a5
  > #include "axconfig.h"
  6c8
  < #include "axconfig.h"
  ---
  > #include "include/http_core.h"
  24a27,30
  > char *
  > ax_resolve_uri(char * base, char * uri)
  > {
  > }
  626a633
  >     SV *style;
  634d640
  <     SV *style = newSVpv(sty, 0);
  636a643,665
  >     core_server_config *server_conf = 
  >         ap_get_module_config(cmd->server->module_config, &core_module);
  >     
  >     if (sty[0] != '/' 
  >             && strcmp(sty, ".") != 0
  >             && strstr(cmd->config_file->name, server_conf->access_name)) 
  >     {
  >         style = newSVpv(
  >                     ap_pstrcat(cmd->pool, "file://",
  >                         ap_make_full_path(cmd->pool,
  >                             ap_make_dirstr_parent(
  >                                         cmd->pool,
  >                                         cmd->config_file->name
  >                             ), sty
  >                         ), NULL
  >                     ), 0);
  >     }
  >     else {
  >         style = newSVpv(sty, 0);
  >     }
  >     
  >     warn("style: %s\n", SvPV_nolen(style));
  >     
  674,675c703,725
  <     av_push(ax->error_stylesheet, newSVpvn(mime, strlen(mime)));
  <     av_push(ax->error_stylesheet, newSVpvn(stylesheet, strlen(stylesheet)));
  ---
  >     core_server_config *server_conf = 
  >         ap_get_module_config(cmd->server->module_config, &core_module);
  >     
  >     av_push(ax->error_stylesheet, newSVpvn(mime, 0));
  >     
  >     if (stylesheet[0] != '/' 
  >                 && strstr(cmd->config_file->name, server_conf->access_name)) 
  >     {
  >         av_push(ax->error_stylesheet,
  >                 newSVpv(
  >                     ap_pstrcat(cmd->pool, "file://",
  >                         ap_make_full_path(cmd->pool,
  >                             ap_make_dirstr_parent(
  >                                         cmd->pool,
  >                                         cmd->config_file->name
  >                             ), stylesheet
  >                         ), NULL
  >                     ), 0)
  >                 );
  >     }
  >     else {
  >         av_push(ax->error_stylesheet, newSVpv(stylesheet, 0));
  >     }
  */
  
  
  
  
  1.1                  xml-axkit/axconfig.h
  
  Index: axconfig.h
  ===================================================================
  /* $Id: axconfig.h,v 1.1 2002/01/13 20:45:08 matts Exp $ */
  
  #ifdef WIN32
  #define _INC_DIRENT
  #define DIR void
  #endif
  #include <EXTERN.h>
  #include <perl.h>
  #include <XSUB.h>
  #ifdef WIN32
  #ifdef lstat
  #define apache_lstat lstat
  #undef lstat
  #endif
  #include "modules/perl/mod_perl.h"
  #ifdef apache_lstat
  #undef lstat
  #define lstat apache_lstat
  #undef apache_lstat
  #endif
  #else
  #include <httpd.h>
  #include <http_config.h>
   /* SUNPRO C does not know something about __attribute__ */
   #ifdef __SUNPRO_C
    #include <http_core.h>
    #include <http_main.h>
    #include <http_protocol.h>
    #include <http_request.h>
    #include <http_log.h>
   #endif
  #endif
  
  typedef struct {
      /* simple types */
      char * cache_dir;
      char * config_reader_module;
      char * provider_module;
      char * styleprovider_module;
      char * default_style;
      char * default_media;
      char * cache_module;
      char * output_charset;
      int    debug_level;
      int    translate_output;
      int    gzip_output; 
      int    reset_processors;
      int    log_declines;
      int    stack_trace;
      int    no_cache;
      int    dependency_checks;
      int    reset_output_transformers;
      int    reset_plugins;
      
      /* complex types */
      HV *   type_map;            /* mime type => module mapping */
      HV *   processors;          /* processor map */
      AV *   dynamic_processors;  /* dynamic processor map */
      HV *   xsp_taglibs;
      AV *   current_styles;
      AV *   current_medias;
      AV *   error_stylesheet;
      AV *   output_transformers;
      AV *   current_plugins;
      
  } axkit_dir_config;
  
  extern module MODULE_VAR_EXPORT XS_AxKit;
  
  void remove_module_cleanup(void * ignore);
  
  HV * ax_get_config (axkit_dir_config * cfg);
  
  void maybe_load_module (char * name);
  
  
  
  
  1.1                  xml-axkit/getstyles.c
  
  Index: getstyles.c
  ===================================================================
  /* $Id: getstyles.c,v 1.1 2002/01/13 20:45:08 matts Exp $ */
  
  #ifdef HAVE_LIBXML2
  #include "getstyles.h"
  
  static void doctypeHandler(void *ctx,
          const xmlChar *name,
          const xmlChar *ExternalID, 
          const xmlChar *SystemID)
  {
      axkit_xml_bits * xml_bits = (axkit_xml_bits*)ctx;
      
      /* warn("doctypeHandler: %s\n", name); */
      
      if (xml_bits->start_element != NULL) {
          return;
      }
      
      xml_bits->dtd = ap_pstrdup(xml_bits->apache->pool, ExternalID);
      xml_bits->publicid = ap_pstrdup(xml_bits->apache->pool, SystemID);
  }
  
  static void startElementHandler(void *ctx, 
          const xmlChar *name, 
          const xmlChar **atts)
  {
      axkit_xml_bits * xml_bits;
      Apache r;
      int i = 0;
      
      xml_bits = (axkit_xml_bits*)ctx;
      
      /* warn("startElement: %s\n", name); */
      
      if (xml_bits->start_element != NULL) {
          return;
      }
      
      r = xml_bits->apache;
      
      xml_bits->start_element = ap_pstrdup(r->pool, name);
      
      xml_bits->start_attribs = newAV();
      
      if (atts != NULL) {
          for (i = 0;(atts[i] != NULL);i++) {
              av_push(xml_bits->start_attribs, newSVpv(ap_pstrdup(r->pool, atts[i]), 0));
  	}
      }
  }
  
  static void processingInstructionHandler(
          void *ctx,
          const xmlChar *target,
          const xmlChar *data)
  {
      axkit_xml_bits * xml_bits = (axkit_xml_bits*)ctx;
      
      if (xml_bits->start_element != NULL) {
          return;
      }
      
      if (strncmp(target, "xml-stylesheet", 14) == 0) {
          av_push(xml_bits->xml_stylesheet, 
                  newSVpv(ap_pstrdup(xml_bits->apache->pool, data), 0));
      }
  }
  
  static void errorHandler(
          void *ctx,
          const char *msg,
          ...)
  {
      va_list args;
      SV * sv;
      
      sv = NEWSV(0,0);
      
      va_start(args, msg);
      sv_vsetpvfn(sv, msg, strlen(msg), &args, NULL, 0, NULL);
      va_end(args);
      
      sv_catsv(error_str, sv);
      SvREFCNT_dec(sv);
  }
  
  xmlEntityPtr getAxEntity(void *user_data, const xmlChar *name) {
      xmlEntityPtr predef = xmlGetPredefinedEntity(name);
      
      if (predef != NULL) {
          /* warn("default entity: %s\n", name); */
          return predef;
      }
      
      /* warn("non-default entity: %s\n", name); */
      return blankEntity;
  }
  
  int
  read_perl (SV * ioref, char * buffer, int len)
  {
      dSP;
      
      int cnt;
      SV * read_results;
      STRLEN read_length;
      char * chars;
      SV * tbuff = NEWSV(0,0);
      SV * tsize = newSViv(len);
      
      ENTER;
      SAVETMPS;
      
      PUSHMARK(SP);
      EXTEND(SP, 3);
      PUSHs(ioref);
      PUSHs(sv_2mortal(tbuff));
      PUSHs(sv_2mortal(tsize));
      PUTBACK;
      
      cnt = perl_call_method("read", G_SCALAR);
      
      SPAGAIN;
      
      if (cnt != 1) {
          croak("read method call failed");
      }
      
      read_results = POPs;
      
      if (!SvOK(read_results)) {
          croak("read error");
      }
      
      read_length = SvIV(read_results);
      
      chars = SvPV(tbuff, read_length);
      strncpy(buffer, chars, read_length);
      /* terminate by NUL in case chars > buffer */
      buffer[len - 1] = 0;
      
      FREETMPS;
      LEAVE;
      
      return read_length;
  }
  
  xmlSAXHandler axkitSAXHandlerStruct = {
      doctypeHandler,
      NULL,
      NULL,
      NULL,
      NULL,
      getAxEntity,
      NULL,
      NULL,
      NULL,
      NULL,
      NULL,
      NULL,
      NULL,
      NULL,
      startElementHandler,
      NULL,
      NULL,
      NULL,
      NULL,
      processingInstructionHandler,
      NULL,
      errorHandler, /* warnings */
      errorHandler, /* errors */
      errorHandler, /* fatal errors */
      getAxEntity,
      NULL,
      doctypeHandler
  };
  
  xmlSAXHandlerPtr axkitSAXHandler = &axkitSAXHandlerStruct;
  
  xmlEntity blankEntityStruct = {
  #ifndef XML_WITHOUT_CORBA
      NULL,	        /* for Corba, must be first ! */
  #endif
      XML_ENTITY_DECL,       /* XML_ENTITY_DECL, must be second ! */
      NULL,	/* Attribute name */
      NULL,	/* NULL */
      NULL,	/* NULL */
      NULL,	/* -> DTD */
      NULL,	/* next sibling link  */
      NULL,	/* previous sibling link  */
      NULL,       /* the containing document */
  
      NULL,	/* content without ref substitution */
      NULL,	/* content or ndata if unparsed */
      0,	/* the content length */
      XML_EXTERNAL_GENERAL_PARSED_ENTITY,	/* The entity type */
      NULL,	/* External identifier for PUBLIC */
      NULL,	/* URI for a SYSTEM or PUBLIC Entity */
  
      NULL,	/* unused */
      NULL	/* the full URI as computed */
  };
  
  xmlEntityPtr blankEntity = &blankEntityStruct;
  
  #endif /* HAVE_LIBXML2 */
  
  
  
  1.1                  xml-axkit/getstyles.h
  
  Index: getstyles.h
  ===================================================================
  /* $Id: getstyles.h,v 1.1 2002/01/13 20:45:08 matts Exp $ */
  
  #ifndef WIN32
  #include <modules/perl/mod_perl.h>
  #endif
  #ifdef HAVE_LIBXML2
  #include <libxml/xmlversion.h>
  #include <libxml/xmlmemory.h>
  #include <libxml/debugXML.h>
  #include <libxml/HTMLtree.h>
  #include <libxml/xmlerror.h>
  
  #ifdef VMS
  extern int xmlDoValidityCheckingDefaultVal;
  #define xmlDoValidityCheckingDefaultValue xmlDoValidityCheckingDefaultVal
  extern int xmlSubstituteEntitiesDefaultVal;
  #define xmlSubstituteEntitiesDefaultValue xmlSubstituteEntitiesDefaultVal
  #else
  extern int xmlDoValidityCheckingDefaultValue;
  extern int xmlSubstituteEntitiesDefaultValue;
  #endif
  extern int xmlGetWarningsDefaultValue;
  extern int xmlKeepBlanksDefaultValue;
  extern int xmlLoadExtDtdDefaultValue;
  extern int xmlPedanticParserDefaultValue;
  
  typedef struct {
      Apache apache;
      AV * xml_stylesheet;
      char * start_element;
      AV * start_attribs;
      char * dtd;
      char * publicid;
  } axkit_xml_bits;
  
  extern xmlSAXHandler axkitSAXHandlerStruct;
  
  extern xmlSAXHandlerPtr axkitSAXHandler;
  
  extern SV * error_str;
  
  extern xmlEntityPtr blankEntity;
  
  int read_perl (SV * ioref, char * buffer, int len);
  
  #endif /* HAVE_LIBXML2 */
  
  
  
  
  1.1                  xml-axkit/typemap
  
  Index: typemap
  ===================================================================
  TYPEMAP
  Apache          T_APACHEOBJ
  HV *            T_HvRV
  
  OUTPUT
  T_APACHEOBJ
          sv_setref_pv($arg, \"${ntype}\", (void*)$var);
  T_HvRV
          $arg = newRV((SV*)$var);
  
  
  INPUT
  T_HvRV
          if( SvROK($arg) && (SvTYPE(SvRV($arg)) == SVt_PVHV) )
                  $var = (HV*)SvRV( $arg );
          else {
                  warn( \"${Package}::$func_name() -- $var is not an HV reference\" );
                  XSRETURN_UNDEF;
          }
  
  T_APACHEOBJ
          r = sv2request_rec($arg, \"$ntype\", cv)
  
  
  
  
  1.1                  xml-axkit/axkit.org/faq.xml
  
  Index: faq.xml
  ===================================================================
  <?xml version="1.0" encoding="ISO-8859-1"?>
  <?xml-stylesheet type="application/x-xpathscript"
  		href="/stylesheets/webpage_html.xps"
  		title="default"?>
  <!DOCTYPE webpage [
  <!ENTITY sidebar SYSTEM "/sidebar.xml">
  <!ENTITY news SYSTEM "/news.xml">
  ]>
  <webpage>
  	<head>
  		<title>AxKit - FAQ</title>
  	</head>
  	<body>
  		<section>
  			<title>Frequently Asked Questions about AxKit</title>
        <subsection>
          <title>Upon startup of Apache I get the error: 
          "Can't locate loadable object for module Apache::Log in
          @INC"</title>
          <para>
            AxKit uses Apache::Log for its logging in some places. This
            gives us some extra flexibility. However it is not built into
            mod_perl by default - you need to compile it in explicitly.
            The simplest way to do this is to just compile all the extra
            bits of mod_perl:
            <pre>
  # in the modperl directory
  perl Makefile.PL EVERYTHING=1
  make
  make test install
            </pre>
            See also the various INSTALL documents that ship with
            mod_perl, and also the <a
            href="http://perl.apache.org/guide/">mod_perl guide</a>.
          </para>
        </subsection>
  			<subsection>
  				<title>I install AxKit and Apache segfaults when it
  				starts</title>
  				<para>
  					The usual cause is currently a conflict between differing
  					versions of expat that are being loaded at the same time. The
  					problem is too deep to go into in a simple FAQ (although I'll
  					try and find a link to an explanation in a future version of
  					this FAQ). Basically the current work around is to remove
  					expat from your Apache (it is built in by default, and is used
  					for mod_dav), and to be careful with PHP, which can also cause
  					these sorts of conflicts. As a developer I respect that
  					neither of these are particularly great long term work
  					arounds, and I'm trying to bring the community of developers
  					together to solve this problem permanently.
  				</para>
          <para>
          First of all, to find out if this is your problem, execute the
          command: <code>strings /path/to/apache/bin/httpd | grep -i
          XML</code>. If there are any results at all then you are going
          to see these segfaults until you recompile Apache (and probably
          mod_perl too)
          </para>
  				<para>
  					To compile Apache without expat, simply supply the option:
  					<pre>RULE_EXPAT=NO</pre>
  					to <code>./configure</code> when you build Apache. Alternatively if you build
  					Apache with mod_perl (with mod_perl's DO_HTTPD option) it will
  					supply this option automatically for you. See also the AxKit
            INSTALL file for a recipe for building Apache and mod_perl
            together that works.
  				</para>
  				<para>
  					If none of this helps, then you may be having platform
  					difficulties with the custom configuration directives that
  					AxKit creates. Hopefully all of these have been solved
  					already, but there may be some conflicts with PHP with these.
  					If you absolutely can't do without PHP then you'll need to
  					completely remove AxKit and all the binary components from
  					your perl lib directory (check using "perl -V" on the command
  					line) by hand (sorry, but there is no "make uninstall"
  					command). Then re-compile AxKit using the following:
  					<pre>perl Makefile.PL NO_DIRECTIVES=1
  make
  make test install</pre>
  					Then start Apache, and hopefully this will fix the problem. We
  					hope that a later version of mod_perl may not require this
  					hack. When AxKit is installed with the NO_DIRECTIVES option,
  					all configuration is done via PerlSetVar. Please contact the
  					AxKit-users mailing list if you need to use this option so
  					that we can provide more documentation where needed.
  				</para>
  			</subsection>
  			
  			<subsection>
  				<title>Which Processor?</title>
  				<para>
  				AxKit ships out of the box with two processors for using
  				XSLT, and several other stylesheet language modules. The choice
  				between XML::XSLT and XML::Sablotron should generally be based
  				on whether or not XML::Sablotron works on your platform, since
  				it is a more complete implementation of XSLT and is faster than
  				XML::XSLT (which is a pure perl implementation). Sablotron is
  				known to work on Linux and Solaris, and other platforms are
  				coming online all the time.
  				</para>
  			</subsection>
  			
  			<subsection>
  				<title>Bizarre copy of ARRAY in aassign (sic)</title>
  				<para>
  				This is actually a bug in Perl 5.6. See <a href="http://www.xray.mpe.mpg.de/mailing-lists/perl5-porters/2000-06/msg00200.html">this post to perl5-porters</a> for a patch.
  				</para>
  			</subsection>
  
  			<subsection>
  				<title>Can I run AxKit on Windows?</title>
  				<para>
  				Yes, although it is not considered a primary platform, so you
          are mostly on your own with this. You can install it via 
          ActiveState's PPM as follows:
          <pre>
  ppm install 
   http://theoryx5.uwinnipeg.ca/ppmpackages/mod_perl-1.25_1.3.17-axkit.ppd
          </pre>
          Note that will install mod_perl too. You can install it without
          mod_perl using the AxKit.ppd package at the same location. If
          you have further questions, please contact the AxKit Users <ulink 
          url="/mailinglist.xml">mailing list</ulink>.
  				</para>
  			</subsection>
        
  			<subsection>
  				<title>AxKit fails on looking up stylesheets</title>
  				<para>
  				If you get an error message in your log with "OK" in it, this
          may be caused by having CGI::Carp, or some other module that
          installs a $SIG{__DIE__} handler in a broken manner. Make sure
          you don't have CGI::Carp in your startup.pl or any other place.
  				</para>
  			</subsection>
        
  			<subsection>
  				<title>AxKit on Debian</title>
  				<para>
  AxKit has "problems" detecting iconv (and probably libxml2) on Debian. We're
  not sure what is causing this yet, so in order to get around these problems
  you will need to edit the Makefile.PL files (in the root directory, and
  in lib/Apache/AxKit/) to comment out the checks for these libraries and just
  enforce their usage regardless. Sorry for the inconvenience - we're working
  on it, and would be interested in patches.
  				</para>
  			</subsection>
  		</section>
  	</body>
  </webpage>
  
  
  
  1.1                  xml-axkit/axkit.org/features.xml
  
  Index: features.xml
  ===================================================================
  <?xml version="1.0" encoding="ISO-8859-1"?>
  <?xml-stylesheet type="application/x-xpathscript"
  		href="/stylesheets/webpage_html.xps"
  		title="default"?>
  <!DOCTYPE webpage [
  <!ENTITY sidebar SYSTEM "/sidebar.xml">
  <!ENTITY news SYSTEM "/news.xml">
  ]>
  <webpage>
  	<head>
  		<title>AxKit - Features</title>
  	</head>
  	<body>
  		<section>
  			<title>Summary of AxKit Features</title>
  			<para>
  				XSLT based pipelined XML transformations
  			</para>
  			<para>
  				Standards base stylesheet selection criteria
  			</para>
  			<para>
  			  Output in more than 180 different character sets (although
  				browser support means that only about 10 of those are useful).
  			</para>
  			<para>
  				XSP support for the Perl language, including Perl DBI based SQL
  				taglib
  			</para>
  			<para>
  				Built in Perl interpreter - no more CGI overhead
  			</para>
  			<para>
  				Plug-in API - All components are replaceable with custom components.
  			</para>
  			<para>
  				Support for almost every platform around: Linux, Solaris, HPUX,
  				AIX, Windows NT, BeOS, *BSD, VMS.
  			</para>
  			<para>
  				Intelligent caching technology means your pages will be
  				delivered as fast as static pages
  			</para>
  			<para>
  				Deliver to different media using the sames set of URLs for all
  				media types. Media types supported are: Screen (web browsers),
  				Handheld (such as palmtop computers and mobile phones), TV (for
  				example Sky's "Open", or Microsoft's WebTV), braille, aural,
  				print, projection and tty (for text-only browsers).
  			</para>
  			<para>
  				GZipped output encoding - great for slower links
  			</para>
  		</section>
  	</body>
  </webpage>
  
  
  
  1.1                  xml-axkit/axkit.org/index.xml
  
  Index: index.xml
  ===================================================================
  <?xml version="1.0" encoding="ISO-8859-1"?>
  <?xml-stylesheet type="application/x-xpathscript"
  		href="/stylesheets/webpage_html.xps"
  		title="default"?>
  		
  <?xml-stylesheet type="application/x-xpathscript"
  		href="/stylesheets/new/webpage_html.xps"
  		title="new"
  		alternate="yes"?>
  
  <!DOCTYPE webpage [
  <!ENTITY sidebar SYSTEM "/sidebar.xml">
  <!ENTITY news SYSTEM "/news.xml">
  ]>
  
  <webpage>
  	<head>
  		<title>Apache AxKit</title>
  	</head>
  	<body>
  		<section>
  			<title>Welcome to AxKit!</title>
  			<para><em>/'aks/-kit</em></para>
  			<para>
  				Apache AxKit is an XML Application Server for <ulink
  				url="http://www.apache.org/httpd.html">Apache</ulink>. It provides
  				on-the-fly conversion from XML to <ulink
  				url="http://www.w3.org/TR/REC-html40/types.html#type-media-descriptors"
  				>any format</ulink>, such as HTML, WAP
  				or text using either <ulink url="http://www.w3.org/">W3C</ulink>
  				standard techniques, or flexible custom code. AxKit also
  				uses a built-in Perl interpreter to provide some amazingly
  				powerful techniques for XML transformation.
  			</para>
  			<para>
  				AxKit makes the separation of content and presentation a
          practical reality. The pipelining technique that AxKit uses allows
  				content to be converted to a presentable format in stages,
  				allowing certain platforms to see data differently to others.
  				AxKit allows web designers to focus on web site design,
  				content developers to work on a purely content basis, and
  				webmasters to focus on their core competencies.
  			</para>
  			<para>
  				Ax<emphasis>Kit</emphasis> is based on components that
  				are replaceable. This allows the web site developer to create a
  				completely custom XML Application Server based around the AxKit
  				API. AxKit can either automatically provide caching facilities,
  				or you can create your own cache handler, so that XML
  				transformations (which can be time consuming) only happen when
  				required.
  			</para>
  			<para>
  				The toolkit also provides ability to build component based web
  				sites, and dynamic content. Database integration is fully
  				supported, allowing either delivery of XML from a database, or
  				XML generation from a database query. Dynamic web components can
  				be built using the Perl language, making the possibilities as
  				infinite as CGI scripts, without the potential mess that CGI
  				programming can cause.
  			</para>
  			<para>
  				For more information please follow the links on this page.
  			</para>
  		</section>
  	</body>
  </webpage>
  
  
  
  1.1                  xml-axkit/axkit.org/install.xml
  
  Index: install.xml
  ===================================================================
  <?xml version="1.0" encoding="ISO-8859-1"?>
  <?xml-stylesheet type="application/x-xpathscript"
  		href="/stylesheets/webpage_html.xps"
  		title="default"?>
  <!DOCTYPE webpage [
  <!ENTITY sidebar SYSTEM "/sidebar.xml">
  <!ENTITY news SYSTEM "/news.xml">
  ]>
  <webpage>
  	<head>
  		<title>AxKit - Installation</title>
  	</head>
  	<body>
  		<section>
  			<title>Installing AxKit</title>
  			<para>
  			Please see the "Introduction to AxKit" article in the
  			Documentation section.
  			</para>
  		</section>
  	</body>
  </webpage>
  
  
  
  1.1                  xml-axkit/axkit.org/license.xml
  
  Index: license.xml
  ===================================================================
  <?xml version="1.0" encoding="ISO-8859-1"?>
  <?xml-stylesheet type="application/x-xpathscript"
  		href="/stylesheets/webpage_html.xps"
  		title="default"?>
  <!DOCTYPE webpage [
  <!ENTITY sidebar SYSTEM "/sidebar.xml">
  <!ENTITY news SYSTEM "/news.xml">
  ]>
  <webpage>
  	<head>
  		<title>AxKit - License</title>
  	</head>
  	<body>
  		<section>
  			<title>License for AxKit</title>
  			<para>
                          Since AxKit is now an official <a
                          href="http://apache.org/">Apache</a> project,
                          it is now licensed under the Apache license,
                          which you can find <a
                          href="http://www.opensource.org/licenses/apachepl.html">here</a>.
  			</para>
  		</section>
  	</body>
  </webpage>
  
  
  
  1.1                  xml-axkit/axkit.org/mailinglist.xml
  
  Index: mailinglist.xml
  ===================================================================
  <?xml version="1.0" encoding="ISO-8859-1"?>
  <?xml-stylesheet href="/stylesheets/webpage_html.xps"
  	type="application/x-xpathscript" title="default"?>
  <webpage>
  <head>
  	<title>AxKit - Mailing List</title>
  </head>
  <body>
  	<section>
  		<title>Mailing List</title>
  		<para>
  			AxKit has a flourishing mailing list for both support and
  			development of AxKit. Any questions you have about AxKit are best
  			asked on the list, rather than directly emailing someone.
  		</para>
  		<para>
  			To join the AxKit mailing list, send a blank email to: <ulink
  			url="mailto:axkit-users-subscribe@axkit.org?subject=Just%20Hit%20Send..."
  			>axkit-users-subscribe@axkit.org</ulink>.
  		</para>
  		<para>
  			Alternatively you can use this web form to either join or leave
  			the list:
  			<form method="POST" action="/cgi-bin/ml-sub-request-axkit">
  			<table>
  			<tr><td>
  			<input type="radio" name="list_request" value="subscribe" checked="checked"></input>Subscribe<br></br>
  			<input type="radio" name="list_request" value="unsubscribe"></input>Unsubscribe
  			</td><td>Email Address: <input type="text" name="user_email" size="40" maxlength="100"></input></td><td><input type="submit" value="submit"></input></td></tr>
  			</table>
  			<input type="hidden" name="listtarget" value="axkit-users"></input>
  			<input type="hidden" name="acktitle" value="Thank You!"></input>
  			<input type="hidden" name="acktext" value="Thanks for your interest! Click &lt;a href=&quot;/&quot;&gt;here&lt;/a&gt; to return to the main page."></input>
  			</form>
  		</para>
  		<subsection>
  			<title>List Archive</title>
  			<para>
  				The AxKit mailing list archive is online at <ulink
  				url="http://axkit.org/cgi-bin/ezmlm-cgi/3"
  				>http://axkit.org/cgi-bin/ezmlm-cgi/3</ulink>.
  			</para>
  		</subsection>
  	</section>
    <section>
      <title>Developers List</title>
      <para>
        There is also a developers mailing list. This list is strictly for 
        core AxKit developers, and is not for general questions about the
        usage of AxKit. Here we discuss the architecture and new features,
        and the list also recieves a list of all new check-ins to the code
        repository along with changelog details.
      </para>
      <para>
        To join AxKit-Devel, send a blank email to: <ulink
        url="mailto:axkit-devel-subscribe@axkit.org?subject=Just%20Hit%20Send..."
        >axkit-devel-subscribe@axkit.org</ulink>.
      </para>
      <para>
        Alternatively you can use this web form either join or leave
        the list:
        <form method="POST" action="/cgi-bin/ml-sub-request-axkit">
  			<table>
  			<tr><td>
  			<input type="radio" name="list_request" value="subscribe" checked="checked"></input>Subscribe<br></br>
  			<input type="radio" name="list_request" value="unsubscribe"></input>Unsubscribe
  			</td><td>Email Address: <input type="text" name="user_email" size="40" maxlength="100"></input></td><td><input type="submit" value="submit"></input></td></tr>
  			</table>
  			<input type="hidden" name="listtarget" value="axkit-devel"></input>
  			<input type="hidden" name="acktitle" value="Thank You!"></input>
  			<input type="hidden" name="acktext" value="Thanks for your interest! Click &lt;a href=&quot;/&quot;&gt;here&lt;/a&gt; to return to the main page."></input>
  			</form>
  		</para>
  		<subsection>
  			<title>List Archive</title>
  			<para>
  				The AxKit Devel mailing list archive is online at <ulink
  				url="http://axkit.org/cgi-bin/ezmlm-cgi/4"
  				>http://axkit.org/cgi-bin/ezmlm-cgi/4</ulink>.
  			</para>
  		</subsection>
  	</section>
        
  </body>
  </webpage>
  
  
  
  1.1                  xml-axkit/axkit.org/news.xml
  
  Index: news.xml
  ===================================================================
  <?xml version="1.0" encoding="ISO-8859-1"?>
  <!DOCTYPE rss PUBLIC "-//Netscape Communications//DTD RSS 0.91//EN"
                "http://my.netscape.com/publish/formats/rss-0.91.dtd">
  <rss version="0.91">
     <channel>
  
     <title>xmlhack</title>
     <link>http://www.xmlhack.com</link>
     <description>Developer news from the XML community</description>
     <language>en-us</language>
     <copyright>Copyright 1999-2001, xmlhack team.</copyright>
     <managingEditor>editor@xmlhack.com</managingEditor>
     <webMaster>webmaster@xmlhack.com</webMaster>
  
        <image>
          <title>xmlhack</title>
          <url>http://www.xmlhack.com/images/mynetscape88.gif</url>
          <link>http://www.xmlhack.com</link>
          <width>88</width>
          <height>31</height>
          <description>News, opinions, tips and issues concerning XML development</description>
        </image>
  
  <item>
  <title>XSLT, XPath, and XQuery drafts</title>
  <link>http://www.xmlhack.com/read.php?item=1493</link>
  <description>The W3C has given developers a huge pile of holiday reading, with first drafts of XSLT 2.0 and XPath 2.0, as well as a collection of revised query drafts.</description>
  <category>XSLT</category>
  <category>Databases</category>
  </item>
  <item>
  <title>Does XQuery fit all?</title>
  <link>http://www.xmlhack.com/read.php?item=1492</link>
  <description>While XQuery is usually considered an inappropriate tool for querying RDF collections or Topic Maps, Jonathan Robie demonstrated at XML 2001 that using XQuery functions over their normalized forms can be practical and not especially complex.</description>
  <category>Databases</category>
  <category>RDF</category>
  </item>
  <item>
  <title>XBeans in second release</title>
  <link>http://www.xmlhack.com/read.php?item=1491</link>
  <description>XBeans.org has issued a second release of XBeans, improving its JavaBean and DOM-based data flows with a GUI customizer tool for making translations between the vocabularies expected by various XBeans.</description>
  <category>Java</category>
  <category>XML</category>
  </item>
  <item>
  <title>Five challenges for XML</title>
  <link>http://www.xmlhack.com/read.php?item=1490</link>
  <description>James Clark, the first
  recipient of the IDEAlliance XML Cup Award and opening keynote speaker at XML
  2001, gave a lively description of the five challenges facing the XML community.</description>
  <category>Community</category>
  <category>Comment</category>
  </item>
  <item>
  <title>XML 2001: open and balanced</title>
  <link>http://www.xmlhack.com/read.php?item=1489</link>
  <description>XML 2001, opened by its new chair Lauren Wood, marked the arrival of XML schema languages and a new competition between the W3C and ISO for XML standardization.</description>
  <category>Community</category>
  <category>Schemas</category>
  </item>
  <item>
  <title>XML 1.1 Working Draft Published</title>
  <link>http://www.xmlhack.com/read.php?item=1488</link>
  <description>The W3C have announced the first public version of a Working 
  Draft of XML 1.1. The new draft has been issued to 
  meet the 'Blueberry' requirements published earlier this 
  year to much community debate.</description>
  <category>XML</category>
  <category>W3C</category>
  </item>
  
  </channel>
  </rss>
  
  
  
  1.1                  xml-axkit/axkit.org/sidebar.xml
  
  Index: sidebar.xml
  ===================================================================
  <sb:sidebar xmlns:sb="http://axkit.org/NS/sidebar-1.0">
  
  	<sb:section>
  		<sb:title>About AxKit</sb:title>
  		<sb:item>
  			<sb:title>Index</sb:title>
  			<sb:url>/index.xml</sb:url>
  		</sb:item>
  		<sb:item>
  			<sb:title>Features</sb:title>
  			<sb:url>/features.xml</sb:url>
  		</sb:item>
  		<sb:item>
  			<sb:title>Installation</sb:title>
  			<sb:url>/install.xml</sb:url>
  		</sb:item>
      <sb:item>
        <sb:title>Daily Churn</sb:title>
        <sb:url>/irclog.xml</sb:url>
      </sb:item>
  	</sb:section>
  	
  	<sb:section>
  		<sb:title>Getting AxKit</sb:title>
  		<sb:item>
  			<sb:title>License</sb:title>
  			<sb:url>/license.xml</sb:url>
  		</sb:item>
  		<sb:item>
  			<sb:title>Download</sb:title>
  			<sb:url>/download/</sb:url>
  		</sb:item>
  		<sb:item>
  			<sb:title>Mailing List</sb:title>
  			<sb:url>/mailinglist.xml</sb:url>
  		</sb:item>
  		<sb:item>
  			<sb:title>Contribute</sb:title>
  			<sb:url>/contribute.xml</sb:url>
  		</sb:item>
  		<sb:item>
  			<sb:title>Support</sb:title>
  			<sb:url>/support.xml</sb:url>
  		</sb:item>
  		<sb:item>
  			<sb:title>Bugs</sb:title>
                          <sb:url>http://rt.cpan.org/NoAuth/Bugs.html?Dist=AxKit</sb:url>
  		</sb:item>
  	</sb:section>
  
  	<sb:section>
  		<sb:title>Documentation</sb:title>
  		<sb:item>
  			<sb:title>Quick Start Guide</sb:title>
  			<sb:url>/docs/quick_start.dkb</sb:url>
  		</sb:item>
  		<sb:item>
  			<sb:title>Introduction to AxKit</sb:title>
  			<sb:url>/docs/introduction.dkb</sb:url>
  		</sb:item>
  		<sb:item>
  			<sb:title>Associating XML Files with Stylesheet</sb:title>
  			<sb:url>/docs/associating_stylesheets.xml</sb:url>
  		</sb:item>
  		<sb:item>
  			<sb:title>XPathScript Guide</sb:title>
  			<sb:url>/docs/xpathscript/guide.dkb</sb:url>
  		</sb:item>
  
  		<sb:subsection expand="no">
  			<sb:title>XSP</sb:title>
  			<sb:url>/docs/xsp/index.xml</sb:url>
  			<sb:item>
  				<sb:title>XSP Guide</sb:title>
  				<sb:url>/docs/xsp/guide.dkb</sb:url>
  			</sb:item>
  			<sb:item>
  				<sb:title>SQL Taglib</sb:title>
  				<sb:url>/docs/xsp/sqltaglib.dkb</sb:url>
  			</sb:item>
  			<sb:item>
  				<sb:title>Writing Taglibs</sb:title>
  				<sb:url>/docs/xsp/writing_taglibs.dkb</sb:url>
  			</sb:item>
  		</sb:subsection>
  		
  		<sb:item>
  			<sb:title>FAQ</sb:title>
  			<sb:url>/faq.xml</sb:url>
  		</sb:item>
  
  		<sb:item>
                          <sb:title>Deutsches Artikel @ iX</sb:title>
                          <sb:url>http://www.heise.de/ix/artikel/2001/03/167/</sb:url>
  		</sb:item>
  
                  <sb:subsection expand="no">
                      <sb:title>Presentations</sb:title>
                      <sb:url>/docs/presentations/index.xml</sb:url>
                      <sb:item>
                          <sb:title>TPC 2001 Presentations</sb:title>
                          <sb:url>/docs/presentations/tpc2001/</sb:url>
                      </sb:item>
                  </sb:subsection>
   
      <sb:item>
        <sb:title>Provider HOWTO</sb:title>
        <sb:url>/docs/provider-howto.dkb</sb:url>
      </sb:item>
  		
  	</sb:section>
  	
  	<sb:section>
  		<sb:title>Examples</sb:title>
  		<sb:item>
  			<sb:title>DocBook Example</sb:title>
  			<sb:url>/examples/docbook.dkb</sb:url>
  		</sb:item>
  		<sb:item>
  			<sb:title>CV Example</sb:title>
  			<sb:url>/examples/cv_example.xml</sb:url>
  		</sb:item>
  	</sb:section>
  
  </sb:sidebar>
  
  
  
  1.1                  xml-axkit/axkit.org/support.xml
  
  Index: support.xml
  ===================================================================
  <?xml version="1.0" encoding="ISO-8859-1"?>
  <?xml-stylesheet type="application/x-xpathscript"
  		href="/stylesheets/webpage_html.xps"
  		title="default"?>
  <!DOCTYPE webpage [
  <!ENTITY sidebar SYSTEM "/sidebar.xml">
  <!ENTITY news SYSTEM "/news.xml">
  ]>
  <webpage>
  	<head>
  		<title>AxKit - Support</title>
  	</head>
  	<body>
  		<section>
  			<title>Commercial Support for AxKit</title>
  			<para>
  				Commercial support for AxKit is available from AxKit.com
  				Ltd (the developers of AxKit). The commercial support site for
  				AxKit is <ulink url="http://axkit.com/">axkit.com</ulink>.
  			</para>
        <para>
        While this level of support is available, we urge you to try the
        free support channels listed below.
        </para>
  		</section>
  		<section>
  			<title>Free Support</title>
  			<para>
        AxKit has excellent free community based support, where the core
        development team regularly participate by answering questions.
        There are two main avenues for this support: a mailing list, and
        an IRC chat channel.
  			</para>
        <subsection>
          <title>Mailing List</title>
          <para>
          The AxKit mailing list has its <ulink url="mailinglist.xml">own
          page</ulink> with subscription and archive details.
          </para>
        </subsection>
        <subsection>
          <title>IRC</title>
          <para>
              The AxKit IRC channel is available on the Rhizomatic IRC
              servers at irc.rhizomatic.net:6667. The channel name is
              #axkit. We're a friendly bunch there, but don't worry if
              you see us shouting <ulink url="http://berjon.com/dahut.txt">DAHUUUUT!!!</ulink> at eachother from time to time.
          </para>
        </subsection>
  		</section>
  	</body>
  </webpage>
  
  
  
  1.1                  xml-axkit/axkit.org/docs/associating_stylesheets.xml
  
  Index: associating_stylesheets.xml
  ===================================================================
  <?xml version="1.0" encoding="ISO-8859-1"?>
  <?xml-stylesheet href="/stylesheets/webpage_html.xps" type="application/x-xpathscript"?>
  <webpage>
  	<head>
  		<title>How AxKit Picks Stylesheets and/or Processors</title>
  	</head>
  <body>
  <section>
  <title>Introduction</title>
  <p>
  AxKit works mainly by associating stylesheets (and by inference, a
  stylesheet processor), with XML files when you request the XML file. The
  process is defined in <a
  href="http://www.w3.org/TR/REC-html40">REC-html40</a>, however the links
  are confusing so I hope to clear things up a bit here.
  </p>
  <p>
  Please note that while the specification we're talking about is the HTML
  4 specification, this has nothing specifically to do with HTML. The <a
  href="http://www.w3.org/TR/xml-stylesheet">xml-stylesheet</a> document
  simply references the HTML 4 spec to save re-iterating what has already
  been specified elsewhere.
  </p>
  </section>
  <section>
  <title>How it works</title>
  <p>
  When an XML file is encountered (determined by AxKit's XMLFinder module
  - which checks first for a .xml extension, then for a &lt;?xml?>
  processing instruction), AxKit needs to determine how to process that
  file. To do so it needs a list of stylesheets to process that file
  against. The stylesheet type (a MIME type) determines the module used to
  actually process the XML file together with the Stylesheet. It's also
  worth noting that a stylesheet file isn't necessary if using a
  DefaultStyleMap - this benefit could be useful, for example, for a
  Cocoon-like XSP page where there is no stylesheet to interact with.
  </p>
  <p>
  First, the &lt;?xml-stylesheet?> processing instructions are considered
  </p>
  <p>
  If the media doesn't match it is discarded immediately. Media types in
  &lt;?xml-stylesheet?> are processed as follows: First they are split
  on /,\s*/. Then each element of the resulting array is stripped of
  characters following and including the first non-ascii-alphanumeric
  character. So "printer and dpi > 90" gets turned into "printer". Then they
  are grepped for
  /^screen|tty|printer|handheld|braille|tv|projection|aural|all$/ (case
  sensitive). Finally the match test: A media type of "all" always matches
  regardless of what is in preferred_media. Otherwise, a case sensitive
  match is done (in perl terms, the "eq" operator). Provided a media
  matches, then we go on to determine if this stylesheet should be used
  depending on the title="..." attribute and the
  alternate="..." attribute. The terms "Persistant", "Preferred" and
  "Alternate" below are straight from REC-html40.
  </p>
  <subsection>
  <title>Persistant Stylesheets</title>
  <p>
  If there is no title and alternate="no" (or no
  alternate="..." attribute) then the stylesheet is added to the front of the list
  of stylesheets - this is a persistant stylesheet and is _always_ applied
  first (unless there are other persistant stylesheets, in which case this
  is applied after those).
  </p>
  </subsection>
  <subsection>
  <title>Preferred Stylesheets</title>
  <p>
  If there is a title and alternate="no" (or no alternate attrib) then the
  stylesheet is pushed onto the list of stylesheets to apply if there is
  either no preferred_style, or preferred_style matches.
  </p>
  </subsection>
  <subsection>
  <title>Alternate Stylesheets</title>
  <p>
  If there is a title and alternate="yes", then the stylesheet is pushed
  onto the list of stylesheets to apply if there is a preferred_style and it
  matches the title.
  </p>
  </subsection>
  <subsection>
  <title>If all else fails</title>
  <p>
  Finally, just to be safe, StyleFinder maintains a list of
  media="screen" stylesheets. If there are no stylesheets in the list at the
  end of processing, the "screen" ones are used. Note though that the same
  rules as above apply there too, and media="screen" is just used as a last
  resort.
  </p>
  </subsection>
  </section>
  <section>
  <title>And Finally...</title>
  <p>
  So when the parser that reads the &lt;?xml-stylesheet?>'s returns, if it
  returns a list of stylesheets, then fine. If not, we read
  $cfg->DefaultStyleMap, the default implementation of which is to read the
  AxAddDefaultStyleMap options in your httpd.conf or .htaccess. The details
  of AxAddDefaultStyleMap are in the AxKit manpage.
  </p>
  </section>
  </body>
  </webpage>
  
  
  
  1.1                  xml-axkit/axkit.org/docs/index.xml
  
  Index: index.xml
  ===================================================================
  <?xml version="1.0" encoding="ISO-8859-1"?>
  <?xml-stylesheet type="application/x-xpathscript"
  		href="/stylesheets/webpage_html.xps"
  		title="default"?>
  <!DOCTYPE webpage [
  <!ENTITY sidebar SYSTEM "/sidebar.xml">
  <!ENTITY news SYSTEM "/news.xml">
  ]>
  <webpage>
  	<head>
  		<title>AxKit - Documentation</title>
  	</head>
  	<body>
  		<section>
  			<title>Documentation</title>
  			<para>
  				AxKit is a complex and powerful system, although getting started
  				in AxKit is as easy as 1,2,3. The documentation here hopes to
  				guide you through AxKit right from simple tasks such as
  				transforming XML files using stylesheets, up to extending the
  				AxKit application server to suit your needs.
  			</para>
  			<para>
  				Please start with the getting started with AxKit guide. More
  				advanced XMLers might be interested in XPathScript - an
  				alternative transformation language to XSLT. If XSLT were
  				likened to awk, XPathScript would be Perl.
  			</para>
  		</section>
  	</body>
  </webpage>
  
  
  
  1.1                  xml-axkit/axkit.org/docs/introduction.dkb
  
  Index: introduction.dkb
  ===================================================================
  <?xml version="1.0" encoding="ISO-8859-1"?>
  <?xml-stylesheet href="/stylesheets/docbook_screen.xps" type="application/x-xpathscript"
  title="default"?>
  <?xml-stylesheet href="/stylesheets/docbook_print.xps"
  type="application/x-xpathscript" title="print" alternate="yes"?>
  <!DOCTYPE article [
  <!ENTITY prompt "&#x25; ">
  ]>
  <article>
  
  <artheader>
  	<title>An Introduction to AxKit</title>
  	
  	<author>
  		<firstname>Matt</firstname>
  		<surname>Sergeant</surname>
  		<affiliation>
  			<address><email>matt@sergeant.org</email></address>
  		</affiliation>
  	</author>
  	
  	<copyright>
  		<year>2000</year>
  		<holder role="mailto:matt@sergeant.org">AxKit.com Ltd</holder>
  	</copyright>
  
  	<abstract>
  		<para>An introduction to AxKit, the XML Application Server for
  		Apache</para>
  	</abstract>
  </artheader>
  
  <sect1>
  <title>Introduction</title>
  <para>
  XML has, in theory, solved one of the problems facing web site
  developers: How to develop a consistent look across your site using a
  template/stylesheet system. Unfortunately a lot of the solution to that
  problem still remains out of reach of the majority of web sites. One
  major piece of the puzzle that still has not been perfected is the
  authoring stage. Authoring XML for non-XML savvy designers is still
  problematic. Some tools exist to solve this problem, such as XMetaL, but
  as first generation tools these still have rather large weak spots. I
  expect to see this side of the puzzle solved with this seasons software
  releases - I hope I'm not wrong.
  </para>
  <para>
  The other side of the puzzle for web developers is delivery. Its going
  to be an extremely long time until all clients support some sort of
  client side transformation. And I remain unconvinced this is the right
  way to do it. Take XSLT as an example; the Apache group are right now
  trying to develop a way to do XSLT without building an in-memory tree
  structure of the XML document. However, as an implementor of XPath
  (Perl's XML::XPath module) myself, I think they are going to find this
  a really tough nut to crack. Not only that, but all this parsing is
  extremely resource intensive, and I think that's the wrong model to be
  looking at, especially when we want to deliver to handheld devices.
  </para>
  <para>
  That leaves server side transformation. There are several available
  options for this. The most immediately obvious is static transformation.
  However that can start to become a maintainence nightmare. Then there
  are application servers that operate within their own world, such as
  Enhydra and Zope. These are excellent solutions for shops that already
  use those solutions. Finally there is <application>Cocoon</application>,
  a truly awsome technology,
  now part of the <application><ulink url="http://www.apache.org/">Apache
  project</ulink></application>. Cocoon is a full blown Application
  server built around XML technology. Part of Cocoon is a system which
  associates stylesheets with XML files. See <ulink
  url="http://www.w3.org/TR/xml-stylesheet">http://www.w3.org/TR/xml-stylesheet</ulink>
  for details on this method. I don't personally have any gripes with
  Cocoon (I do have gripes with Java, but thats another issue). To an
  extent AxKit emulates Cocoon (although in reality, Cocoon and AxKit are
  just implementing suggested standards).
  </para>
  <para>
  AxKit fits into this picture by providing simple intuitive ways for web
  developers to deliver XML to clients in different media formats and
  stylesheets. AxKit also, very much like Cocoon, provides caching
  facilities built into its core, so that only at single points of change
  will AxKit attempt to re-create the document being delivered. Unless, of
  course, the developer explicitly decides not to cache the document.
  Unlike Cocoon though, AxKit is built in perl, and integrates extremely
  tightly with Apache. AxKit also provides some of the technology natively
  that Cocoon 2 is going to deliver (AxKit also doesn't provide some of
  the technology that Cocoon does deliver!).
  </para>
  </sect1>
  
  <sect1>
  <title>Why Choose AxKit?</title>
  <para>
  AxKit is based on a plugin architecture. This allows the developer to
  very quickly design modules based on currently available technology to
  achieve: New stylesheet languages, new methods for delivering alternate
  stylesheets and new methods for determining media types. Because it's
  built in perl, these sort of plugins are incredibly simple to develop.
  Not long after releasing AxKit, a developer wrote a file suffix
  stylesheet chooser module, which returns different stylesheets if the
  user requests file.xml.html or file.xml.text, in just 15 lines of code.
  This plugin architecture also makes developing new stylesheet modules
  very simple, using some of the readily available code in Perl's
  excellent CPAN (the Comprehensive Perl Archive Network). A stylesheet
  module to deliver XML-News files as HTML would only take a few lines of
  code based on David Megginson's XMLNews::HTMLTemplate module, and AxKit
  works out all the nuances of caching for you.
  </para>
  <para>
  Another important part of this is that AxKit is
  <emphasis>pragmatic</emphasis> about what it delivers to clients. It
  doesn't have to be HTML, or XHTML, strict HTML 4, or indeed compliant to
  any particular standard. This decision was made because no matter what,
  clients are still not going to upgrade their browsers just because you
  want them to. So AxKit says that you can deliver XML or XHTML if you
  want to (and the tools are there for you to do so), but its just as easy
  to deliver any other format.
  </para>
  <para>
  AxKit comes with a number of pre-built stylesheet modules, including
  two XSLT modules: one built around Perl's XML::XSLT module, a DOM based
  XSLT implementation that is in the beginning stages, and one built
  around Ginger Alliance Ltd's Sablotron XSLT library, which is a much
  more complete XSLT implementation built in C++, and is extremely fast.
  For the closet XSLT haters out there (come on - I know there are quite
  a few!) there's XPathScript - a language that takes some of the good
  features of XSLT, such as node matching and finding using XPath, and
  combines it with the power of ASP-like code integration and in-built
  Perl scripting. XPathScript also compiles your stylesheet into native
  perl code whenever it changes, so execution times are very good for XML
  stylesheet processing. As an example of XPathScript's power, I've
  created a DocBook stylesheet that dynamically can show separate sections
  of a DocBook/XML file.
  </para>
  <para>
  The core of AxKit is also very quick. Delivering cached results
  it runs at about 80% of the speed of Apache. It achieves this primarily
  because it's built in mod_perl. The tight coupling with Apache that
  mod_perl provides means that an awful lot of the code is running in
  compiled C. In order to deliver cached results, AxKit just tells Apache
  where to find the cached file, and that it doesn't want to handle it.
  Apache comes up with the goods at its usual lightning speed.
  </para>
  <para>
  Finally, AxKit works hand-in-hand with Apache. So any webmaster skills
  will not go to waste. Cocoon 2 is about to deliver a sitemap feature,
  whereby you don't have to use <literal>&lt;?xml-stylesheet?></literal> processing
  instructions everywhere to build up your site. AxKit already provides
  this, and integrates directly with Apache's
  <literal>&lt;Files></literal>,
  <literal>&lt;Location></literal> and <literal>&lt;Directory></literal>
  directives. All AxKit's configuration takes this
  approach, so you never have to teach a webmaster any new tricks to build
  up your XML site.
  </para>
  
  </sect1>
  
  <sect1>
  <title>Putting it all together</title>
  <para>
  In simple terms, how does AxKit work? AxKit registers with Apache a "handler". In
  Apache terms this is a module that works in a particular part of the request
  phase (which cover things like Authentication, Type checking, Response,
  and Logging). When a request for a file comes in, AxKit does some very
  quick checking to see if the file is XML. The main checks performed are
  to see if the file extension is <filename>.xml</filename>, and/or to check the first few
  characters of the file for the <literal>&lt;?xml?></literal> declaration. If the file is
  not XML, AxKit lets Apache deliver the file as it would normally. Note
  that using Apache's configuration methods described above, it's quite
  possible to apply this only to certain parts of your web site.
  </para>
  <para>
  When an XML file is detected, the next step is to call any plugin
  modules that determine the media type and/or stylesheet preference.
  Media type chooser plugins normally look at the User-Agent header, or
  at the Accept header, however its possible to use any method at
  all to determine the media type. Stylesheet choosers exist currently
  based on Path Info (this is a path following the filename, so you could
  request <filename>myfile.xml/mystyle</filename>), querystring (for example
  <filename>myfile.xml?style=mystyle</filename>), and file suffix
  (<filename>myfile.xml.mystyle</filename>).
  </para>
  <para>
  The final part, and the most significant part, is the plumbing together
  of all the stylesheets with the XML file in the right order,
  implementing cascading where appropriate, and also to "do the right
  thing" with regards to the cache. One "leg-up" we have on Cocoon here is
  that AxKit invalidates the cache when external entities (parsed or
  unparsed) change too. This allows modular stylesheets to change only
  part of their make-up and ensure that changes to these sub-components
  cause a re-build of the cache.
  </para>
  <sect2>
  <title>Mapping XML Files to Stylesheets</title>
  <para>
  AxKit uses two separate methods for mapping XML files to stylesheets.
  The primary method is to use the W3C recommendation at <ulink
  url="http://www.w3.org/TR/xml-stylesheet">http://www.w3.org/TR/xml-stylesheet</ulink>.
  This specifies that a <literal>&lt;?xml-stylesheet?></literal> processing instruction at
  the beginning of the xml file (after the <literal>&lt;?xml?></literal> declaration, and
  before the first element) defines the location and type of the
  stylesheet. The actual details of how all this works are defined in
  <ulink url="http://www.w3.org/TR/REC-html40">TR/REC-html40</ulink>
  (which has just recently been superceded by html 4.01). The second
  method of mapping XML files to stylesheets is used when no usable
  <literal>&lt;?xml-stylesheet?></literal> directives are found in the XML
  file. This uses a <option>DefaultStyleMap</option>
  option in your Apache configuration files. These directives can be used
  anywhere within Apache's <literal>&lt;Files></literal>,
  <literal>&lt;Location></literal>, <literal>&lt;Directory></literal> and
  <filename>.htaccess</filename> configuration system. In this way it's possible to define
  complex mapping rules for different file types and locations in
  whichever manner pleases you.
  </para>
  <para>
  AxKit then uses the type of the stylesheet (in the
  <literal>type="..."</literal> attribute
  of the <literal>&lt;?xml-stylesheet?></literal> directive, or the first parameter of the
  <option>AxAddDefaultStyleMap</option> option) to decide on a module to use to process that
  type of file. Again this is slightly different to
  <application>Cocoon 1.x</application>, which requires
  special <literal>&lt;?cocoon?></literal> directives to be added to your XML files to
  determine the processor module to use. The type is then mapped to a
  module using another Apache configuration option:
  <option>AxAddStyleMap</option>. Again,
  this directive can appear anywhere within Apache's configuration
  structure. This allows you to try different modules for your processing
  of the same file (for example, you might like to try both XSLT
  processors to see which suits your needs best).
  </para>
  </sect2>
  <sect2>
  <title>Choosing a Stylesheet</title>
  <para>
  In the course of examining the options of which stylesheets to choose,
  often a single XML file (or a <option>DefaultStyleMap</option> - see above) can provide
  more than one option. There are two important parts of this to consider.
  The first is choosing from multiple stylesheets based on media type, and
  stylesheet preference. The Media type of a stylesheet must always match the
  requested media type, or be of media type <literal>"all"</literal>, however it's worth
  noting here that <application>Cocoon</application> provides many alternative media types to the
  W3C's specification list, such as "wap", "lynx", "explorer" and
  "netscape". The merits of this are debatable. The stylesheet preference
  is based on 3 types of stylesheet: A persistant stylesheet, a preferred
  stylesheet and an alternate stylesheet. Persistant stylesheets
  declarations contain no <literal>title="..."</literal> attribute, preferred stylesheets
  contain a <literal>title="..."</literal> attribute, but have
  <literal>alternate="no"</literal> (or no
  <literal>alternate="..."</literal> attribute), and alternate stylesheets contain a
  <literal>title="..."</literal> attribute and have explicitly set
  <literal>alternate="yes"</literal>.
  </para>
  <para>
  AxKit always applies persistant stylesheets, and will apply alternate
  stylesheets only if a plugin has determined that one should be
  displayed, otherwise the preferred stylesheets are used. This all seems
  rather confusing and long winded, but it allows a very modular system,
  and also allows for wonderful flexibility in choosing stylesheets for
  users. For example, a plugin could connect to a database and retrieve
  the correct alternate stylesheet for a particular user based on an
  authentication token. This would allow users to change the whole look of
  their favourite web site, and AxKit will do all the hard work for you.
  </para>
  </sect2>
  
  <sect2>
  <title>Cascading Stylesheets</title>
  <para>
  It's easy to get confused by the term "stylesheet" here. A quick read of
  this might make it seem like all they are good for is transforming
  static XML files into further static XML files. This is especially the
  case if all you can picture is XSL(T) (or even CSS). However stylesheets
  in AxKit's terms can do anything, provided you can build a Language
  module to parse it. The concept of stylesheets in AxKit replace all the
  stages in Cocoon: Producer, Processor and Formatter. So it becomes
  possible to, just as in Cocoon, return database results, format add
  tags, and format the result to WAP, HTML or any possible format.
  </para>
  <para>
  The term cascading here therefore refers to the case of one stylesheet's
  results "cascading" into the next. With AxKit there are a number of ways
  to achieve that. The first and simplest method is to have all your
  stylesheets based on DOM, and produce DOM trees. When all the
  stylesheets have finished processing, AxKit takes care to dispose of
  your DOM tree and output the results to the user agent.
  </para>
  <para>
  The second method of cascading is to simply cascade the textual results
  of your output. This is necessary with modules like Sablotron where
  there is no DOM tree available. Modules further down the processing
  stream are able to parse this string directly (provided they are
  designed to work this way) as XML, and continue processing.
  </para>
  <para>
  The final, and possibly most interesting method, is to use "end-to-end
  SAX". This is where AxKit sets up a chain of SAX handlers to process the
  document with. AxKit stylesheet languages based on SAX are responsible
  for simply sending on SAX events to the next SAX handler up the chain
  (they are provided a SAX handler to pass events to on construction). The
  final SAX handler in the chain simply outputs its results to the
  browser. This doesn't sound particularly interesting, until you consider
  that this end-to-end system starts outputting data to the browser
  immediately as soon as parsing begins. This system allows database
  modules to not build DOM trees in memory, which can be resource
  consuming, but to simply fire SAX events, and the output from the
  database will appear as results are available. Cocoon 2 will have a
  system similar, if not identical, to this.
  </para>
  </sect2>
  
  </sect1>
  
  <sect1>
  <title>A Simple Setup Example</title>
  <para>
  Setting up AxKit is simple. I don't believe in tools like this being
  hard to use or even hard to setup. Provided you can use an editor and
  modify a few Apache configuration files, setup should be a breeze.
  </para>
  <para>
  Unfortunately AxKit requires mod_perl, so there is an extra component to
  install first. Installation of mod_perl can be complex, depending upon
  your setup. To that end I will just provide a link: <ulink
  url="http://perl.apache.org/guide/install.html">The mod_perl Guide -
  Installation</ulink>.
  </para>
  <para>
  Now onto AxKit itself. First, installing the required perl modules is very simple. Download
  AxKit (see link below), extract the archive and change to the directory created. Then
  simply type:
  <informalexample>
  <screen>
  &prompt;<userinput>perl Makefile.PL</userinput>
  &prompt;<userinput>make</userinput>
  &prompt;<userinput>make test</userinput>
  &prompt;<userinput>make install</userinput>
  </screen>
  </informalexample>
  If you don't have apxs in your path, mod_perl versions below 1.24 will
  produce a warning at the first step. This warning can be ignored.
  </para>
  <para>
  Next up, editing Apache's configuration files. First you need to enable
  AxKit so that Apache understands AxKit's configuration directives, so
  add the following line to your httpd.conf file:
  <informalexample>
  <screen>
  PerlModule AxKit
  </screen>
  </informalexample>
  Finally, you can add in the core of AxKit - handler itself. This
  can be added to any .htaccess file, or to your httpd.conf file:
  <informalexample>
  <screen>
  SetHandler perl-script
  PerlHandler AxKit
  
  AxAddStyleMap text/xsl Apache::AxKit::Language::Sablot
  </screen>
  </informalexample>
  The last line there associates the type "text/xsl" with the stylesheet
  module specified.
  </para>
  <para>
  Now you're ready to start serving up XML files. Check out the example
  files in the AxKit distribution, these should get you started.
  </para>
  </sect1>
  
  <sect1>
  <title>Conclusions</title>
  <para>
  AxKit provides web developers with the tools they need to deliver
  complex systems quickly, and eases them into the development process. It
  gives them the power to develop their own system for stylesheet decision
  making and also the flexibility to design completely new stylesheet
  languages. All of this while integrating tightly with Apache, providing
  a fast, scalable and well architectured system. But then, I'm biased.
  </para>
  <para>
  AxKit is not finished yet, however the majority of the features
  described above are built and working very reliably. The most
  significant things missing from AxKit are SAX based stylesheet
  languages (which just need to be designed and built - which I have a
  number of ideas for), and alternate ways to generate the initial XML
  file (which cocoon calls "Producers"). These will be coming in a future
  release. Being free software I hope people will jump in and help. We
  have the beginnings of an active mailing list, where you can vote on
  features, or help develop them, or simply lurk. We're moving extremely
  quickly with the features. Developing in Perl allows us to do this,
  while still maintaining readable code (something I deem very important
  - so don't assume because it's written in Perl that it's going to be a
  ball of spaghetti!). If there's something you'd like to see in AxKit,
  please join the mailing list and participate with us.
  </para>
  </sect1>
  
  <sect1>
  <title>Links</title>
  <para>
  The following are links relevant to this article:
  <itemizedlist>
  	<listitem><ulink url="http://axkit.org/">AxKit</ulink> - The
  	main homepage for AxKit.</listitem>
  </itemizedlist>
  </para>
  </sect1>
  
  </article>
  
  
  
  1.1                  xml-axkit/axkit.org/docs/provider-howto.dkb
  
  Index: provider-howto.dkb
  ===================================================================
  <?xml version="1.0" standalone="no"?>
  <?xml-stylesheet href="/stylesheets/docbook_screen.xps" 
  				type="application/x-xpathscript" 
  				media="screen"
  				title="default" ?>
  <?xml-stylesheet href="/stylesheets/docbook_screen_fancy.xps" 
  				type="application/x-xpathscript" 
  				media="screen"
  				title="fancy"
  				alternate="yes" ?>
  <?xml-stylesheet href="/stylesheets/docbook_print.xps" 
  				type="application/x-xpathscript" 
  				media="screen"
  				title="print"
  				alternate="yes" ?>
  <?xml-stylesheet href="/stylesheets/docbook_handheld.xps" 
  				type="application/x-xpathscript" 
  				media="handheld"
  				title="default"?>
  <article label="ProviderHOWTO">
  <artheader>
  	<title>AxKit Provider HOWTO</title>
  	
  	<author>
  		<firstname>Riccardo</firstname>
  		<surname>Cambiassi</surname>
  		<email>brujah@infodrome.net</email>
  	</author>	
  	<abstract>
  		<para>A quite complete HOWTO on writing Providers for AxKit</para>
  	</abstract>
  </artheader>
  <sect1>
  <title>Introduction</title>
  <para>
  AxKit is an XML Application Server for Apache. It provides on-the-fly conversion from XML to any format, such as HTML, WAP or text using either W3C standard techniques, or flexible custom code. AxKit also uses a built-in Perl interpreter to provide some amazingly powerful techniques for XML transformation.  For more information on AxKit see <ulink url="http://www.axkit.org">www.axkit.org</ulink>.
  </para>
  <para>
  One of the most interesting features of AxKit is it's object architecture: it makes easy (and quite fun) to extend it to meet your own needs.
  From the AxKit manpage we learn that we can operate on three aspects of AxKit behaviour:
  <itemizedlist>
  <listitem>
  <emphasis>AxConfigReader</emphasis> returns information about various configuration options.
  </listitem>
  <listitem>
  <emphasis>AxProvider</emphasis> is the means by wich AxKit gets its resources from. 
  </listitem>
  <listitem>
  <emphasis>AxCacheModule</emphasis> is responsible for storing cache data for later retrieval.
  
  </listitem>
  </itemizedlist>
  The special feature that we'll exploit in this document is the extension of the <emphasis>Provider</emphasis> module.
  </para>
  </sect1>
  <sect1>
  <title>Overview of AxKit Providers</title>
  <para>All Providers descends from <application>Apache::AxKit::Provider</application> Module.
  From its manpage (and a bit of hacking) we learn that it relies upon the following (main) methods:
  <itemizedlist>
  <listitem>
  <application>new()</application>
  - creates a new Provider object; normally this method should be defined only in the parent class (<application>Apache::AxKit::Provider</application>). To handle custom initialization, look at <application>init()</application> below.
  </listitem>
  <listitem>
  <application>init()</application>
   - intialize the provider; it is called by <application>new()</application>. Here the Provider module has the chance to carry out custom initialization procedures. The standard behaviour is to do nothing.
  <note>The <application>init()</application> method should accept argument as follows: the first is always the request object; then we have a list of key => value pairs 
  containing either 'uri' or 'file' key for the desired resource id and, in case of an external entity, 'rel' containing the Provider object for the main document. [is this correct ???].</note>
  </listitem>
  <listitem>
  <application>process() *</application>
  - answers the question "Shall we process this request?". Should return 1 if it can process the resource or die it it cannot, eventually <emphasis>throwing</emphasis> an opportune exception (see below).
  </listitem>
  <listitem>
  <application>exists() *</application>
   - answers the question: "Does the resource exist?". Return 1 if it exists.
  </listitem>
  <listitem>
  <application>mtime() *</application>
  - answers the question "How old is this resource?". Return the modification time in days before the current time. It's used to test the validity of cached data.
  </listitem>
  <listitem>
  <application>get_fh() *</application>
   - returns an open filehandle for the resource (or die if that's not possible).
  </listitem>
  <listitem>
  <application>get_strref() *</application>
   - returns a reference to a scalar containing the resource; note that at least one of get_fh or get_strref must work.
  </listitem>
  <listitem>
  <application>key() *</application>
   - returns an unique identifier for this resource.
  </listitem>
  <listitem>
  <application>get_styles()</application>
   - extract stylesheets and external entities from the resource.
  </listitem>
  <listitem>
  <application>get_ext_ent_handler() </application>
  - return a reference to be used instead of XML::Parser's default external entity handler.
  </listitem>
  </itemizedlist>
  <note>All methods marked with * are not defined in <application>Apache::AxKit::Provider</application>, so each real Provider will have to implement their own.</note>
  </para>
  <sect2>
  <title>How do Providers work?</title>
  <para>Throughout the processing of a request, whenever AxKit needs to fetch a resource, it creates an <application>Apache::AxKit::Provider</application> object for the desired resource.
  This is a generic (highlevel) object whose job is not limited to define standard methods (see above) but also to verify which actual Provider is in charge and <application>reconsecrate()</application> to it.
  To change the default Provider you can use the <emphasis>AxProvider</emphasis> directive or simply set the variable with <emphasis>PerlSetVar</emphasis> directive from your httpd.conf:
  <programlisting>
  # either
  AxProvider Apache::AxKit::Provider::File
  # or
  PerlSetVar AxProvider Apache::AxKit::Provider::File
  </programlisting>
  </para>
  
  <para>
  A new Provider is created in order to get:
  <itemizedlist>
  <listitem>The XML document</listitem>
  <listitem>Every stylesheets</listitem>
  <listitem>Every external entity</listitem>
  </itemizedlist>
  </para>
  
  <example>
  <para>For example, let's suppose we have requested the following document at the url http://localhost/sample.xml</para>
  <programlisting>
  &lt;?xml version="1.0" ?&gt;
    &lt;xml-stylesheet href="/sample.xsl" type="text/xsl"?&gt;
    &lt;page&gt;
      &lt;para&gt;Hello World!&lt;/para&gt;
    &lt;/page&gt;
  </programlisting>
  From a quite high level point of view, AxKit will:
  <orderedlist>
  <listitem>Create a Provider for this resource [my $provider = Apache::AxKit::Provider->new($r)]</listitem>
  <listitem>Check if to proceed in processing this resource [if ($provider->process())]</listitem>
  <listitem>Get the resource [$provider->get_fh or $provider->get_strref()]</listitem>
  <listitem>Parse the XML to extract all stylesheet and external entities and, for each resource:</listitem>
  <orderedlist>
  <listitem>Create a Provider for this resource, specifying the 'uri' in case of a stylesheet or  either 'file' or 'uri' in case of an external entity.
  In our example we have just one stylesheet ('/sample.xsl') and no external entities.</listitem>
  <listitem>Check if we can process this resource</listitem>
  <listitem>Get the resource</listitem>
  </orderedlist>
  <listitem>Once AxKit has got all resources it will use Language Processors to apply the stylesheet to the document, and then delivery the result to the browser.</listitem>
  </orderedlist>
  </example>
  
  </sect2>
  
  </sect1>
  <sect1>
  <title>Standard Providers</title>
  <para>What follows is a list of the Providers that come with the standard distribution of AxKit.</para>
  <sect2>
  <title>File</title>
  <para>That's the default. It gets input from files (surprise!) relative to Apache's DocumentRoot. This is also the most complete Provider in that it defines all features and IMO the best starting place for everyone who wants to develop a new Provider.
  </para>
  <para> 
  It defines:
  <itemizedlist>
  <listitem> get_fh()</listitem>
  <listitem> get_strref()</listitem> 
  <listitem>key()</listitem>
  <listitem>exists()</listitem>
  <listitem>process()</listitem>
  <listitem>mtime()</listitem>
  </itemizedlist>
  and redefines:
  <itemizedlist>
  <listitem>init().</listitem>
  </itemizedlist>
  </para>
  </sect2>
  <sect2>
  <title>Scalar</title>
  <para>This is a basic provider, gets input from a scalar variable.
  AxKit uses this Provider in order to handle Error messages.</para>
  <para>
  It defines all standard methods:
  <itemizedlist>
  <listitem>process()</listitem>
  <listitem>exists()</listitem>
  <listitem>mtime()</listitem>
  <listitem>get_fh</listitem>
  <listitem>get_strref()</listitem>
  <listitem>key()</listitem>
  </itemizedlist>
  
   and redefines:
  <itemizedlist>
  <listitem>new()</listitem>
  <listitem>init()</listitem>
  <listitem>apache_request</listitem>
  <listitem>get_styles()</listitem>
  </itemizedlist>
  </para>
  </sect2>
  <sect2>
  <title>Filter</title>
  <para>The most exotic Provider and, quite surprisingly, the most simple: it works with <application>Apache::Filter</application> in order to get data from another <emphasis>PerlHandler</emphasis>. This requires other Handlers to be "Filter aware". By the time of this writing, this applies to:
  <itemizedlist>
  <listitem>Apache::Registry</listitem>
  <listitem>Apache::SSI</listitem>
  <listitem>Apache::ASP</listitem>
  <listitem>HTML::Mason</listitem>
  <listitem>Apache::SimpleReplace</listitem>
  </itemizedlist>
  The Filter Provider is derived from File Provider and redefines just:
  <itemizedlist>
  <listitem>init()</listitem>
  <listitem>get_fh()</listitem>
  <listitem>get_strref()</listitem>
  <listitem>mtime()</listitem>
  </itemizedlist>
  </para>
  </sect2>
  </sect1>
  <sect1>
  <title>Using Filter Provider</title>
  <para>Here we'll discuss how to use Filter Provider in order to exploit Apache::Filter</para>
  <sect2>
  <title>Apache::Filter</title>
  <para>To enable the Filter chain, you will have to operate on both the Apache configuration and the single handlers  (here called Filters). The following piece of code is borrowed from Apache::Filter manpage:</para>
  <programlisting>
  #### In httpd.conf:
           PerlModule Apache::Filter
           # That's it - this isn't a handler.
  
           &lt;Files ~ "*\.blah"&gt;
            SetHandler perl-script
            PerlSetVar Filter On
            PerlHandler Filter1 Filter2 Filter3
           &lt;/Files&gt;
  
  #### In Filter1, Filter2, and Filter3:
           $r = $r->filter_register();  # Required
           my $fh = $r->filter_input(); # Optional (you might not need the input FH)
           while (&lt;$fh&gt;) {
             s/ something / something else /;
             print;
           }
  </programlisting>
  <para>As we noticed before, currently the following public modules are Filter-aware.</para>
  <itemizedlist>
  <listitem>
          Apache::Registry (using Apache::RegistryFilter, included with Apache::Filter)
  </listitem>
  <listitem>
          Apache::SSI
  </listitem>
  <listitem>
          Apache::ASP
  </listitem>
  <listitem>
          HTML::Mason
  </listitem>
  <listitem>
          Apache::SimpleReplace
  </listitem>
  </itemizedlist>
  </sect2>
  <sect2>
  <title>... with simple CGIs</title>
  <para>How to use Apache::RegistryFilter and AxKit. This is pretty simple: just add in httpd.conf:</para>
  <programlisting>
  PerlModule Apache::RegistryFilter
  PerlModule AxKit
  
  &lt;Location /filter&gt;
   SetHandler perl-script
   PerlSetVar Filter On
   AxProvider Apache::AxKit::Provider::Filter
   PerlHandler Apache::RegistryFilter AxKit
  &lt;/Location&gt;
  </programlisting>
  <para>Then write a CGI that will generate your xml document and put it in the $DocumentRoot/filter/ directory. What follows is a minimalistic example:</para>
  <programlisting>
  #!/usr/bin/perl
  
  print &lt;&lt;EOT;
  &lt;?xml version="1.0"?&gt;
  &lt;?xml-stylesheet href="plain.xsl"
      type="text/xsl"?&gt;
  &lt;html&gt;
    &lt;head&gt;
    &lt;title&gt;AxKit/Simple CGI filter test&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
    &lt;table bgcolor="#FFFFFF"&gt;
  EOT
  
  print map { "&lt;tr&gt;&lt;td&gt;$_&lt;/td&gt;&lt;td&gt;$ENV{$_}&lt;/td&gt;&lt;/tr&gt;" } keys %ENV;
  
  print &lt;&lt;EOT;
  &lt;/table&gt;
  &lt;/body&gt;
  &lt;/html&gt;
  EOT
  </programlisting>
  <para>Finally, you will need a stylesheet to process the xml generated by the CGI. Here's an example:</para>
  <programlisting>
  &lt;?xml version="1.0" ?&gt;
  &lt;xsl:stylesheet
  	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  	version="1.0"
  &gt;
  &lt;xsl:output
  	method="html"
  	indent="yes"
  	encoding="ISO-8859-1"
  /&gt;
  &lt;xsl:template match="/"&gt;
  	&lt;xsl:apply-templates select="/html/*"/&gt;
  &lt;/xsl:template&gt;
  
  &lt;xsl:template match="*"&gt;
  	&lt;xsl:copy&gt;
  		&lt;xsl:copy-of select="./@*" /&gt;
  		&lt;xsl:apply-templates /&gt;
  	&lt;/xsl:copy&gt;
  &lt;/xsl:template&gt;
  &lt;/xsl:stylesheet&gt;
  </programlisting>
  </sect2>
  <sect2>
  <title>... with Apache::ASP</title>
  <para><emphasis>Apache::ASP</emphasis> provides an Active Server Pages port to the Apache Web Server with Perl as the host scripting language. For more information about Apache::ASP, see <ulink url="http://www.apache-asp.org">Apache ASP homepage</ulink>.
  </para>
  <para>To enable it to work with the AxKit Filter Provider, just add the following in httpd.conf:</para>
  
  <programlisting>
  PerlModule Apache::Filter
  PerlModule Apache::ASP
  PerlModule AxKit
  
  &lt;Location /filter&gt;
   SetHandler perl-script
   PerlSetVar Filter On
   AxProvider Apache::AxKit::Provider::Filter
   PerlHandler Apache::ASP AxKit
  &lt;/Location&gt;
  </programlisting>
  <para>Then create a sample ASP page to generate the xml. Here is a minimalistic example:</para> 
  <programlisting>
  &lt;?xml version="1.0"?&gt;
  &lt;?xml-stylesheet href="plain.xsl"
  	type="text/xsl"?&gt;
  &lt;html&gt;
  	&lt;head&gt;
  	&lt;title&gt;AxKit / Apache::ASP filter test&lt;/title&gt;
  	&lt;/head&gt;
  &lt;body bgcolor="#000000" text="#CCCCCC"&gt;
  	&lt;h3&gt;Environment Variables:&lt;/h3&gt;
  	&lt;table border="1" width="100%" cellspacing="0" cellpadding="0"&gt;
  &lt;%
  	my $env = $Request->ServerVariables;
                  $Response->Write ( map {  "&lt;tr&gt;&lt;th&gt;$_&lt;/th&gt;&lt;td&gt;$env->{$_}&lt;/td&gt;&lt;/tr&gt;" } keys %$env );
  
  %&gt;
  &lt;/table&gt;
  &lt;/body&gt;  
  &lt;/html&gt;
  </programlisting>
  </sect2>
  <!--
  <sect2>
  <title>... with HTML::Mason</title>
  <para>Mason is a tool for building, serving and managing large websites. For more information about Mason, take a look at <ulink url="http://www.masonhq.com">Mason HeadQuarters</ulink>.</para>
  </sect2>
  -->
  </sect1>
  <sect1>
  <title>Provider Internals</title>
  <para>Some advanced tips about AxKit. Everything you'll find in this chapter is to be considered just an overview on the subject and is included here just to let the reader better understand some of the main topics covered in this paper.</para>
  <!--
  <sect2><title>Use of Apache notes ($r->p?notes)</title>
  <para>headers_sent, passthru and so on...</para>
  </sect2>
  -->
  <sect2>
  <title>AxKit::Apache object</title>
  <para>AxKit redefines the request object through the <emphasis>AxKit::Apache</emphasis> package.</para>
  <para>This (re)defines the following methods:
  <itemizedlist>
  <listitem>content-type</listitem>
  <listitem>print</listitem>
  <listitem>no_cache</listitem>
  <listitem>send_http_header</listitem>
  </itemizedlist>
  </para>
  <para>The changes in the methods have mostly to do with cache handling and shouldn't be of much interest to you. Just note that with the <emphasis>no_cache</emphasis> method you can disable AxKit's own cache too.</para>
  </sect2>
  <sect2>
  <title>External Entity Handler</title>
  <para>As we stated before, it is possible to define a custom External Entity Handler in the Provider module. This 
  happens through the <emphasis>get_ext_ent_handler()</emphasis> routine. The default behaviour is to fetch remote <emphasis>http:</emphasis> entities with HTTP::GHTTP and local (unknown or no scheme) ones with the current AxKit Provider.</para>
  </sect2>
  <sect2>
  <title>Apache::AxKit::Exception</title>
  <para>AxKit uses a subclass of <emphasis>Error</emphasis> to handle Exceptions. This implements the try / catch / otherwise / finally primitives. You can use them to handle (or recover from) errors in a clean way. </para>
  <para>The Apache::AxKit::Exception package defines the following types of exception:
  <itemizedlist>
  <listitem>Apache::AxKit::Exception</listitem>
  <listitem>Apache::AxKit::Exception::Declined</listitem>
  <listitem>Apache::AxKit::Exception::Error</listitem>
  <listitem>Apache::AxKit::Exception::OK</listitem>
  <listitem>Apache::AxKit::Exception::Retval</listitem>
  <listitem>Apache::AxKit::Exception::IO</listitem>
  </itemizedlist>
  </para>
  </sect2>
  </sect1>
  <sect1>
  <title>Writing a simple provider: DBI</title>
  <para>Here's a complete example of an <application>AxKit Provider</application> from scratch.</para>
  <para>This is called <emphasis>Apache::AxKit::Provider::DBI</emphasis> and is designed to get its data from a DataBase.
  In my example, I used as data source a MySQL installed on localhost, with the 'test' db and a 'guest' user with password 'guest'. Here is the table definition and default values:
  </para>
  <programlisting>
  
  # Table definition
  CREATE TABLE blocks (
     id varchar(255) DEFAULT '' NOT NULL,
     block TEXT,
     PRIMARY KEY (id)
  );
  
  # Sample code
  INSERT INTO blocks VALUES ('/index.xml', 
     '&lt;?xml version="1.0"?&gt;
       &lt;page&gt;
  	&lt;title&gt;Hello world&lt;/title&gt;
       &lt;/page&gt;');
  
  INSERT INTO blocks VALUES ('/index.xsl', 
  '&lt;?xml version="1.0" ?&gt;
  &lt;xsl:stylesheet
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      version="1.0"
  &gt;
  
  &lt;xsl:template match="/"&gt;
     &lt;xsl:apply-templates /&gt;
  &lt;/xsl:template&gt;
  
  &lt;xsl:template match="title"&gt;
     title = &lt;xsl:value-of select="."/&gt;
  &lt;/xsl:template&gt;
  &lt;/xsl:stylesheet&gt;');
  
  </programlisting>
  
  What follows is the code for the provider: 
  <programlisting>
  #TODO: 
  # - insert some decent comments
  # - write down a sample external entity handler
  # - explain better the get_mtime() thing.
  
  package Apache::AxKit::Provider::DBI;
  use strict;
  use vars qw/@ISA/;
  @ISA = ('Apache::AxKit::Provider');
  
  use Apache;
  use Apache::Log;
  use Apache::AxKit::Exception;
  use Apache::AxKit::Provider;
  use Apache::AxKit::Provider::File;
  use Apache::MimeXML;
  use Apache::Constants;
  
  use DBI;
  
  # sub: Init
  # Here we do some initialization stuff. 
  sub init {
      my $self = shift;
      my (%p) = @_;
      
      if ($p{uri}) {
        # called from :
        # process_request ($styleprovider = Apache::AxKit::Provider->new)
        # check_resource_mtimes ($ent_provider = ... )
        # [...]
        $self->{id} = $p{uri};
      }
      elsif ($p{file}) {
        
        $self->{id} = $p{file};
      }
      else {
        $self->{id} = $self->{apache}->filename();
      }
  }
  
  # sub: get_fh
  # we don't want to handle files, so we just throw an exception here.
  sub get_fh {
      throw Apache::AxKit::Exception::IO( 
              -text => "Can't get fh for DBI filehandle"
              );
  }
  
  # sub: get_strref
  # since we refused to work with file handles, we HAVE to define this.
  sub get_strref {
    my $self = shift;
  
    # Connect to the DB and query it.
    my $dbh = DBI->connect("dbi:mysql:test",'guest', 'guest');
    my $sth = $dbh->prepare("SELECT block FROM blocks WHERE id='".$self->{id}."'");
    $sth->execute;
  
    # Now get the data and disconnect from the DB
    my $res = $sth->fetchrow();
    $dbh->disconnect;
    return \$res;
    
  }
  
  # sub: mtime
  # This should return the modification time of the resource, for simplicity here we decrement it everytime we are called
  # so that resources are never considere cacheable.
  
  use vars qw/$mtime/;
  
  $mtime = 0;
  
  sub mtime {
    my $self=shift;
    return --$time; #borrowed from Scalar Provider
  }
  
  # sub: process
  
  sub process {
    my $self = shift;
    # For simplicity, let's assume our DB entry always exists
    return 1; 
  } 
  
  # sub: key
  # should return a unique identifier for the resource.
  # Let's assume the id from the uri is a good one.
  
  sub key {
    my $self = shift;
    return $self->{id};
  }
  
  # sub: exists
  # should return 1 only if the resource actually exists. Let's cheat for now.
  sub exists {
    my $self = shift;
    return 1;
  }
  
  1;
  
  
  </programlisting>
  
  to enable it, just modify your httpd.conf as follows:
  <programlisting>
  
  &lt;Location /&gt;
  PerlHandler AxKit
  AxProvider Apache::AxKit::Provider::DBI
  &lt;/Location &gt;
  </programlisting> 
  </sect1>
  
  
  </article>
  
  
  
  1.1                  xml-axkit/axkit.org/docs/quick_start.dkb
  
  Index: quick_start.dkb
  ===================================================================
  <?xml version="1.0"?>
  <?xml-stylesheet href="/stylesheets/docbook_screen.xps"
          type="application/x-xpathscript"
          media="screen"
          title="default"
  ?>
  <?xml-stylesheet href="/stylesheets/docbook_print.xps"
          type="application/x-xpathscript"
          title="print"
          alternate="yes"
  ?>
  <!DOCTYPE article [
  <!ENTITY prompt "&#x25; ">
  ]>
  <article>
  <artheader>
    <title>AxKit Quickstart Guide</title>
    <author>
      <firstname>Kip</firstname>
      <surname>Hampton</surname>
      <affiliation>
        <address><email>khampton@totalcinema.com</email></address>
      </affiliation>
    </author>
    <abstract>
      <para>
      This document is a <emphasis>quick start</emphasis> guide designed to help developers
      deliver transformed XML with AxKit as quickly as possible. As such, many of the
      finer points of AxKit's extremely robust interface will not be covered. For a more
      descriptive introduction, please see Matt's <ulink url=
      "http://axkit.org/docs/introduction.dkb">Introduction to AxKit</ulink>.
      </para>
      <para>
  
      </para>
    </abstract>
  </artheader>
  <sect1>
  <title>Installing and Configuring AxKit</title>
  <para>
  This guide presumes that you already have an
  <ulink url ="http://www.apache.org/httpd.html">Apache</ulink> httpd server running
  <ulink url="http://perl.apache.org/">mod_perl</ulink>. If this is not
  the case, please visit the <ulink url
  ="http://www.apache.org/httpd.html">Apache
  </ulink> and <ulink url="http://perl.apache.org/">mod_perl</ulink> pages for
  more information.
  </para>
  <sect2>
  <title>Step 1 - Download and Install AxKit</title>
  <para>
  First, visit the AxKit <ulink url="http://www.axkit.org/download/">
  download directory</ulink> and grab the latest tarball. Once you've
  downloaded the source, type the following:
  <informalexample>
  <programlisting>
  &prompt; <userinput>tar -zxvf AxKit-x.xx.tar.gz</userinput>
  &prompt; <userinput>cd AxKit-x.xx/</userinput>
  &prompt; <userinput>perl Makefile.PL</userinput>
  &prompt; <userinput>make</userinput>
  &prompt; <userinput>make test</userinput>
  &prompt; <userinput>make install</userinput>
  </programlisting>
  </informalexample>
  If <literal>perl Makefile.PL</literal> warns about missing modules, make a note of these
  dependencies and install the corresponding Perl packages before proceeding with the AxKit
  installation. AxKit will not be properly installed, otherwise.
  </para>
  </sect2>
  <sect2>
  <title>Step 2 - Edit Your httpd.conf File</title>
  <para>
  Add the following lines to your httpd.conf:
  <informalexample>
  <programlisting>
  PerlModule AxKit
  SetHandler perl-script
  PerlHandler AxKit
  AxAddStyleMap application/x-xpathscript Apache::AxKit::Language::XPathScript
  
  # add the following only if you intend to install Sablotron
  AxAddStyleMap text/xsl Apache::AxKit::Language::Sablot
  </programlisting>
  </informalexample>
  </para>
  <para>
  Don't worry that this looks like AxKit will deliver all of your files,
  if it doesn't detect XML at the URL you give it, it will let the httpd
  deliver it as normal. If you're still concerned, put <emphasis>all but the first</emphasis>
  configuration directive in a <ulink
  url="http://www.apache.org/docs/mod/core.html#location"
  ><literal>&lt;Location></literal></ulink> section. Note that the first
  line: <literal>PerlModule AxKit</literal> <emphasis>MUST</emphasis>
  occur in your httpd.conf so that it is parsed in the parent httpd
  process. This is because it adds configuration directives to the entire
  Apache httpd. If you do not do this you will get SEGFAULTs.
  </para>
  <para>
  Now, stop and restart your Apache server and assuming you've had no trouble
  with the steps above, you are now ready to begin publishing transformed XML
  with AxKit!
  </para>
  </sect2>
  </sect1>
  <sect1>
  <title>Installing the Sablotron XSLT Processor (Optional)</title>
  <para>
  This section assumes that you will be installing the Sablotron XSLT processor along with
  AxKit. If you want to see AxKit in action but do not want to install Sablotron you may safely
  skip to the <ulink url="quick_start.dkb?section=3">next section</ulink>.
  </para>
  <sect2>
  <title>Step 1 - Download and Install the Sablotron XSLT
  Processor</title>
  <para>
  Next, fetch either the source or appropriate binary distribution of the
  Sablotron XSLT Processor from <ulink url="http://www.gingerall.com/">
  www.gingerall.com</ulink>. As of version 0.50, Sablotron requires the shared library version
  of the Expat XML parser to be installed before installing Sablotron. Visit <ulink url=
  "http://sourceforge.net/projects/expat/">the Expat project page</ulink> for sources and
  installation instructions.
  </para>
  <para>
  Installing Sablotron from the source:
  <informalexample>
  <programlisting>
  &prompt; <userinput>tar -zxvf Sablot-x.xx.tar.gz</userinput>
  &prompt; <userinput>cd Sablot-x.xx/</userinput>
  &prompt; <userinput>./configure</userinput>
  &prompt; <userinput>make</userinput>
  &prompt; <userinput>make install</userinput>
  </programlisting>
  </informalexample>
  Please note that Sablotron build process described here covers versions 0.50. Consult the
  INSTALL and README files that shipped with the version you downloaded for details
  and possible changes.
  </para>
  </sect2>
  <sect2>
  <title>Step 2 - Install the XML::Sablotron Perl Library</title>
  <para>
  Next, install XML::Sablotron. You can either install the package by hand or use the CPAN
  shell. To install using the CPAN shell type:
  <informalexample>
  <programlisting>
  &prompt; <userinput>perl -MCPAN -e shell</userinput>
  &prompt; <userinput>install XML::Sablotron</userinput>
  </programlisting>
  </informalexample>
  To install the packages yourself, get the latest version of the XML::Sablotron Perl package
  from <ulink url="http://www.gingerall.com/">www.gingerall.com</ulink>, cd to the
  directory you downloaded the tarball to and type:
  <informalexample>
  <programlisting>
  &prompt; <userinput>tar -zxvf XML-Sablot-x.xx.tar.gz</userinput>
  &prompt; <userinput>mv XML-Sablot-x.xx/ Sablot-x.xx/</userinput>
  &prompt; <userinput>cd Sablot-x.xx/XML-Sablot-x.xx/</userinput>
  &prompt; <userinput>perl Makefile.PL</userinput>
  &prompt; <userinput>make</userinput>
  &prompt; <userinput>make test</userinput>
  &prompt; <userinput>make install</userinput>
  </programlisting>
  </informalexample>
  </para>
  </sect2>
  </sect1>
  <sect1>
  <title>Sample Document Transformations</title>
  <para>
  Now, we're going to see how AxKit works by transforming an XML file containing data about
  Camelids (note the dubious Perl reference) into HTML.
  </para>
  <sect2>
  <title>Step 1 - A Sample XML Document</title>
  <para>
  First, you will need a sample XML file. Open the text editor of your choice
  and type the following:
  <informalexample>
  <programlisting><![CDATA[
  <?xml version="1.0"?>
    <dromedaries>
      <species name="Camel">
        <humps>1 or 2</humps>
        <disposition>Cranky</disposition>
      </species>
      <species name="Llama">
        <humps>1 (sort of)</humps>
        <disposition>Aloof</disposition>
      </species>
      <species name="Alpaca">
        <humps>(see Llama)</humps>
        <disposition>Friendly</disposition>
      </species>
  </dromedaries>
  ]]></programlisting>
  </informalexample>
  Save this file as test.xml.
  </para>
  </sect2>
  <sect2>
  <title>Step 2 - Create a Stylesheet</title>
  <para>
  Now, create the stylesheet to transform your XML document. If you have chosen to install
  Sablotron or one of the other XSLT processors that AxKit supports you may use either the XSLT
  or XPathScript samples below. If you have not installed an XSLT processor skip directly to
  the XPathScript example.
  </para>
  </sect2>
  <sect2>
  <title>Using XSLT</title>
  <para>
  Start a new file and type the following:
  <informalexample>
  <programlisting><![CDATA[
  <?xml version="1.0"?>
  <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">
  <xsl:template match="/">
    <html>
    <head><title>Know Your Dromedaries</title></head>
    <body>
      <table bgcolor="eeeeee" border="1">
      <tr>
      <th>Species</th>
      <th>No of Humps</th>
      <th>Disposition</th>
      </tr>
      <xsl:for-each select="dromedaries">
        <xsl:apply-templates select="./species" />
    </xsl:for-each>
    </table>
    </body>
    </html>
  </xsl:template>
  
  <xsl:template match="species">
    <tr>
    <td><xsl:value-of select="@name" /></td>
    <td><xsl:value-of select="./humps" /></td>
    <td><xsl:value-of select="./disposition" /></td>
    </tr>
  </xsl:template>
  
  </xsl:stylesheet>
  ]]></programlisting>
  </informalexample>
  Save this file as test.xsl.
  </para>
  </sect2>
  <sect2>
  <title>Using XPathScript</title>
  <para>
  Create a new file and type:
  <informalexample>
  <programlisting><![CDATA[
  <%
  $t->{'humps'}{'pre'} = "<td>";
  $t->{'humps'}{'post'} = "</td>";
  $t->{'disposition'}{'pre'} = "<td>";
  $t->{'disposition'}{'post'} = "</td>";
  $t->{'species'}{testcode} = sub {
      my $node = shift;
      my $t = shift;
      $t->{pre} = '<tr><td>' . findvalue('@name', $node) . '</td>';
      $t->{post} = "</tr>";
      return 1;
  }
  %>
  
  <html>
  <head>
          <title>Know Your Dromedaries</title>
  </head>
  <body bgcolor="white">
      <table bgcolor="eeeeee" border="1">
      <tr><th>Species</th><th>No. of Humps</th><th>Disposition</th></tr>
  
      <%= apply_templates('/dromedaries/species') %>
  
      </table>
  </body>
  </html>
  ]]></programlisting>
  </informalexample>
  Save this file as test.xps.
  </para>
  </sect2>
  <sect2>
  <title>Step 3 - Associate the XML Document with your Stylesheet</title>
  <para>
  Next, re-open the test.xml file and add the following just after the &lt;?xml version="1.0"?>
  declaration. If you have selected the XSLT example, add:
  <informalexample>
  <programlisting><![CDATA[
  <?xml-stylesheet href="test.xsl" type="text/xsl"?>
  ]]></programlisting>
  </informalexample>
   Or, if you have chosen the XPathScript sample, use:
  <informalexample>
  <programlisting><![CDATA[
  <?xml-stylesheet href="test.xps" type="application/x-xpathscript"?>
  ]]></programlisting>
  </informalexample>
  Note that this line is telling AxKit which stylesheet to use and
  which handler to use for that stylesheet. Now, save the test.xml
  file and quit your editor.
  </para>
  </sect2>
  <sect2>
  <title>Step 4 - Finishing Up</title>
  <para>
  You are now ready to deliver your little zoological XML data file as
  formatted HTML! Just copy or move the XML and stylesheet files into the
  same directory under your httpd DocumentRoot. Then, point your browser to
  http://your-server.com/path/to/test.xml and you should see your data nicely
  formatted in an HTML table.
  </para>
  <para>
  Congratulations, you are now well on the road to adding XML, XSLT, XPathScript and AxKit
  to your developmental toolbelt. For more information about AxKit's advanced
  features, please visit the <ulink url="http://www.axkit.org/">AxKit homepage</ulink>.
  </para>
  </sect2>
  <sect2>
  <title>If Things Go Wrong</title>
  <para>
  As I stated in the introduction, this is document is designed to walk someone through the
  AxKit instalation process, on to serving transformed documents as quickly and simply as
  possible. Depending upon your setup, installing AxKit may require some special attention to
  get it to sanely co-exist with some of the other tools that you may be using. If you've had
  any trouble while following the steps outlined here, please consult the <ulink url=
  "http://axkit.org/faq.xml">AxKit FAQ</ulink>, and the AxKit User's <ulink url=
  "http://axkit.org/cgi-bin/ezmlm-cgi/4">mailing list archives</ulink>.
  </para>
  </sect2>
  </sect1>
  <sect1>
  <title>Other Resources</title>
  <sect2>
  <title>mod_perl</title>
  <para>
    <itemizedlist>
      <listitem><ulink url="http://perl.apache.org">mod_perl Homepage</ulink>
      </listitem>
      <listitem><ulink url="http://perl.apache.org/guide/">mod_perl Guide
      </ulink>
      </listitem>
      <listitem><ulink url="http://www.modperl.com/">Writing Apache Modules
      with Perl and C</ulink>
      </listitem>
      <listitem><ulink url="http://take23.org/">mod_perl News and Information</ulink>
      </listitem>
    </itemizedlist>
  </para>
  </sect2>
  <sect2>
  <title>XSLT</title>
  <para>
    <itemizedlist>
      <listitem><ulink url="http://www.w3.org/Style/XSL/">W3C Specification
      </ulink></listitem>
      <listitem><ulink url="http://www.xml.com/pub/Guide/XSLT">XSLT Resources at XML.com
      </ulink></listitem>
      <listitem><ulink url="http://www.mulberrytech.com/quickref/index.html">Mulberry
      Technologies' XPath and XSLT quick reference card (PDF).</ulink></listitem>
      <listitem><ulink url="http://www.zvon.org/xxl/XSLTreference/Output/index.html">An
      expanded XSLT reference from zvon.org.</ulink></listitem>
      <listitem><ulink url="http://www.xslt.com/">XSLT.com</ulink></listitem>
    </itemizedlist>
  </para>
  </sect2>
  <sect2>
  <title>XPathScript</title>
  <para>
    <itemizedlist>
      <listitem><ulink url="http://axkit.org/docs/xpathscript/guide.dkb">XPathScript - A Viable
      Alternative to XSLT?</ulink></listitem>
    </itemizedlist>
  </para>
  </sect2>
  </sect1>
  </article>
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  1.1                  xml-axkit/axkit.org/docs/presentations/tpc2001/ax_logo.png
  
  	<<Binary file>>
  
  
  1.1                  xml-axkit/axkit.org/docs/presentations/tpc2001/redbg.png
  
  	<<Binary file>>
  
  
  1.1                  xml-axkit/axkit.org/docs/presentations/tpc2001/tpc-axkit.axp
  
  Index: tpc-axkit.axp
  ===================================================================
  <?xml version="1.0"?>
  <slideshow>
  
    <title>AxKit</title>
    <metadata>
       <speaker>Matt Sergeant</speaker>
       <email>matt@axkit.com</email>
       <organisation>AxKit.com Ltd</organisation>
       <link>http://axkit.com/</link>
       <logo scale="0.4">ax_logo.png</logo>
       <background>redbg.png</background>
    </metadata>
    
    <slide>
      <title>Introduction</title>
      <point level="1">Perl's XML Capabilities</point>
      <point level="1">AxKit intro</point>
      <point level="1">AxKit static sites</point>
      <point level="1">AxKit dynamic sites (XSP)</point>
      <point level="1">Advanced AxKit</point>
    </slide>
    
    <slideset>
       <title>XML with Perl Introduction</title>
       
       <slide>
          <title>Basis : XML::Parser</title>
          <point level="1">XS wrapper around expat</point>
          <point level="1">expat - James Clark</point>
          <point level="1">XML::Parser - originally by Larry Wall</point>
          <point level="2">Now maintained by Clark Cooper</point>
          <point level="1">Allows different "styles" of parsing</point>
          <point level="1">Default style is callback/event based stream parsing</point>
          <point level="1">Also implements a "push" parser</point>
       </slide>
       
       <slide>
          <title>XML::Parser usage</title>
          <point level="1">SAX-like API</point>
          <point level="1">register callback handler methods</point>
          <point level="2">start tag</point>
          <point level="2">end tag</point>
          <point level="2">characters</point>
          <point level="2">comments</point>
          <point level="2">processing instructions</point>
          <point level="2">... and more</point>
          <point level="1">Non validating XML parser</point>
          <point level="1">dies (throws an exception) on bad XML</point>
       </slide>
       
       <slide>
          <title>XML::Parser code</title>
          <source_code><![CDATA[
  my $p = XML::Parser->new(
      Handlers => { 
          Start => \&start_tag, 
          End => \&end_tag,
          # add more handlers here
          });
  
  $p->parsefile("foo.xml");
  
  exit(0);
  
  sub start_tag {
    my ($expat, $tag, %attribs) = @_;
    print "Start tag: $tag\n";
  }
  
  sub end_tag {
    my ($expat, $tag) = @_;
    print "End tag: $tag\n";
  }
          ]]></source_code>
       </slide>
       
       <slide>
       <title>XML::Parser - maintaining state</title>
       <source_code><![CDATA[
  my $ctxt = MyParser->new();
  
  my $p = XML::Parser->new(
    Handlers => {
      Start => sub { $ctxt->parse_start(@_) },
      End => sub { $ctxt->parse_end(@_) }
      });
  
  $p->parse("<some><xml>here</xml></some>");
  
  package MyParser;
  
  sub new {
     return bless {}, shift;
  }
  sub parse_start {
      my ($self, $expat, $tag, %attribs) = @_;
      $self->{some_state} = "Open $tag";
  }
  sub parse_end {
      my ($self, $expat, $tag) = @_;
      print "Last state: $self->{some_state}\n";
  }
       ]]></source_code>
       </slide>
       
       <slide>
       <title>PYX</title>
       <point level="1">Turns XML into a stream of text events</point>
       <point level="1">Based on SGML ESIS streams</point>
       <point level="2">)start</point>
       <point level="2">(end</point>
       <point level="2">Aattribute=value</point>
       <point level="2">-text</point>
       <point level="2">?processing instruction</point>
       <point level="1">Comments are dropped</point>
       </slide>
       
       <slide>
       <title>PYX example</title>
       <source_code>
  $ pyx presentation.xml | more
  (slideshow
  -\n
  -  
  (title
  -Developing XML Applications with Perl and AxKit
  )title
  -\n
  -  
  (metadata
  -\n
  -     
  (speaker
  -Matt Sergeant
  )speaker
  -\n
  -     
  (email
  -matt@axkit.com
  )email
  ...
       </source_code>
       </slide>
       
       <slide>
       <title>PYX usage</title>
       <point level="1">pyx xmlfile | something | pyxw</point>
       <point level="1">pyx generates PYX streams</point>
       <point level="1">pyxw generates XML from PYX</point>
       <point level="1">pyxhtml parses HTML to PYX</point>
       </slide>
       
       <slide>
       <title>HTML Tidy, or html2xml</title>
       <point level="1">Java utility by Dave Ragget of the W3C</point>
       <point level="1">Tidy converts HTML to XHTML</point>
       <point level="1">PYX can do the same:</point>
       <point level="2">pyxhtml file.html | pyxw > file.xhtml</point>
       <point level="1">pyxhtml uses HTML::TreeBuilder</point>
       </slide>
       
       <slide>
       <title>XPath intro</title>
       <point level="1">W3C Standard for locating nodes within an XML document</point>
       <point level="1">A subset of XPath is used for "matching" nodes in XSLT</point>
       <point level="1">Looks like directory paths: /path/to/node</point>
       <point level="1">But that's an abbreviated syntax...</point>
       <point level="2">/child::path/child::to/child::node</point>
       <point level="1">Full grammar containing expressions, calculations, functions, etc</point>
       </slide>
       
       <slide>
       <title>XPath Examples</title>
       <point level="1">Find title of XHTML document</point>
       <source_code>/html/head/title</source_code>
       <point level="1">Find all hrefs in &lt;a> tags</point>
       <source_code>/html/body/descendant::a[@href]/@href</source_code>
       </slide>
       
       <slide>
       <title>XML::XPath</title>
       <point level="1">Full implementation of W3C XPath on a DOM-like API</point>
       <point level="1">Easy to use:</point>
       <source_code><![CDATA[
  my $xp = XML::XPath->new(filename => "foo.xml");
  print $xp->findvalue("/html/head/title");
       ]]></source_code>
       <point level="1">Can also process HTML:</point>
       <source_code><![CDATA[
  my $xp = XML::XPath->new(
     filename => "pyxhtml foo.html | pyxw |");
       ]]></source_code>
       </slide>
       
       <slide>
       <title>XML::XPath continued</title>
       <point level="1">Every node has methods findvalue(), findnodes() and find()</point>
       <source_code><![CDATA[
  my $xp = XML::XPath->new(filename => "foo.xml");
  foreach my $link ($xp->findnodes('/html/body/a[@href]')) {
      print "Link: ", $link->findvalue('@href'), "\n";
      # or :
      # print "Link: ", $link->getAttribute('href'), "\n";
  }
       ]]></source_code>
       <point level="1">Not 100% DOM compatible, but close</point>
       </slide>
       
       <slide>
       <title>XML::XPath Implementation</title>
       <point level="1">XML::Parser and SAX parsers build an in-memory tree</point>
       <point level="1">Hand-built parser for XPath syntax (rather than YACC based parser)</point>
       <point level="1">Garbage Collection yet still has circular references (and works on Perl 5.005)</point>
       <image>pointers.png</image>
       </slide>
       
       <slide>
       <title>XML to XML Conversions</title>
       <point level="1">Use XSLT (or XPathScript, see later)</point>
       <point level="1">Either use the command line (e.g. xsltproc from libxslt)</point>
       <point level="1">... or call from Perl:</point>
       <source_code><![CDATA[
  use XML::LibXSLT;
  use XML::LibXML;
  my $parser = XML::LibXML->new();
  my $xslt = XML::LibXSLT->new();
  
  my $source_dom = $parser->parsefile('foo.xml');
  my $style_dom = $parser->parsefile('bar.xsl');
  
  my $stylesheet = $xslt->parse_stylesheet($style_dom);
  
  my $results = $stylesheet->transform($source_dom);
  print $stylesheet->output_string($results);
       ]]></source_code>
       </slide>
       
       <slide>
       <title>SAX Filters</title>
       <point level="1">SAX events passed between SAX handlers</point>
       <point level="1">e.g. tags to lower case:</point>
       <source_code><![CDATA[
  my $ya = XML::Handler::YAWriter->new( AsFile => '-' );
  my $lc = XML::Filter::ToLower->new( Handler => $ya );
  my $parser = XML::Parser::PerlSAX->new( Handler => $lc );
  
  package XML::Filter::ToLower;
  @ISA = ('XML::Filter::Base');
  sub start_element {
    my ($self, $element) = @_;
    $element->{Name} = lc($element->{Name});
    $self->{Handler}->start_element($element);
  }
  sub end_element {
    my ($self, $element) = @_;
    $element->{Name} = lc($element->{Name});
    $self->{Handler}->end_element($element);
  }
       ]]></source_code>
       </slide>
       
       <slide>
       <title>SAX Filters (cont)</title>
       <point>Almost any level of complexity possible</point>
       <point>Chain as many filters together as you like</point>
       <point>Fast way of processing XML</point>
       <point level="2">We don't have to build a tree in memory</point>
       <point>Possible to output to HTML using XML::Handler::HTMLWriter</point>
       </slide>
       
    </slideset>
    
    <slideset>
    <title>AxKit Introduction</title>
    
       <slide>
       <title>AxKit Introduction</title>
       <point level="1">XML Publishing Framework</point>
       <point level="1">XML Application Server</point>
       <point level="1">Designed for Content Diversification</point>
       <point level="2">Same content, delivered differently</point>
       <point level="2">HTML, WAP, PDF, SOAP, etc</point>
       <point level="1">Built in C, Perl, and mod_perl/Apache</point>
       <point level="1">Plugin Architecture, allows operation to be changed easily</point>
       </slide>
       
       <slide>
       <title>How does it work?</title>
       <point level="1">Apache handle</point>
       <point level="1">Embedded perl interpreter via mod_perl</point>
       <point level="1">Apache Config directives</point>
       <point level="1">Transformation Pipeline</point>
       <point level="1">Language Modules implement stylesheets and other engines</point>
       <point level="1">XSP for Dynamic Content</point>
       <point level="1">Caching</point>
       <point level="1">Apache::Filter Support</point>
       </slide>
       
       <slide>
       <title>mod_perl</title>
       <point level="1">mod_perl used to implement most of AxKit</point>
       <point level="1">... though more code being ported to C all the time</point>
       <point level="1">Gives us bytecode compiled Perl</point>
       <point level="1">Makes development faster</point>
       </slide>
       
       <slide>
       <title>Configuration Directives</title>
       <point level="1">Config directives written in the C code</point>
       <point level="1">Implemented on top of Apache</point>
       <point level="1">Fits in with httpd.conf and .htaccess files</point>
       <point level="1">28 Config directives in total</point>
       <point level="1">Setup the pipeline, debugging options, and various other flags</point>
       </slide>
       
       <slide>
       <title>Config Directives examples</title>
       <source_code><![CDATA[
  AxDebugLevel 5
  AxCacheDir /tmp/axkit_cache
  
  <Location /apps/calendar>
  
   AxAddProcessor application/x-xsp .
  
   <AxMediaType handheld>
    AxAddProcessor text/xsl /styles/cal2wml.xsl
   </AxMediaType>
   <AxMediaType screen>
    AxAddProcessor text/xsl /styles/cal2html.xsl
   </AxMediaType>
  
  </Location>
       ]]></source_code>
       </slide>
       
       <slide>
       <title>Transformation Pipeline</title>
       <point level="1">AxKit is a pipeline engine</point>
       <point level="1">Output of one stage goes to input of the next</point>
       <point level="1">Allows us to build up our application in stages</point>
       <point level="1">Pipeline passed as one of:</point>
       <point level="2">String</point>
       <point level="2">DOM Tree</point>
       <point level="2">SAX events</point>
       </slide>
       
       <slide>
       <title>Language Modules</title>
       <point level="1">Every stage in the pipeline implemented by a language module</point>
       <point level="1">Language modules can either use a stylesheet for transformation or not</point>
       <point level="2">Examples of modules not using a stylesheet are "XSL:FO to PDF", XSP, and the module which produces this slideshow</point>
       <point level="1">Different implementations of same language possible, e.g. XSLT</point>
       <point level="2">XML::LibXSLT</point>
       <point level="2">XML::Sablotron</point>
       <point level="2">XML::XSLT</point>
       <point level="2">XML::Xalan</point>
       <point level="2">XML::Transformiix</point>
       </slide>
       
       <slide>
       <title>Languages modules available</title>
       <point level="1">XSLT</point>
       <point level="1">XPathScript</point>
       <point level="1">XSP</point>
       <point level="1">AxPoint (this slideshow)</point>
       <point level="1">XMLNews::HTMLTemplate</point>
       <point level="1">Template Toolkit w/XPath plugin</point>
       </slide>
       
       <slide>
       <title>Caching</title>
       <point level="1">XML Transformation can be slow, caching vital to making a fast XML Publishing Engine</point>
       <point level="1">Cache uses the filesystem, not memory or DBM files</point>
       <point level="1">Cache results of transformation pipeline, where appropriate</point>
       <point level="1">Cache results of dynamic processing by implementing has_changed() method</point>
       <point level="1">Using filesystem allows Apache to deliver the cache directly:</point>
       <source_code>Apache->request->filename = &lt;cache-file>;
  return DECLINED;
  </source_code>
       </slide>
       
       <slide>
       <title>Cache (cont.)</title>
       <point level="1">Fastest possible implementation of a cache</point>
       <point level="1">Default storage place is .xmlstyle_cache directory in same directory as XML file</point>
       <point level="1">Overridable using AxCacheDir directive</point>
       <point level="1">Files named as MD5 hash of XML filename, media type, style name and optionally run-time parameters that may affect the cache</point>
       <point level="1">Does use large amounts of space, so can be turned off</point>
       </slide>
       
       <slide>
       <title>Non Cache-able content</title>
       <point level="1">POST requests usually not cacheable</point>
       <point level="1">XSP pages usually not cacheable</point>
       <point level="1">Can turn off the cache in Perl code:</point>
       <source_code>$r->no_cache(1);</source_code>
       <point level="1">Cache can be modified using plugins, so cache can be based on phase of the moon</point>
       </slide>
       
       <slide>
       <title>Apache::Filter Support</title>
       <point level="1">Alternate plugin "Provider" module</point>
       <point level="1">AxKit can get XML from previous mod_perl handler</point>
       <point level="1">Works with Apache::ASP, HTML::Mason, etc</point>
       </slide>
       
       <slide>
       <title>Apache::Filter example</title>
       <point level="1">ASP Script</point>
       <source_code><![CDATA[
  <?xml version="1.0"?>
  <?xml-stylesheet href="/foo.xsl" type="text/xsl"?>
  <page>
  <head><title>Hello World</title></head>
  <body>
  <section>
  <title>Test Page</title>
  <para>
  Hello World
  </para>
  <para>
  The time is now: <%= localtime %>
  </para>
  </body>
  </page>
       ]]></source_code>
       </slide>
       
       <slide>
       <title>Apache::Filter example (stylesheet)</title>
       <source_code><![CDATA[
  <xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  
  <xsl:template match="/">
  <html>
  <head><title><xsl:value-of select="/page/head/title"/> </title></head>
  <body>
  <xsl:apply-templates/>
  </body>
  </html>
  </xsl:template>
  
  <xsl:template match="section">
  <h1><xsl:value-of select="title"/></h1>
  <xsl:apply-templates select="para"/>
  </xsl:template>
  
  <xsl:template match="para">
  <p><xsl:apply-templates/></p>
  </xsl:template>
  
  </xsl:stylesheet>
       ]]></source_code>
       </slide>
       
       <slide>
       <title>Apache::Filter example (results)</title>
       <source_code><![CDATA[
  <html>
  <head><title>Hello World</title></head>
  <body>
  <h1>Test Page</h1>
  <p>
  Hello World
  </p>
  <p>
  The time is now: Tue May  8 07:41:33 2001
  </p>
  </body>
  </html>
       ]]></source_code>
       </slide>
       
       <slide>
       <title>Gzipped Output</title>
       <point level="1">Many HTTP/1.1 browsers will accept gzip encoded results (modulo bugs)</point>
       <point level="1">AxKit can check Accept-Encoding header for this capability</point>
       <point level="1">Will then gzip content before delivery</point>
       <point level="1">Cache stores regular and gzipped copy</point>
       <point level="1">Reduces used bandwidth by up to 85%</point>
       </slide>
       
       <slide>
       <title>Developer's Perspective</title>
       <point level="1">Two ways to tell AxKit how to process XML</point>
       <point level="2">Processing Instructions in the XML</point>
       <source_code><![CDATA[
  <?xml-stylesheet href="/styles/foo.xsl" type="text/xsl"?>
       ]]></source_code>
       <point level="2">Configuration Directives in httpd.conf</point>
       <source_code><![CDATA[
  AxAddProcessor text/xsl /styles/foo.xsl
       ]]></source_code>
       <point level="1">PIs act as override mechanism</point>
       <point level="1">Also PIs are sometimes appropriate, e.g. for XSP files</point>
       </slide>
       
       <slide>
       <title>Processing Instructions</title>
       <point level="1">Follows the W3C xml-stylesheet REC</point>
       <point level="1">Works almost identically to HTML's LINK element</point>
       <point level="1">Processing instruction is just &lt;?target data?>, so attributes are called "psuedo attributes"</point>
       <point level="1">Allowed attributes:</point>
       <point level="2">href - the location of the stylesheet</point>
       <point level="2">type - the mime type of the stylesheet</point>
       <point level="2">alternate - yes or no, specifies alternate styling</point>
       <point level="2">media - media type this stylesheet applies to</point>
       <point level="2">title - group this stylesheet belongs to</point>
       <point level="1">Only href and type are required</point>
       </slide>
       
       <slide>
       <title>Config Directives</title>
       <point level="1">More complex and more powerful than PIs</point>
       <source_code><![CDATA[
  AxAddProcessor text/xsl /styles/foo.xsl
  AxAddRootProcessor text/xsl /styles/article.xsl article
  AxAddDocTypeProcessor text/xsl /styles/docbook.xsl \
          "-//OASIS//DTD DocBook XML V4.1.2//EN"
  AxAddDTDProcessor text/xsl /styles/bar.xsl /dtds/bar.dtd
  AxAddURIProcessor text/xsl /styles/book.xsl \
          "book.*\.xml$"
       ]]></source_code>
       <point level="1">Fully documented in perldoc AxKit</point>
       </slide>
       
       <slide>
       <title>AxKit vs Cocoon</title>
       <point level="1">Cocoon is a Servlet implementing some of the same functionality</point>
       <point level="1">AxKit is tied to Apache</point>
       <point level="1">Cocoon 2 [alpha] adds features like AxKit config directives</point>
       <point level="1">Both implement XSP, and collaborate on the spec</point>
       <point level="1">Both open source</point>
       <point level="1">Perl and C vs Java</point>
       <point level="1">Use what fits your environment</point>
       </slide>
       
    </slideset>
    
    <slideset>
       <title>AxKit for Static Content</title>
       
       <slide>
       <title>XML Publishing</title>
       <point level="1">Using XML for the source of all our web site's content</point>
       <point level="1">Semantically rich original data</point>
       <point level="1">Delivery to different media types</point>
       <point level="2">Example - slides</point>
       <point level="3">Deliver to HTML</point>
       <point level="3">Or PDF</point>
       <point level="3">Or plain text</point>
       <point level="2">AxKit can detect via plugins, which version the user wants</point>
       </slide>
       
       <slide>
       <title>XML Based Content</title>
       <point level="1">DocBook</point>
       <point level="1">XHTML</point>
       <point level="1">TEI XML</point>
       <point level="1">RSS</point>
       <point level="1">MyML - invent your own!</point>
       <point level="1">Or use Provider interface...</point>
       <point level="2">OpenOffice</point>
       <point level="2">MS Word (via libwv)</point>
       <point level="2">Content Management System</point>
       <point level="1">Or use other mod_perl modules (Mason, ASP, etc) to generate XML</point>
       </slide>
       
       <slide>
       <title>The Value of Semantics</title>
       <point level="1">HTML has low semantic value</point>
       <point level="1">Example point - farming the web</point>
       <point level="1">XML provides the ability to add value to our content</point>
       <point level="1">But we still need to deliver it as HTML</point>
       <point level="1">AxKit can give you the best of both worlds</point>
       </slide>
       
       <slide>
       <title>Alternative Media</title>
       <point level="1">The web is no longer just web browsers on PCs</point>
       <point level="1">TV is becoming huge, Phone/Handheld is already huge</point>
       <point level="1">But the cost of entry into different media types is still high</point>
       <point level="1">Using XML for our content reduces that cost of entry</point>
       <point level="2">And it will reduce your long term costs</point>
       </slide>
       
       <slide>
       <title>Media vs Styles</title>
       <point level="1">Same concept, different reasons</point>
       <point level="1">Use different styles for the same media device</point>
       <point level="1">e.g. No graphics HTML version, printable version,
       PDF version. All delivered to browsers on PCs</point>
       <point level="1">Some applications in personalisation</point>
       </slide>
       
       <slide>
       <title>The Key - Stylesheets</title>
       <point level="1">Stylesheets translate XML on the fly to different formats</point>
       <point level="1">Different stylesheets used for different media types</point>
       <point level="1">... and for different styles of the same media type</point>
       <point level="1">Generally, content is cached for increased performance</point>
       </slide>
       
       <slide>
       <title>What is the benefit to you?</title>
       <point level="1">Web Developers benefit because of separation of concerns</point>
       <point level="1">Content</point>
       <point level="1">Presentation</point>
       <point level="1">Logic</point>
       <point level="1">Site Management</point>
       </slide>
       
       <slide>
       <title>Development Scalability</title>
       <point level="1">Long term site management is a mess without separation of concerns</point>
       <point level="1">Templates vs print print print</point>
       <point level="2">But we can do better</point>
       <point level="1">Different devices throws a spanner in the works</point>
       <point level="1">Ideal is for content creators to focus on content, developers to focus on logic, designers to focus on presentation, and webmasters to focus on site management</point>
       <point level="1">Unfortunately this is still idealistic, but much better than print print print</point>
       </slide>
       
       <slide>
       <title>Content Authoring</title>
       <point level="1">XML Editors</point>
       <point level="1">WYSIWYM Editors (What You See Is What You Mean)</point>
       <point level="1">foo2xml can also be used (e.g. pod2xml)</point>
       <point level="1">Mostly commercial offerings: Adept, XMeTaL, XMLSpy</point>
       <point level="1">Open source options: OpenOffice, Emacs+psgml, jedit</point>
       <point level="1">Some re-training may be required</point>
       </slide>
       
       <slide>
       <title>Presentation Layer</title>
       <point level="1">Currently the hardest part of converting from static HTML sites</point>
       <point level="1">Some commercial tools available for writing XSLT stylesheets</point>
       <point level="1">Sometimes templates generated using HTML tools (e.g. Frontpage) possible</point>
       <point level="1">Often best way is to get a design, then have a stylesheet guru convert that</point>
       </slide>
       
       <slide>
       <title>Business Logic</title>
       <point level="1">AxKit uses taglib concept</point>
       <point level="1">Logic wrapped in XML tags</point>
       <source_code>&lt;order_fluglebar quantity="1"/></source_code>
       <point level="1">Designers can drop these tags into their XML</point>
       <point level="1">Logic tags output XML, not HTML</point>
       <point level="1">Thus logic can be used for any output media/style</point>
       <point level="1">See next section (Dynamic Content)</point>
       </slide>
       
       <slide>
       <title>Site Management</title>
       <point level="1">AxKit uses Apache config directives in httpd.conf/.htaccess</point>
       <point level="1">Allows you to use your web master skills</point>
       <point level="1">Also allows flexible configuration based on &lt;Location>, &lt;Files>, and &lt;Directory> directives</point>
       </slide>
       
       <slide>
       <title>XSLT Overview</title>
       <point level="1">Reformation of transformation part of DSSSL into XML (from Scheme)</point>
       <point level="1">Functional language (no side effects)</point>
       <point level="1">Rules based ("apply this template when we see this element")</point>
       <point level="1">Implemented in AxKit via:</point>
       <point level="2">XML::LibXSLT (very fast compliant processor from Red Hat Labs</point>
       <point level="2">XML::Sablotron (lightweight processor from Ginger Alliance</point>
       <point level="2">Others: XML::Xalan, XML::XSLT, XML::Transformiix</point>
       </slide>
       
       <slide>
       <title>XPathScript Overview</title>
       <point level="1">Combination of:</point>
       <point level="2">Perl</point>
       <point level="2">ASP &lt;% %> delimiters</point>
       <point level="2">XML::XPath for locating nodes in the source XML</point>
       <point level="2">Declarative (Rules based) processing</point>
       <point level="1">Not a functional language</point>
       <point level="1">Side effects allowed</point>
       <point level="1">Full access to Apache API</point>
       </slide>
       
       <slide>
       <title>XPathScript example (xml)</title>
       <source_code><![CDATA[
  <employees>
   <employee>
    <name>
     <firstname>Roger</firstname>
     <lastname>Rabbit</lastname>
    </name>
    <department>Humour</department>
   </employee>
   <employee>
    <name>
     <firstname>Jessica</firstname>
     <lastname>Rabbit</lastname>
    </name>
    <department>Modelling</department>
   </employee>
  </employees>
       ]]></source_code>
       </slide>
       
       <slide>
       <title>XPathScript example (stylesheet)</title>
       <source_code><![CDATA[
  <html>
  <head><title>Employee List</title></head>
  <body>
  <h1>Employees at Acme Corp.</h1>
  
  <% foreach my $employee (findnodes('/employees/employee')) { %>
  
  <b><%= $employee->findvalue("name/lastname") %>,
  <%= $employee->findvalue("name/firstname") %></b>
  works in the <%= $employee->findvalue('department') %>
  department.
  
  <hr>
  
  <% } %>
  
  </body>
  </html>
       ]]></source_code>
       </slide>
       
       <slide>
       <title>XPathScript example (results)</title>
       <source_code><![CDATA[
  <html>
  <head><title>Employee List</title></head>
  <body>
  <h1>Employees at Acme Corp.</h1>
  
  <b>Rabbit, Roger</b> works in the Humour department
  <hr>
  <b>Rabbit, Jessica</b> works in the Modelling department
  <hr>
  </body>
  </html> 
       ]]></source_code>
       </slide>
       
       <slide>
       <title>XPathScript - Processing Documents</title>
       <point level="1">Transforming Data is very different to transforming Documents</point>
       <point level="1">Documents have mixed content</point>
       <point level="1">Needs rules based processing</point>
       <point level="1">XPathScript implements a feature rich declarative processing system</point>
       </slide>
       
       <slide>
       <title>XPathScript - Declarative Templates</title>
       <point level="1">The $t hash reference</point>
       <source_code><![CDATA[
  $t->{'para'}{pre} = '<p>';
  $t->{'para'}{post} = '</p>';
       ]]></source_code>
       <point level="1">Matches element names (unlike XSLT)</point>
       <point level="1">XPathScript applies the rules as it traverses the document tree</point>
       </slide>
       
       <slide>
       <title>XPathScript - $t subkeys</title>
       <point level="1">Sub-keys of $t specify what to do</point>
       <point level="1">Keys who's value is something to add to the ouput:</point>
       <point level="2">pre</point>
       <point level="2">post</point>
       <point level="2">prechildren</point>
       <point level="2">postchildren</point>
       <point level="2">prechild</point>
       <point level="2">postchild</point>
       <point level="1">Other keys:</point>
       <point level="2">showtag</point>
       <point level="2">testcode</point>
       </slide>
       
       <slide>
       <title>XPathScript - testcode</title>
       <point level="1">testcode value is a code ref</point>
       <point level="1">Executed every time an element with that name is visited</point>
       <point level="1">Gives access to XML::XPath API on that node, and a local copy of $t</point>
       <source_code><![CDATA[
  $t->{'a'}{testcode} = sub {
    my ($node, $t2) = @_;
    if ($node->findvalue('@name')) {
      # process as anchor
    }
    else {
      # process as link
    }
    return DO_SELF_AND_KIDS;
  };
       ]]></source_code>
       </slide>
       
       <slide>
       <title>XPathScript tech details</title>
       <point level="1">Script is compiled into Perl code</point>
       <point level="1">Compiled once on first hit. Only re-compiled if changed</point>
       <point level="1">Imports some functions: apply_templates(), findnodes(), findvalue()</point>
       <point level="1">Fast enough for most real-time transformation needs (e.g. for dynamic content</point>
       </slide>
       
       <slide>
       <title>Complete Example</title>
       <point level="1">Take23.org - mod_perl news and resources</point>
       <point level="1">Uses RSS 1.0 for all headlines and syndication</point>
       <point level="1">Static Content, but looks dynamic</point>
       <point level="1">Accepts content for articles in:</point>
       <point level="2">XHTML</point>
       <point level="2">DocBook</point>
       <point level="2">POD (via pod2xml)</point>
       <point level="2">OpenOffice 614 XML</point>
       </slide>
       
       <slide>
       <title>Take23 - how it works</title>
       <point level="1">httpd.conf contains AxAddRootProcessor directives</point>
       <point level="1">Stylesheets written in XPathScript</point>
       <point level="1">Stylesheets componentized to maximize reuse</point>
       <point level="1">Uses OpenOffice provider module from AxKit.com to read OpenOffice files as XML</point>
       <point level="1">News items created using AxKit::NewsMaker, a lightweight RSS/News content management system</point>
       <point level="1">Cached content delivered at static HTML speeds</point>
       </slide>
            
    </slideset>
    
    <slideset>
       <title>AxKit for Dynamic Content</title>
       
       <slide>
       <title>XSP is the Key</title>
       <point level="1">XSP is an XML language invented by the Cocoon Project at Apache</point>
       <point level="1">XSP is implementation language agnostic</point>
       <point level="2">Cocoon implements using Java</point>
       <point level="2">AxKit implements using Perl</point>
       <point level="1">XSP allows you to embed code in XML</point>
       <point level="1">XSP is a page-based language like PHP and ASP</point>
       <point level="1">... but XSP is better because you generate XML, maintaining separation of concerns</point>
       </slide>
       
       <slide>
       <title>XSP Example</title>
       <source_code><![CDATA[
  <xsp:page xmlns:xsp="http://www.apache.org/1999/XSP/Core"
    language="Perl">
  <time>
   <xsp:expr>localtime(time)</xsp:expr>
  </time>
  </xsp:page>
       ]]></source_code>
       <point level="1">Which generates:</point>
       <source_code><![CDATA[
  <time>
    Tue May  8 10:20:03 2001
  </time>
       ]]></source_code>
       <point level="1">Note: we then transform to HTML in the next part of the pipeline</point>
       </slide>
       
       <slide>
       <title>XSP Lowdown</title>
       <point level="1">XSP implements 11 core tags</point>
       <point level="2">page</point>
       <point level="2">structure</point>
       <point level="2">import</point>
       <point level="2">content</point>
       <point level="2">logic</point>
       <point level="2">element</point>
       <point level="2">attribute</point>
       <point level="2">pi</point>
       <point level="2">comment</point>
       <point level="2">text</point>
       <point level="2">expr</point>
       <point level="1">(plus a couple of other minor/supporting tags)</point>
       </slide>
       
       <slide>
       <title>XSP and Namespaces</title>
       <point level="1">XML Namespaces are key to how XSP works</point>
       <source_code>
  &lt;prefix:tag xmlns:prefix="URI">
       </source_code>
       <point level="1">Each namespace can either implement custom functionality</point>
       <point level="1">... or just be part of the output</point>
       <point level="1">We call the namespaces "Tag Libraries", or taglibs for short</point>
       </slide>
       
       <slide>
       <title>More XSP Examples</title>
       <source_code><![CDATA[
  <xsp:page xmlns:xsp="http://www.apache.org/1999/XSP/Core"
    language="Perl">
  <xsp:structure>
    <xsp:import>Time::Piece</xsp:import>
  </xsp:structure>
  
  <xsp:logic>
    sub afternoon {
      return localtime->hour > 12;
    }
  </xsp:logic>
  
  <page>
  <xsp:logic>
  if (afternoon()) {
  <xsp:content>
  Good <em>Afternoon!!!</em>
  </xsp:content>
  }
  else {
  <xsp:content>
  Good <em>Morning!!!</em>
  </xsp:content>
  }
  </xsp:logic>
  </page>
  </xsp:page>
       ]]></source_code>
       </slide>
       
       <slide>
       <title>XSP Implementation</title>
       <point level="1">SAX Parser, callbacks generate Perl code</point>
       <point level="1">Perl compiled into a unique class</point>
       <point level="1">handler() method wraps non-XSP top level tags (&lt;page> tag in previous example)</point>
       <point level="1">xsp:logic outside of top level tags can generate local functions</point>
       <point level="2">mod_perl developers will know how handy this is to avoid closure problems</point>
       <point level="1">Parser code implements "namespace dispatch"</point>
       <point level="1">taglib implementations register with the core XSP parser engine's namespace dispatch system</point>
       </slide>
       
       <slide>
       <title>XSP Taglibs</title>
       <point level="1">Taglibs implement custom functionality</point>
       <point level="1">Usually this is business logic</point>
       <point level="1">Some low level taglibs exist on CPAN:</point>
       <point level="2">Param</point>
       <point level="2">Utils</point>
       <point level="2">Cookie</point>
       <point level="2">ParamAttrib</point>
       <point level="2">SendMail</point>
       <point level="2">Exceptions</point>
       <point level="2">ESQL</point>
       </slide>
       
       <slide>
       <title>Implementing Taglibs</title>
       <point level="1">3 ways to implement taglibs (in decreasing levels of difficulty)</point>
       <point level="1">1. Write a Stylesheet</point>
       <point level="2">this transforms the taglib tags into core XSP tags</point>
       <point level="1">2. Write a SAX parser</point>
       <point level="2">here the parser has to write perl code</point>
       <point level="1">3. Use the AxKit TaglibHelper module</point>
       <point level="2">here you just write a Perl module, naming the functions after the tags you want to implement</point>
       <point level="1">Most of the taglibs on CPAN use method 2 at the moment, simply because of when TaglibHelper appeared</point>
       </slide>
       
       <slide>
       <title>Param Taglib</title>
       <source_code><![CDATA[
  <xsp:page
   xmlns:xsp="http://apache.org/xsp/core/v1"
   xmlns:param="http://axkit.org/NS/xsp/param/v1"
   language="Perl"
  >
  <page>
   <xsp:logic>
    if (<param:name/>) {
    <xsp:content>
     Your name is: <param:name/>
    </xsp:content>
    }
    else {
    <xsp:content>
     <form>
     Enter your name: <input type="text" name="name" />
     <input type="submit"/>
     </form>
    </xsp:content>
    }
   </xsp:logic>
  </page>
  </xsp:page>
       ]]></source_code>
       </slide>
       
       <slide>
       <title>Things to note...</title>
       <point level="1">Freely mixed Perl and XML</point>
       <point level="2">No &lt;% ... %> to introduce one or the other</point>
       <point level="2">An XSP page is pure XML</point>
       <point level="1">That has consequences:</point>
       <source_code><![CDATA[
  <xsp:logic>
  my $page = <param:page/>
  if ($page < 3) { # INVALID CODE. < not allowed in XML
    ...
  }
  </xsp:logic>
       ]]></source_code>
       <point level="1">Could iritate some people</point>
       <point level="1">Easy to fix this case. Use 3 > $page</point>
       </slide>
       
       <slide>
       <title>Cookie Taglib</title>
       <source_code><![CDATA[
  <xsp:page
   xmlns:xsp="http://apache.org/xsp/core/v1"
   xmlns:cookie="http://axkit.org/NS/xsp/cookie/v1"
   language="Perl"
  >
  <page>
   <xsp:logic>
   my $value;
   if ($value = <cookie:fetch name="count"/>) {
    $value++;
   }
   else {
    $value = 1;
   }
   </xsp:logic>
  
   <cookie:create name="count">
    <cookie:value><xsp:expr>$value</xsp:expr></cookie:value>
   </cookie:create>
  
   <p>Cookie value: <xsp:expr>$value</xsp:expr></p>
  </page>
  </xsp:page>
       ]]></source_code>
       </slide>
       
       <slide>
       <title>More things to note</title>
       <point level="1">XSP extends Perl's notion of DWIM (Do What I Mean)</point>
       <point level="1">Note how the cookie:value is taken from a Perl expression</point>
       <point level="1">It could equally have been hard coded. Witness these three versions:</point>
       <source_code><![CDATA[
  <cookie:value>3</cookie:value>
  <cookie:value><xsp:expr>2 + 1</xsp:expr></cookie:value>
  <cookie:value><param:cookie_value/></cookie:value>
       ]]></source_code>
       <point level="1">All produce valid code</point>
       </slide>
       
       <slide>
       <title>SendMail Taglib</title>
       <source_code><![CDATA[
  <xsp:page (namespace stuff removed for space issues)>
  <page>
   <xsp:logic>
    if (!<param:email/>) {
     <p>You forgot to supply an email address!</p>
    }
    else {
     if (<param:subopt/> eq "sub") {
      $to = "axkit-users-subscribe@axkit.org";
     }
     elsif (<param:subopt/> eq "unsub") {
      $to = "axkit-users-unsubscribe@axkit.org";
     }
     <mail:send-mail>
      <mail:from><param:user_email/></mail:from>
      <mail:to><xsp:expr>$to</xsp:expr></mail:to>
      <mail:body>Subscribe or Unsubscribe <param:user_email/></mail:body>
     </mail:send-mail>
     <p>(un)subscription request sent</p>
    }
   </xsp:logic>
  </page>
  </xsp:page>
       ]]></source_code>
       </slide>
       
       <slide>
       <title>Exceptions Taglib</title>
       <point level="1">Previous slides lacking error handling</point>
       <point level="1">All AxKit taglibs throw exceptions on error</point>
       <point level="1">Need Exception taglib to catch them</point>
       <source_code><![CDATA[
  <except:try>
   ... # code that throws exceptions
   <except:catch>
    <error>An Error Occured: <except:message/></error>
   </except:catch>
  </except:try>
       ]]></source_code>
       </slide>
       
       <slide>
       <title>ESQL Taglib</title>
       <point level="1">The mother of all taglibs</point>
       <point level="1">Executes SQL against DBI connections</point>
       <point level="1">Add Apache::DBI in the mix for cached connections</point>
       <point level="1">Options for SQL with or without results</point>
       <point level="1">Columns can be retrieved all at once, or one at a time</point>
       <point level="1">Allows emulation of nested queries (e.g. for MySQL)</point>
       </slide>
       
       <slide>
       <title>ESQL Example</title>
       <source_code><![CDATA[
  <xsp:page
    language="Perl"
    xmlns:xsp="http://apache.org/xsp/core/v1"
    xmlns:esql="http://apache.org/xsp/SQL/v2"
    xmlns:except="http://axkit.org/NS/xsp/exception/v1"
    xmlns:param="http://axkit.org/NS/xsp/param/v1"
    indent-result="no"
  >
  <addresses>
   <esql:connection>
    <!-- connection parameters (normally use entities for these) -->
    <esql:driver>Pg</esql:driver>
    <esql:dburl>dbname=phonebook</esql:dburl>
    <esql:username>postgres</esql:username>
    <esql:password></esql:password>
  
    <except:try>
  
    <esql:execute-query>
       ]]></source_code>
       </slide>
       
       <slide>
       <title>ESQL Example (cont)</title>
       <source_code><![CDATA[
     <xsp:logic>
     if (<param:address_id/>) {
      <esql:query>
       SELECT * FROM address WHERE id =
       <esql:parameter><param:address_id/></esql:parameter>
      </esql:query>
     }
     else {
      <esql:query>
       SELECT * FROM address
      </esql:query>
     }
     </xsp:logic>
  
     <esql:results>
      <esql:row-results>
      <address>
       <esql:get-columns/>
      </address>
      </esql:row-results>
     </esql:results>
    </esql:execute-query>
       ]]></source_code>
       </slide>
       
       <slide>
       <title>ESQL Example (cont)</title>
       <source_code><![CDATA[
  
    <except:catch>
     Error Occured: <except:message/>
    </except:catch>
  
   </except:try>
  
   </esql:connection>
  </addresses>
  </xsp:page>
       ]]></source_code>
       </slide>
       
       <slide>
       <title>ESQL Example (results)</title>
       <source_code><![CDATA[
  <addresses>
   <address>
    <id>2</id>
    <last_name>Sergeant</last_name>
    <first_name>Matt</first_name>
    <title>Mr</title>
    <company>AxKit.com Ltd</company>
    <email>matt@axkit.com</email>
    <classification_id>1</classification_id>
   </address>
  </addresses>
       ]]></source_code>
       </slide>
       
       <slide>
       <title>PerForm</title>
       <point>Easy Form handling</point>
       <point>Should allow for same form to be presented to WML or other formats. But HTML is the priority</point>
       <point>Callback based</point>
       <point>Supports validation, loading, start/end form</point>
       <point>Generates XML abstract form, not HTML or XHTML form</point>
       <point>Example XSLT supplied for transforming to HTML</point>
       </slide>
       
       <slide>
       <title>PerForm (example)</title>
       <source_code><![CDATA[
  <xsp:page>
  <xsp:logic>
  ...
  </xsl:logic>
  <html>
  
  <head><title>Index</title></head>
  <body>
    <f:form name="view">
    <h2>View Items</h2>
    <f:single-select name="types"/>
    <f:submit name="go" value="Go!"/>
    </f:form>
  </body>
  </html>
  </xsp:page>
       ]]></source_code>
       <point>Callbacks implemented in top-level &lt;xsp:logic> section</point>
       </slide>
       
       <slide>
       <title>PerForm (example cont)</title>
       <point>Callbacks:</point>
       <source_code><![CDATA[
  sub load_types {
    my ($ctxt, $selected) = @_;
    
    my $db = AxKit::RHNews::DB->instance();
    my @types = $db->get_asset_types();
    return $selected, "All Types" => "0", map { $_->[1] => $_->[1] } @types;
  }
  
  use Apache::Util ();
  
  sub submit_go {
    my ($ctxt) = @_;
    return "view_asset.xsp?type=" . Apache::Util::escape_uri($ctxt->{Form}{types});
  }
       ]]></source_code>
       </slide>
       
       <slide>
       <title>Building your XSP Based App</title>
       <point level="1">Hide logic in taglibs</point>
       <point level="1">Or hide logic in Perl modules called from &lt;xsp:logic> and &lt;xsp:expr> sections</point>
       <point level="1">Generate XML, not HTML</point>
       <point level="1">Process to HTML in a further pipeline transformation</point>
       </slide>
       
    </slideset>
    
    <slideset>
    <title>Advanced AxKit</title>
    
    <slide>
    <title>Alternate Providers</title>
    <point>Builtins: File, Filter, Scalar</point>
    <point>Others: e.g. OpenOffice</point>
    <point>API:</point>
    <point level="2">init( %params )</point>
    <point level="2">key() = string</point>
    <point level="2">exists() = ?</point>
    <point level="2">process() = ?</point>
    <point level="2">decline(%args)</point>
    <point level="2">mtime() = int</point>
    <point level="2">get_fh() = FH</point>
    <point level="2">get_strref() = strref</point>
    <point level="2">has_changed($mtime) = ?</point>
    <point level="2">get_ext_ent_handler() = subref</point>
    <point level="2">get_styles($media, $style)</point>
    </slide>
    
    <slide>
    <title>Example: Ever expiring file</title>
    <source_code><![CDATA[
  package Apache::AxKit::Provider::ExpireFile;
  use Apache::AxKit::Provider::File;
  @ISA = ('Apache::AxKit::Provider::File');
  
  sub has_changed {
    my ($self, $mtime) = @_;
    return 1;
  }
  
  1;
    ]]></source_code>
    <point>httpd.conf</point>
    <source_code><![CDATA[
  AxProvider Apache::AxKit::Provider::ExpireFile
    ]]></source_code>
    </slide>
    
    <slide>
    <title>Alternate ConfigReaders</title>
    <point level="2">new($r)</point>
    <point level="2">StyleMap() = hashref</point>
    <point level="2">CacheDir() = string</point>
    <point level="2">ProviderClass() = string</point>
    <point level="2">PreferredStyle() = string</point>
    <point level="2">PreferredMedia() = string</point>
    <point level="2">CacheModule() = string</point>
    <point level="2">DebugLevel() = int</point>
    <point level="2">StackTrace() = ?</point>
    <point level="2">LogDeclines() = ?</point>
    <point level="2">OutputCharset() = string</point>
    <point level="2">ErrorStyles() = complex</point>
    <point level="2">GzipOutput() = ?</point>
    <point level="2">DoGzip() = ?</point>
    <point level="2">GetMatchingProcessors() = complex</point>
    <point level="2">XSPTaglibs() = list</point>
    </slide>
    
    <slide>
    <title>Alternate Cache Module</title>
    <point>Possibly store cache in a database</point>
    <point>API:</point>
    <point level="2">new($r, $xmlfile, @parts)</point>
    <point level="2">write($string)</point>
    <point level="2">read() = string</point>
    <point level="2">get_fh() = FH</point>
    <point level="2">deliver()</point>
    <point level="2">reset()</point>
    <point level="2">has_changed($mtime) = ?</point>
    <point level="2">mtime()</point>
    <point level="2">exists() = ?</point>
    <point level="2">key() = string</point>
    <point level="2">no_cache($on)</point>
    </slide>
    
    </slideset>
    
    <slide>
    <title>AxKit Future Plans</title>
    <point level="1">Content Management System, for easier site building and maintainence</point>
    <point level="1">Apache 2.0 Port</point>
    <point level="1">More SAX work, more C work</point>
    <point level="1">Allow JSP taglibs to work</point>
    </slide>
    
    <slide>
    <title>Conclusions</title>
    <point level="1">Perl and XML are a powerful combination</point>
    <point level="1">XPath and XSLT add to the mix...</point>
    <point level="1">AxKit can reduce your long term costs</point>
    <point level="2">In site re-design</point>
    <point level="2">and in content re-purposing</point>
    <point level="1">Open Source equal to commercial alternatives</point>
    </slide>
    
    <slide>
    <title>Resources and contact</title>
    <point level="1">AxKit: http://axkit.org/</point>
    <point level="1">CPAN: http://search.cpan.org</point>
    <point level="1">libxml and libxslt: http://www.xmlsoft.org</point>
    <point level="1">Sablotron: http://www.gingerall.com</point>
    <point level="1">XPath and XSLT Tutorials: http://zvon.org</point>
    </slide>
    
  </slideshow>
  
  
  
  
  1.1                  xml-axkit/axkit.org/docs/xpathscript/guide.dkb
  
  Index: guide.dkb
  ===================================================================
  <?xml version="1.0" encoding="ISO-8859-1"?>
  <?xml-stylesheet href="/stylesheets/docbook_screen.xps" type="application/x-xpathscript"
  title="default"?>
  <?xml-stylesheet href="/stylesheets/docbook_print.xps"
  type="application/x-xpathscript" title="print" alternate="yes"?>
  <!DOCTYPE article [
  <!ENTITY prompt "&#x25; ">
  <!ENTITY XPath "<ulink url='http://www.w3.org/TR/xpath'>XPath</ulink>">
  <!ATTLIST sect1 	id      ID  #IMPLIED>
  <!ATTLIST sect2 	id      ID  #IMPLIED>
  ]>
  <article>
  
  <artheader>
  	<title>XPathScript - A Viable Alternative to XSLT?</title>
  	
  	<author>
  		<firstname>Matt</firstname>
  		<surname>Sergeant</surname>
  		<affiliation>
  			<address><email>matt@sergeant.org</email></address>
  		</affiliation>
  	</author>
  	
  	<copyright>
  		<year>2000</year>
  		<holder role="mailto:matt@sergeant.org">AxKit.com Ltd</holder>
  	</copyright>
  
  	<abstract>
  		<para>This guide gives an introduction to the features of
  		XPathScript, a template processor that is part of <ulink
  		url="http://xml.sergeant.org/axkit/">AxKit</ulink> which provides
  		full programming facilities alongside XPath based node resolution.
  		It also features code / template separation using the
  		ASP <literal>&lt;% %></literal> paradigm.</para>
  	</abstract>
  </artheader>
  
  <!-- ################################### -->
  <sect1>
  <title>Introduction</title>
  <para>
  XPathScript is a stylesheet language for translating XML files into some
  other format. It has only a few features, but by combining those
  features with the power and flexibility of Perl, XPathScript is a very
  capable system. Like all XML stylesheet languages, including XSLT, an
  XPathScript stylesheet is always executed in the context of a source XML
  file. In many cases the source XML file will actually define what
  stylesheets to use via the <literal>&lt;?xml-stylesheet?></literal>
  processing instruction.
  </para>
  <para>
  XPathScript was concieved as part of AxKit - an application server
  environment for Apache servers running mod_perl (XML.com ran my <ulink
  url="http://www.xml.com/pub/a/2000/05/24/axkit/index.html">
  Introduction to AxKit</ulink> article in May). Its
  primary goal was to achieve the sorts of transformations that XSLT can
  do, without being restricted by XSLT's XML based syntax, and to provide
  full programming facilities within that environment. I also wanted
  XPathScript to be completely agnostic about output formats, without
  having to program in special after-effect filters. The result is a
  language for server-side transformation that provides the power and flexibility of
  XSLT combined with the full capabilities of the Perl language, and the
  ability to produce stylesheets in any ASP capable editor or ordinary
  text editor. The above Introduction to AxKit is recommended reading
  before reading this guide.
  </para>
  </sect1>
  
  <!-- ################################### -->
  <sect1>
  <title>The Syntax</title>
  
  <para>
  XPathScript follows the basic ASP syntax of introducing code with the
  <literal>&lt;% %></literal> delimiters. Here's a brief example of a
  fully compatible XPathScript stylesheet:
  <informalexample>
  <programlisting><![CDATA[
  <html>
   <body>
    <%= 5+5 %>
   </body>
  </html> 
  ]]></programlisting>
  </informalexample>
  This simply outputs the value 10 in a HTML document. The delimiters used
  here are the <literal>&lt;%= %></literal> delimiters, which are slightly
  different in that they send the results of the expression to the browser
  (or to the next processing stage in AxKit).
  Of course this example does absolutely nothing with the source XML file
  which is completely separate from this stylesheet. Here's another
  example:
  <informalexample>
  <programlisting><![CDATA[
  <html>
   <body>
    <% $foo = 'World' %>
  Hello
    <%= $foo %> !!!
   </body>
  </html>
  ]]></programlisting>
  </informalexample>
  This outputs the text <literal>"Hello World !!!"</literal>. Again, we're
  not actually doing anything here with our source document, so all XML
  files using this stylesheet will look identical. This seems rather
  uninteresting until we discover the library of functions that are
  accesible to our XPathScript stylesheets for accessing the source
  document contents.
  </para>
  </sect1>
  
  <!-- ################################### -->
  <sect1>
  <title>The XPathScript API</title>
  
  <para>
  Along with the code delimiters XPathScript provides stylesheet
  developers with a full API for accessing and transforming the source XML
  file. This API can be used in conjunction with the delimiters above to
  provide a stylesheet language that is as powerful as XSLT, and yet
  provides all the features of a full programming language (in this case,
  Perl, but I'm certain that other implementations such as Python or Java
  would be possible).
  </para>
  
  <sect2 id="extract_values">
  <title>Extracting Values</title>
  
  <para>
  A simple example to get us started, is to use the API to bring in the
  title from a docbook article. A docbook article title looks like this:
  
  <informalexample>
  <programlisting><![CDATA[
  <article>
   <artheader>
    <title>XPathScript - A Viable Alternative to XSLT?</title>
    ...
  ]]></programlisting>
  </informalexample>
  
  The XPath expression to retrieve the text in the title element is:
  
  <informalexample>
  <programlisting><![CDATA[
  /article/artheader/title/text()
  ]]></programlisting>
  </informalexample>
  
  Putting this all together to make this text into the HTML title we get
  the following XPathScript stylesheet:
  
  <informalexample>
  <programlisting><![CDATA[
  <html>
  	<head>
  		<title><%= findvalue("/article/artheader/title/text()") %></title>
  	</head>
  	<body>
  		This was a DocBook Article. We're only extracting the title for now!
  		<p>
  		The title was: <%= findvalue("/article/artheader/title/text()") %>
  	</body>
  </html>
  ]]></programlisting>
  </informalexample>
  
  </para>
  <para>
  There are lots of features to the expression syntax we used to find that
  "node", and this syntax is called &XPath;. This is a W3C standard for
  finding and matching XML document nodes. The standard is fairly readable
  and is at <ulink
  url="http://www.w3.org/TR/xpath">http://www.w3.org/TR/xpath</ulink>
  alternatively I can recommend <ulink
  url="http://www.arbortext.com/Think_Tank/Norm_s_Column/Issue_One/Issue_One.html"
  >Norm Walsh's XPath introduction</ulink> which covers a slightly older
  version of the specification, but I didn't notice anything in the
  article that is missing or different from the current recommendation.
  </para>
  </sect2>
  
  <sect2>
  <title>Extracting Nodes</title>
  
  <para>
  The above example showed us how to extract single values, but what if we
  have a list of things we wish to extract values from? Here's how we
  might get a table of contents from docbook article sections:
  
  <informalexample>
  <programlisting><![CDATA[
  ...
  <%
  for my $sect1 (findnodes("/article/sect1")) {
  	print $sect1->findvalue("title/text()"), "<br>\n";
  	for my $sect2 ($sect1->findnodes("sect2")) {
  		print " + ", $sect2->findvalue("title/text()"), "<br>\n";
  		for my $sect3 ($sect2->findnodes("sect3")) {
  			print " + + ", $sect3->findvalue("title/text()"), "<br>\n";
  		}
  	}
  }
  %>
  ...
  ]]></programlisting>
  </informalexample>
  
  This gives us a table of contents down to three levels (adding links to
  the actual part of the document is left as an exercise). The first call
  to findnodes gives use all sect1 nodes that are children of the root
  element (article). The &XPath; expressions following that are relative to the
  current node. You can see that by the absence of the leading
  <literal>/</literal>. Again, &XPath; is a very interesting query language,
  and you would be best to visit <ulink
  url="http://www.w3.org/TR/xpath">the XPath specification</ulink> to
  learn more.
  </para>
  <para>
  Note that in the above we don't use the global function findnodes()
  after finding the sect1 nodes, instead we call the node method
  findnodes(), which does exactly the same thing, but makes the node you
  are calling from the context of the XPath expression.
  </para>
  </sect2>
  
  <sect2>
  <title>Declarative Templates</title>
  
  <para>
  The examples up to now have all covered a concept of a single global
  template with a search/replace type functionality from the source XML
  document. This is a powerful concept in itself, especially when combined
  with loops and the ability to change the context of searches. But that
  style of template is limited in utility to well structured data, rather
  than processing large documents. In order to ease the processing of
  documents, XPathScript includes a declarative template processing model
  too, so that you can simply specify the format for a particular element
  and let XPathScript do the work for you.
  </para>
  <para>
  In order to support this method, XPathScript introduces one more API
  function: <literal>apply_templates()</literal>. The name is intended to
  appeal to people already familiar with XSLT. The
  <literal>apply_templates()</literal> function takes either a list of
  start nodes, or an &XPath; expression (that must result in a node set) and
  optional context. Starting at the start nodes it traverses the document
  tree applying the templates defined by the <literal>$t</literal> hash
  reference.
  </para>
  <para>
  First a simple example to introduce this feature. Lets assume for a
  moment that our source XML file is valid XHTML, and we want to change
  all anchor links to italics. Here is the very simple XPathScript
  template that will do that:
  
  <informalexample>
  <programlisting><![CDATA[
  <%
  $t->{'a'}{pre} = '<i>';
  $t->{'a'}{post} = '</i>';
  $t->{'a'}{showtag} = 1;
  %>
  <%= apply_templates() %>
  ]]></programlisting>
  </informalexample>
  
  Note that <literal>apply_templates()</literal> has to be output using
  <literal>&lt;%= %></literal>. That's because
  <literal>apply_templates()</literal> actually outputs a string
  representation of the transformation, it doesn't do the output to the
  browser for you.
  </para>
  <para>
  The first thing this example does is sets up a hash reference
  <literal>$t</literal> that XPathScript knows about (lets call it
  magical). The keys of <literal>$t</literal> are element names (including
  namespace prefix if we are using namespaces). The hash can have the
  following sub-keys:
  <itemizedlist>
  	<listitem><literal>pre</literal></listitem>
  	<listitem><literal>post</literal></listitem>
  	<listitem><literal>showtag</literal></listitem>
  	<listitem><literal>testcode</literal></listitem>
  </itemizedlist>
  We'll cover <literal>testcode</literal> in more depth later in <xref
  linkend="template_sect"/>, but for now know that it is a place holder
  for code that allows for more complex templates.
  </para>
  <para>
  Unlike XSLT's declarative transformation syntax, the keys of
  <literal>$t</literal> do <emphasis>not</emphasis> specify &XPath; match
  expressions. Instead they are simple element names. This is a trade off
  of speed of execution over flexibility. Perl hash lookups are extremely
  quick compared to XPath matching. Luckily because of the
  <literal>testcode</literal> option, more complex matches are quite
  possible with XPathScript.
  </para>
  <para>
  The simple explanation for now is that <literal>pre</literal> specifies
  output to appear before the tag, <literal>post</literal> specifies
  output to appear after the tag, and <literal>showtag</literal> specifies
  that the tag itself should be output as well as the pre and post values.
  </para>
  </sect2>
  </sect1>
  
  <!-- ################################### -->
  <sect1>
  <title>A Complete Example</title>
  
  <para>
  Now lets put all of these ideas together into a (almost) complete
  example. This is part of the stylesheet I use to process my docbook articles
  online:
  
  <informalexample>
  <programlisting><![CDATA[
  <!--#include file="docbook_tags.xps"-->
  <%
  
  my %links;
  my $linkid = 0;
  $t->{'ulink'}{testcode} = sub { 
  		my $node = shift;
  		my $t = shift;
  		my $url = findvalue('@url', $node);
  		if (!exists $links{$url}) {
  			$linkid++;
  			$links{$url} = $linkid;
  		}
  		my $link_number = $links{$url};
  		$t->{pre} = "<i><a href=\"$url\">";
  		$t->{post} = " [$link_number]</a></i>";
  		return 1;
  	};
  
  %>
  <html>
  <head>
  	<title><%= findvalue('/article/artheader/title/text()') %></title>
  </head>
  <body bgcolor="white">
  
  <%
  # display title/TOC page
  print apply_templates('/article/artheader/*');
  %>
  
  <hr>
  
  <%
  # display particular page
  foreach my $section (findnodes("/article/sect1")) {
  	print apply_templates($section);
  }
  %>
  
  <h1>List of Links</h1>
  <table border="1">
  <th>URL</th>
  <%
  for my $link (sort {$links{$a} <=> $links{$b}} keys %links) {
  %>
  <tr>
  <td><%= "[$links{$link}] $link" %></td>
  </tr>
  <% } %>
  </table>
  
  </body>
  </html>
  ]]></programlisting>
  </informalexample>
  
  The very first line there imports a library of tags that are shared
  between this stylesheet, and one that is easier for web viewing with
  clickable links between sections (which can be downloaded <ulink
  url="http://xml.sergeant.org/docbook_screen.xps">here</ulink>). The
  import system is based on Server Side Includes (SSI) although only SSI
  file includes are supported at this time (SSI virtual includes can be
  implemented using mod_include). Here is part of the docbook_tags.xps
  file:
  
  <informalexample>
  <programlisting><![CDATA[
  <%
  
  $t->{'attribution'}{pre} = "<i>";
  $t->{'attribution'}{post} = "</i><br>\n";
  
  $t->{'para'}{pre} = '<p>';
  $t->{'para'}{post} = '</p>';
  
  $t->{'ulink'}{testcode} = sub { 
  		my $node = shift;
  		my $t = shift;
  		$t->{pre} = "<i><a href=\"" .
  						findvalue('./@url', $node) . "\">";
  		$t->{post} = '</a></i>';
  		return 1;
  	};
  
  $t->{'title'}{testcode} = sub { 
  		my $node = shift;
  		my $t = shift;
  		if (findvalue('parent::blockquote', $node)) {
  			$t->{pre} = "<b>";
  			$t->{post} = "</b><br>\n";
  		}
  		elsif (findvalue('parent::artheader', $node)) {
  			$t->{pre} = "<h1>";
  			$t->{post} = "</h1>";
  		}
  		else {
  			my $parent = findvalue('name(..)', $node);
  			if (my ($level) = $parent =~ m/sect(\d+)$/) {
  				$t->{pre} = "<h$level>";
  				$t->{post} = "</h$level>";
  			}
  		}
  
  		return 1;
  	};
  
  %>
  ]]></programlisting>
  </informalexample>
  </para>
  <para>
  We go into detail of what is happening in this example in the next
  section.
  </para>
  </sect1>
  
  <!-- ################################### -->
  <sect1>
  <title>Stepping Through the Example</title>
  
  <para>
  Careful readers will note that the first thing we see is a
  <literal>$t</literal> specification for <literal>&lt;ulink></literal>
  tags, and you'll also note that the included
  <filename>docbook_tags.xps</filename> contains a specification for
  <literal>&lt;ulink></literal>. The reason is to override the default
  behaviour for ulink tags in the print version of my articles to contain
  a reference that we can use later in a list of links. We can also see
  that this specification uses a <literal>testcode</literal> parameter
  that we haven't encountered before. We'll see how and why that's used
  later in <xref linkend="template_sect"/>.
  </para>
  <para>
  Next we see the <literal>findvalue()</literal> function used exactly as
  we already saw in <xref linkend="extract_values"/>.
  </para>
  <para>
  Then we have a section with a comment marked: "display Title/TOC page".
  This uses the <literal>apply_templates()</literal> function with an
  &XPath; expression. Note that rather than use the <literal>&lt;%=
  %></literal> delimiters around the apply_templates() call, we simply use
  the print function. This has the same effect, and is used here to show
  the flexibility in this approach.
  </para>
  <para>
  The main part of the code loops through all sect1 tags, and calls
  apply_templates on those nodes. Note how this is another demonstration
  of Perl's TMTOWTDI (There's More Than One Way To Do It) approach - the
  same code could have been written:
  
  <informalexample>
  <programlisting><![CDATA[
  <%= apply_templates("/article/sect1") %>
  ]]></programlisting>
  </informalexample>
  
  </para>
  <para>
  Finally, because this is the print version of our article, we provide a
  list of links so that people viewing a printed version of this article
  can type in those links, and they can also refer to the link by
  reference number, as we saw earlier. We use the hash of links in the
  <literal>%links</literal> variable that we built in the
  <literal>testcode</literal> handler for our <literal>ulink</literal>
  template.
  </para>
  
  <para>
  The other file, <filename>docbook_tags.xps</filename>, is included only
  in part here, to demonstrate a few of the transformations we're applying
  to various docbook article tags. We can see that we're turning
  <literal>&lt;para></literal> tags into <literal>&lt;p></literal> tags,
  and doing some more complex processing with <literal>testcode</literal>
  to <literal>&lt;title></literal> tags. We'll see in <xref
  linkend="template_sect"/> exactly what <literal>testcode</literal>
  allows us to achieve.
  </para>
  
  </sect1>
  
  
  <!-- ################################### -->
  <sect1 id="template_sect">
  <title>The Template Hash</title>
  <para>
  The <literal>apply_templates()</literal> function iterates over the
  nodes of your XML file applying the templates in the
  <literal>$t</literal> hash reference. This is the most important feature
  of XPathScript, because it allows you to define the appearance for
  individual tags without having to do it programmatically. This is the
  declarative part of XPathScript. There is an important point to make
  here: XSLT is a purely declarative syntax, and people are having to work
  procedural code into XSLT via work arounds. XPathScript takes a much
  more pragmatic approach (much like Perl itself) - it is both declarative
  and procedural, allowing you the flexibility to use real code for real
  problems. It is important to note that apply_templates returns a string,
  so you must either use <literal>print apply_templates()</literal> if
  using it from a Perl section of code, or via <literal>&lt;%=
  apply_templates() %></literal>.
  </para>
  <para>
  The keys of <literal>$t</literal> are the names of the elements, including namespace
  prefixes. When you call <literal>apply_templates()</literal>, every
  element visited is looked up in the <literal>$t</literal> hash, and the
  template items stored in that hash are applied to the node. It's worth
  noting at this point, that unlike XSLT, XPathScript does not perform
  tree transformations from one tree to another. It simply sends its
  output to the browser directly. This has advantages and disadvantages,
  but they are beyond the scope of this guide.
  </para>
  <para>
  The following sub-keys define the transformation:
  <itemizedlist>
  	<listitem><literal>pre</literal> - the output to occur before the
  	tag.</listitem>
  	<listitem><literal>post</literal> - the output to occur after the
  	tag.</listitem>
  	<listitem><literal>prechildren</literal> - the output to occur before
  	the children of this tag are output.</listitem>
  	<listitem><literal>postchildren</literal> - the output to occur after
  	the children of this tag are output.</listitem>
  	<listitem><literal>prechild</literal> - the output to occur before
  	each child of this tag.</listitem>
  	<listitem><literal>postchild</literal> - the output to occur after
  	each child of this tag.</listitem>
  	<listitem><literal>showtag</literal> - set to a true value to display
  	the tag as well as the pre and post values. If unset or false the tag
  	itself is not displayed.</listitem>
  	<listitem><literal>testcode</literal> - code to execute upon visiting
  	this tag. See below.</listitem>
  </itemizedlist>
  The showtag option is mostly equivalent to the XSLT
  <literal>&lt;xsl:copy></literal> tag, only less verbose. The pre and post
  options are useful because generally in transformations we want to
  specify what comes before and after a tag. For example, to change an
  HTML A tag to be in italics, but still have the link, we would use the
  following:
  <informalexample>
  <programlisting><![CDATA[
  $t->{A}{pre} = "<i>";
  $t->{A}{post} = "</i>";
  $t->{A}{showtag} = 1;
  ]]></programlisting>
  </informalexample>
  </para>
  
  <sect2>
  <title>"testcode"</title>
  
  <para>
  The <literal>testcode</literal> option is where we perform really
  powerful transformations. Its how we can do more complex tests on the
  node that are available in XPath, and locally modify the transformation
  based on what we find.
  </para>
  <para>
  The value stored in <literal>testcode</literal> is simply a reference to
  a subroutine. In Perl these are incredibly simple to create using the
  anonymous sub keyword (note that these are often erroneously called
  closures, but they only become closures if they reference a lexical
  variable outside the scope of the subroutine itself). The sub is called
  every time one of these elements is visited. The subroutine is passed two parameters:
  The node itself, and an empty hash reference that you can populate using
  the <literal>pre</literal>, <literal>post</literal>,
  <literal>prechildren</literal>, <literal>prechild</literal>,
  <literal>postchildren</literal>, <literal>postchild</literal> and
  <literal>showtag</literal> values that we've discussed already. Unlike
  the global <literal>$t</literal> hashref you don't have to first
  specify the element name as a key. Here's the
  <literal>&lt;ulink></literal> example from the global tags code above:
  <informalexample>
  <programlisting><![CDATA[
  $t->{'ulink'}{testcode} = sub { 
  	my ($node, $t) = @_;
  	$t->{pre} = '<i><a href="' . findvalue('@url', $node) . '">';
  	$t->{post} = '</a></i>';
  	return 1;
  };
  ]]></programlisting>
  </informalexample>
  The equivalent XSLT code looks like this:
  <informalexample>
  <programlisting><![CDATA[
  <xsl:template match="ulink">
  	<i><a>
  		<xsl:attribute name="href">
  			<xsl:value-of select="@url"/>
  		</xsl:attribute>
  		<xsl:apply-templates/>
  	</a></i>
  </xsl:template>
  ]]></programlisting>
  </informalexample>
  Note in the XPathScript above that the inner <literal>$t</literal> is
  lexically scoped, so changes to it don't affect the outer
  <literal>$t</literal>. To save some confusion we might have named that
  variable <literal>$localtransforms</literal>, but some people like
  myself hate typing... ;-)
  </para>
  <para>
  The return value from the testcode is also important. A return value of
  1 means to process this node and continue processing all the children of
  this node. A return value of -1 means to process this node and stop, and
  a return value of 0 means do not process this node at all. This is
  useful in conditional tests, where you may not wish to process the nodes
  under certain conditions. You may also use a return code of a consisting
  of a string that is an XPath expression. See <xref
  linkend="mini_reference"/> for more information.
  </para>
  <para>
  It is important to note that we can do things here based on XPath
  lookups just as we can in XSLT. While it is a little more verbose than a
  simple XSLT pattern match, the trade off is in performance. An example
  is in XSLT you might match <literal>artheader/title</literal> and
  elsewhere you might match <literal>title[name(..) != "artheader"</literal>. 
  In XPathScript we can only match "title" in the
  template hash. But we can use the testcode section to extend the match:
  <informalexample>
  <programlisting><![CDATA[
  $t->{'title'}{testcode} = sub { 
  	my $node = shift;
  	my $t = shift;
  	if (findvalue('parent::blockquote', $node)) {
  		$t->{pre} = "<b>";
  		$t->{post} = "</b><br>\n";
  	}
  	elsif (findvalue('parent::artheader', $node)) {
  		$t->{pre} = "<h1>";
  		$t->{post} = "</h1>";
  	}
  	else {
  		my $parent = findvalue('name(..)', $node);
  		if (my ($level) = $parent =~ m/sect(\d+)$/) {
  			$t->{pre} = "<h$level>";
  			$t->{post} = "</h$level>";
  		}
  	}
  
  	return 1;
  };
  ]]></programlisting>
  </informalexample>
  Here we check what the parent node is before performing our modification
  to the local <literal>$t</literal> hashref. Specifically note the
  utility of being able to perform Perl regular expressions to extract
  values.
  </para>
  </sect2>
  
  <sect2>
  <title>Copying styles</title>
  <para>
  One really neat feature of XPathScript that is really hard to do with
  XSLT is to be able to copy a style completely:
  <informalexample>
  <programlisting><![CDATA[
  <%
  $t->{'foo'}{pre} = "<i>";
  $t->{'foo'}{post} = "</i>";
  $t->{'foo'}{showtag} = 1;
  
  $t->{'bar'} = $t->{'foo'};
  %>
  ]]></programlisting>
  </informalexample>
  While this would be possible in XSLT using entities, it's certainly not
  very practical or neat. With XPathScript many tags can share the same
  template. Be careful though - this is a reference copy, not a deep copy,
  so the following may not do what you think it should:
  <informalexample>
  <programlisting><![CDATA[
  <%
  $t->{'foo'}{pre} = "<i>";
  $t->{'foo'}{post} = "</i>";
  $t->{'foo'}{showtag} = 1;
  
  $t->{'bar'} = $t->{'foo'};
  $t->{'bar'}{post} = "</i><br>";
  %>
  ]]></programlisting>
  </informalexample>
  Because this is a reference, the last line there changes the values for
  'foo' as well as 'bar'.
  </para>
  </sect2>
  
  <sect2>
  <title>A "Catch All"?</title>
  <para>
  Does XPathScript have a "catch all" option for elements that I don't
  have a <literal>$t</literal> entry for? Yes, of course! Simply set
  <literal>$t->{'*'}</literal> to the template you want to execute. You
  can even do some really clever things, such as using the
  <literal>testcode</literal> section to output a warning to the Apache
  error log about an unrecognised tag, rather than having to place some
  output in the resulting document and bother your users!
  </para>
  <para>
  This feature was introduced in AxKit 0.94.
  </para>
  
  </sect2>
  
  <sect2>
  <title>Interpolation</title>
  
  <para>
  Adding attributes or other data into the translated nodes is non-trivial
  using this setup. It requires you to drop down into testcode. Here's an
  example of turning <literal>&lt;link url="..."></literal> tags into
  HTML <literal>&lt;a></literal> tags:
  <informalexample>
  <programlisting><![CDATA[
  <%
  $t->{'link'}{testcode} = sub {
    my ($node, $t) = @_;
    $t->{pre} = '<a href="' . $node->findvalue('@url') . '">';
    $t->{post} = '</a>';
    return 1;
  };
  %>
  ]]></programlisting>
  </informalexample>
  This is obviously rather verbose.
  </para>
  
  <para>
  To make this a little simpler, in XPathScript as of AxKit 1.1, we have
  introduced interpolation of the replacement strings, much the same as
  you can do with XSLT attributes. Here is the appropriate
  <literal>$t</literal> entry as of AxKit 1.1:
  <informalexample>
  <programlisting><![CDATA[
  <%
  $t->{'link'}{pre} = '<a href="{@url}">';
  $t->{'link'}{post} = '</a>';
  %>
  ]]></programlisting>
  </informalexample>
  The curly brackets <literal>{}</literal> delimit an XPath expression on
  which findvalue is called using the current node as the context. Any
  XPath expression should be valid within those delimiters.
  </para>
  
  <para>
  As a backwards compatibility measure, and to ensure efficiency is
  defaulted, interpolation only occurs when you have the following
  somewhere in your Apache configuration defined for the current request:
  <informalexample>
  <programlisting><![CDATA[
  PerlSetVar AxXPSInterpolate 1
  ]]></programlisting>
  </informalexample>
  You can also turn off interpolation temporarily in your script using the
  global variable <literal>$XPathScript::DoNotInterpolate</literal>. Set
  that to a true value to turn off interpolation. Be careful to only do
  that locally (using the perl <literal>local</literal> keyword) to ensure
  it doesn't remain set for the next invocation of the script.
  </para>
  
  </sect2>
  
  </sect1>
  
  <!-- ################################### -->
  <sect1>
  <title>Writing Dynamic Content</title>
  
  <para>
  Because XPathScript has full access to all the perl builtins, you can
  very easily create dynamic content with XPathScript. There is only 1
  caveat though: The AxKit cache works on the basis of the timestamp of
  the original XML file. This means that your XPathScript code will only
  be executed when the XML resource that is being requested actually
  changes.
  </para>
  <para>
  To work around this limitation you simply need to tell AxKit that this
  stylesheet contains dynamic content, and therefore the output should not
  be cached. The syntax for this duplicates the Apache API for telling
  proxy servers not to cache the output:
  <informalexample>
  <programlisting>
  &lt;%
  ...
  
  $r->no_cache(1);
  
  ...
  %>
  </programlisting>
  </informalexample>
  </para>
  </sect1>
  
  <!-- ################################### -->
  <sect1 id="mini_reference">
  <title>An XPathScript Mini-Reference</title>
  
  <para>
  Code is separated from output in XPathScript using the <literal>&lt;%
  %></literal> delimiters.
  </para>
  <para>
  Perl expression results can be sent to the browser either using
  <literal>print()</literal> if inside a <literal>&lt;% %></literal>
  section, or via <literal>&lt;%= <emphasis>code</emphasis> %></literal>.
  </para>
  <para>
  The following XPath functions are imported for your use:
  <itemizedlist>
  	<listitem><literal>findnodes($path, [$context])</literal></listitem>
  	<listitem><literal>findvalue($path, [$context])</literal></listitem>
  	<listitem><literal>findnodes_as_string($path, [$context])</literal></listitem>
  	<listitem><literal>apply_templates( $path, [$context])</literal></listitem>
  	<listitem><literal>apply_templates( @nodes )</literal></listitem>
  	<listitem><literal>import_template( $uri )</literal></listitem>
  </itemizedlist>
  </para>
  <para>
  The first three methods are documented more completely in the XML::XPath manual
  pages.
  </para>
  <para>
  Apply templates examines the contents of the local <literal>$t</literal>
  hash reference for elements names. For example, when encountering a
  <literal>&lt;foo></literal> element via apply_templates, XPathScript
  will try to find a transformation hash in the key
  <literal>$t->{'foo'}</literal>.
  </para>
  <para>
  Import template can be used to pull in an external XPathScript template
  file.  <literal>$uri</literal> should be a path to the stylesheet to be
  included.  The function returns an anonymous subroutine that when
  executed will run the stylesheet.  The anonymous subroutine takes two
  arguments, which makes it ideal to plug into a
  <literal>testcode</literal> entry, for example:
  <informalexample>
  <programlisting>
  $t->{BODY}{testcode} = import_template("/xps/bodystyle.xps");
  </programlisting>
  </informalexample>
  Inside the imported stylesheet, you will be referencing the same
  <literal>$t</literal> as the parent stylesheet.  You can get at the
  usual <literal>testcode</literal> version of <literal>$t</literal> by
  using <literal>$real_local_t</literal>.
  </para>
  <para>
  If you want to include a stylesheet anyway (not as part of a testcode
  setup), just write it as normal, and include a line like this in the
  parent stylesheet:
  <informalexample>
  <programlisting>
  import_template("/xps/bodystyle.xps")->();
  </programlisting>
  </informalexample>
  </para>
  <para>
  The value in <literal>$t->{'foo'}</literal> above is a hash reference
  with the following optional keys:
  <itemizedlist>
  	<listitem>pre</listitem>
  	<listitem>post</listitem>
  	<listitem>prechildren</listitem>
  	<listitem>postchildren</listitem>
  	<listitem>prechild</listitem>
  	<listitem>postchild</listitem>
  	<listitem>showtag</listitem>
  	<listitem>testcode</listitem>
  </itemizedlist>
  </para>
  <para>
  If a value is <emphasis>not</emphasis> found in <literal>$t</literal> for 
  the current element, then the element is output verbatim, and 
  apply_templates performed on all its children. Except in the case where
  a <literal>$t->{'*'}</literal> value exists, which is a "catchall"
  transformation specification. This might be a useful place to add some
  testcode to output a warning to the error log.
  </para>
  <para>
  If a value <emphasis>is</emphasis> found in <literal>$t</literal> for
  the current element then the tag itself is not displayed unless
  <literal>$t->{&lt;element_name>}{showtag}</literal> is set to a true
  value.
  </para>
  <para>
  <literal>testcode</literal> is a reference to a subroutine (often
  constructed as an anonymous subroutine). The subroutine is called with
  two parameters: The current node and a localised hash reference to store
  new transformations for this node and this node only. The return value
  from this subroutine must be one of:
  <itemizedlist>
  	<listitem><literal>1</literal> - process this node and all children</listitem>
  	<listitem><literal>-1</literal> - process this node but not the children of this
  	node</listitem>
  	<listitem><literal>0</literal> - do not process this node or its children</listitem>
  	<listitem><literal>'string'</literal> - any string (other than
  	<literal>"1"</literal>, <literal>"0"</literal> or <literal>"-1"</literal>)
  	is equivalent to <literal>1</literal>, except rather than processing
  	the node's children, it processes the nodes found by executing
  	<literal>findnodes('string', $node)</literal> where
  	<literal>$node</literal> is the current node. Obviously
  	<literal>'string'</literal> has to be a valid XPath expression.</listitem>
  </itemizedlist>
  </para>
  <para>
  XPathScript stylesheets can be modularised using SSI #include
  directives. The code in #included files is added verbatim into the
  current code at the position of the include. This allows you to use this
  fact to override defaults (as we saw in the first example where the
  template for ulink is overridden).
  </para>
  </sect1>
  
  <!-- ################################### -->
  <sect1>
  <title>Using XPathScript to Write XSP TagLibs</title>
  <para>
  XSP is an alternative server side XML programming API. It is not a
  stylesheet system though - the XSP page is executed directly without a
  stylesheet. XSP was originally incorporated into the <ulink
  url="http://xml.apache.org/cocoon">Cocoon</ulink> application
  framework, and AxKit included XSP capabilities because it's a very
  interesting and useful tool.
  </para>
  <para>
  One of the interesting things about XSP is the ability to write taglibs
  using some form of stylesheet transformation language. A taglib is a
  separate sheet of tags that have special meaning to your code. They can
  execute external functions or simply be used in a similar way to
  external parsed entities. Here's the classic example of a usage of a
  taglib from the Cocoon documentation (slightly modified from the
  original):
  <informalexample>
  <programlisting><![CDATA[
  <xsp:page
  language="Perl"
  xmlns:xsp="http://www.apache.org/1999/XSP/Core"
  xmlns:example="http://www.plenix.com/DTD/XSP/Example"
  >
  <page title="Time of Day">
  <p>
    To the best of my knowledge, it's now
    <!-- Substitute time of day here -->
    <example:time-of-day format="%y/%m/%d %r"/>
  </p>
  </page>
  </xsp:page>
  ]]></programlisting>
  </informalexample>
  </para>
  <para>
  Here the <literal>&lt;example:time-of-day></literal> tag gets converted
  at run time to the current time using the <literal>strftime</literal>
  format specified in the format attribute.
  </para>
  <para>
  A taglib implementation is a stylesheet that is evaluated against this
  file prior to passing it to the XSP processor. The stylesheet converts
  the tags that it recognises into pure XSP code (see <ulink
  url="http://xml.apache.org/cocoon/xsp.html">http://xml.apache.org/cocoon/xsp.html</ulink>
  for more information on XSP). While this seems a rather redundant
  feature, it allows even further separation between code and design.
  Designers can just introduce these special tags, without worrying about
  the logic behind them.
  </para>
  <para>
  The Cocoon recommendation is to write taglibs using XSLT. This works
  well, but the code often looks confusing. My recommendation for AxKit is
  to use XPathScript. Here's our implementation of the time-of-day tag
  using XPathScript:
  <informalexample>
  <programlisting><![CDATA[
  <%
  $t->{'xsp:page'}{prechildren} = <<EOXML;
  <xsp:structure>
  	<xsp:include>POSIX</xsp:include>
  </xsp:structure>
  EOXML
  
  $t->{'example:time-of-day'}{testcode} = sub {
          my ($node, $t) = @_;
          $t->{pre} = 
  '<xsp:expr>
  	POSIX::strftime("' . findvalue('@format', $node) . '", localtime)
  </xsp:expr>';
          return 1;
      };
  %>
  <%= apply_templates() %>
  ]]></programlisting>
  </informalexample>
  This is a rather trivial example of a taglib, but hopefully it
  introduces the possibilities of further extending your tag library.
  </para>
  <para>
  In order to enable this tag library, we simply make the taglib
  stylesheet the first in our stylesheet cascade:
  <informalexample>
  <programlisting><![CDATA[
  <?xml version="1.0"?>
  <?xml-stylesheet type="application/x-xpathscript" href="example.taglib"?>
  <?xml-stylesheet type="application/x-xsp" href="."?>
  <?xml-stylesheet type="text/xsl" href="example.xsl"?>
  <xsp:page
  language="Perl"
  xmlns:xsp="http://www.apache.org/1999/XSP/Core"
  xmlns:example="http://www.plenix.com/DTD/XSP/Example"
  >
  <page title="Time of Day">
    <p>
      To the best of my knowledge, it's now
      <!-- Substitute time of day here -->
      <example:time-of-day format="%y/%m/%d %r"/>
    </p>
  </page>
  </xsp:page>
  ]]></programlisting>
  </informalexample>
  Note that the XSP script is executed using the stylesheet processing
  instruction, with a stylesheet of ".". This stylesheet could be anything
  in the case of XSP, since there is actually no stylesheet associated
  with it, and the "." is merely a convention.
  </para>
  <para>
  For comparison, here's the equivalent XSLT based taglib:
  <informalexample>
  <programlisting><![CDATA[
  <?xml version="1.0"?>
  <xsl:stylesheet xmlns:xsl="http://www.w3.org/XSL/Transform/1.0"
    xmlns:xsp="http://www.apache.org/1999/XSP/Core"
    xmlns:example="http://www.plenix.com/DTD/XSP/Example"
  >
    <xsl:template match="xsp:page">
      <xsp:page>
        <xsl:copy>
          <xsl:apply-templates select="@*"/>
        </xsl:copy>
        <xsp:structure>
          <xsp:include>POSIX</xsp:include>
        </xsp:structure>
        <xsl:apply-templates/>
      </xsp:page>
    </xsl:template>
  
    <xsl:template match="example:time-of-day">
      <xsp:expr>
  			POSIX::strftime("<xsl:value-of select="@format"/>", localtime)
      </xsp:expr>
    </xsl:template>
  
    <xsl:template match="@*|node()" priority="-1">
      <xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy>
    </xsl:template>
  
  </xsl:stylesheet>
  ]]></programlisting>
  </informalexample>
  Some people may find one version easier to work with than the other,
  although I personally prefer the simplicity of XPathScript.
  </para>
  </sect1>
  </article>
  
  
  
  1.1                  xml-axkit/axkit.org/docs/xsp/guide.dkb
  
  Index: guide.dkb
  ===================================================================
  <?xml version="1.0" encoding="ISO-8859-1"?>
  <?xml-stylesheet href="/stylesheets/docbook_screen.xps" type="application/x-xpathscript"
  title="default"?>
  <?xml-stylesheet href="/stylesheets/docbook_print.xps"
  type="application/x-xpathscript" title="print" alternate="yes"?>
  <!DOCTYPE article [
  <!ENTITY prompt "&#x25; ">
  ]>
  <article>
  
  <artheader>
  	<title>Using XSP in AxKit</title>
  	
  	<author>
  		<firstname>Matt</firstname>
  		<surname>Sergeant</surname>
  		<affiliation>
  			<address><email>matt@sergeant.org</email></address>
  		</affiliation>
  	</author>
  	
  	<copyright>
  		<year>2000</year>
  		<holder role="mailto:matt@sergeant.org">AxKit.com Ltd</holder>
  	</copyright>
  
  	<abstract>
  		<para>This article covers the XSP technology within AxKit, how to
  		get started using XSP to create dynamic pages, and a full guide to
  		the XSP syntax.</para>
  	</abstract>
  </artheader>
  
  <sect1>
  <title>Introduction</title>
  <para>
  XSP is a server-side technology for embedding code into XML. It belongs
  to the same family of products as ASP and JSP. However XSP focuses
  completely on XML, and contains methods for generating XML that make
  life for the XML programmer easier. XSP is designed to work in the early
  stages of the XML processing pipeline, often as the very first stage in
  the processing pipeline. Generally the output from XSP goes on to
  further stage processing to deliver HTML or WAP. XSP also has powerful
  database tools that deliver their output in XML for further
  manipulation.
  </para>
  <para>
  A simple XSP example that generates the current time looks like this:
  <informalexample>
  <programlisting><![CDATA[
  <xsp:page xmlns:xsp="http://www.apache.org/1999/XSP/Core"
  	xmlns="uri://axkit.org/NS/MyHomePage"
  	language="Perl">
  	<page>
  		<body>
  			<heading>Hello World</heading>
  			It is now: <xsp:expr>scalar localtime</xsp:expr>
  		</body>
  	</page>
  </xsp:page>
  ]]></programlisting>
  </informalexample>
  </para>
  <para>
  And the output from that example is:
  <informalexample>
  <programlisting><![CDATA[
  <page xmlns="uri://axkit.org/NS/MyHomePage">
  	<body>
  		<heading>Hello World</heading>
  		It is now: Sat Jun 24 10:46:51 2000
  	</body>
  </page>
  ]]></programlisting>
  </informalexample>
  </para>
  <para>
  While this isn't going to be displayable by any current browser (unless
  we're talking about client side XSLT or even CSS), AxKit pipes this into
  the next stage of transformation, which is likely to be an XSLT
  processor, to generate HTML or any other format.
  </para>
  <para>
  Using the XSP constructs, like <literal>&lt;xsp:expr></literal> we can build up
  complex rules and code structures to generate the desired output. XSP
  includes several tags for making life easier for generating XML, as
  we'll see later.
  </para>
  </sect1>
  <sect1>
  <title>The Elements of XSP</title>
  <para>
  We've already mentioned that XSP includes tags for generating XML, and
  it should be noted that the above <literal>&lt;xsp:expr></literal> does
  not generate text, but a DOM text node. In this section we'll see what
  tags XSP offers to us, and later we'll see how to put them all together.
  </para>
  <para>
  First though, its important to know a little bit about how XSP works
  behind the scenes withing AxKit. Astute readers will have noted that the
  <literal>&lt;xsp:page></literal> tag included a
  <literal>language="Perl"</literal> attribute. This is because XSP is
  language independant. While AxKit only implements XSP with the Perl
  language, <ulink url="http://xml.apache.org/cocoon">Cocoon</ulink> has
  implemented support for Java and Javascript, and other languages are
  coming online with the Cocoon project soon.
  </para>
  <para>
  When AxKit parses the XSP page it generates a Perl class - an object
  oriented Perl module. This class can be considered to have three main
  sections:
  <itemizedlist>
  	<listitem>structure - this currently only defines libraries that your
  	XSP code might use, although it is left open for other structural
  	items</listitem>
  	<listitem>logic - this section is for any logic in addition to that
  	which you wish to embed directly within the output. This generally
  	includes function definitions and global variables (although see <xref
  	linkend="global_vars_not_global"/> for some important information on
  	global variables)</listitem>
  	<listitem>content - the main section of XSP occurs
  	as soon as the XSP parser sees a non-XSP tag (which is
  	called the "User root" tag).</listitem>
  </itemizedlist>
  A breakdown of how this might look to a perl programmer is as follows:
  <informalexample>
  <programlisting><![CDATA[
  package MyModule;
  
  # begin structure
  use MyLib;
  # end structure
  
  # begin logic
  sub foo {
  	return "bar";
  }
  # end logic
  
  # begin content
  sub handler {
  	# generate DOM tree of content output
  	...
  }
  # end content
  1;
  ]]></programlisting>
  </informalexample>
  </para>
  <para>
  When an XSP page is executed, assuming the code is already compiled,
  AxKit attempts to execute the <literal>handler()</literal> function.
  </para>
  <para>
  Now that we know what sort of code is behind the execution of XSP, lets
  get deeper into those sections.
  </para>
  <sect2>
  <title>Structure</title>
  <para>
  Structure tags may appear only between the root
  <literal>&lt;xsp:page></literal> tag and  the user root tag. The syntax
  of the structure tag is:
  <informalexample>
  <programlisting><![CDATA[
  <xsp:structure>
  	<xsp:include>MyLib</xsp:include>
  </xsp:structure>
  ]]></programlisting>
  </informalexample>
  </para>
  <para>
  This adds the following to the structure section of the compiled class:
  <informalexample>
  <programlisting>
  use MyLib;
  </programlisting>
  </informalexample>
  </para>
  <para>
  Currently <literal>&lt;xsp:include></literal> is the only tag provided
  for use within the structure section.
  </para>
  </sect2>
  
  <sect2>
  <title>Logic</title>
  <para>
  The logic section is freeform, and should generally be used along with
  an accompanying <literal>&lt;![CDATA[...]]&gt;</literal> declaration to
  ensure that your code is not treated as XML. Note that unlike some
  languages, perl may be particularly prone to having the string
  <literal>"]]&gt;"</literal> somewhere in the code, so be aware of that as
  you work in CDATA sections. A simple solution is to change that to
  <literal>"]] &gt;"</literal> - note the extra space.
  </para>
  <para>
  An example of a logic section might be the definition of a function for
  providing the current time:
  <informalexample>
  <programlisting><![CDATA[
  <xsp:structure>
  	<xsp:include>Time::Object</xsp:include>
  </xsp:structure>
  
  <xsp:logic><![CDATA[
  sub mytime {
  	my ($time) = @_;
  	$time ||= time;
  	return Time::Object->new($time);
  }
  ]]>]]&gt;<![CDATA[</xsp:logic>
  ]]></programlisting>
  </informalexample>
  </para>
  </sect2>
  
  <sect2>
  <title>Content</title>
  <para>
  The content section consists of automatically generated code based on the
  non-xsp tags in the document, along with code that has been generated
  based on the xsp tags.
  </para>
  <para>
  Currently XSP is DOM based (there are plans to make a SAX based XSP for
  the obvious performance benefits, but that hasn't been realised yet),
  and as such for each non-xsp entity in the XML resource, the XSP parser
  generates code that will create a DOM node. When it sees a tag it
  generates code to make an element node, when it sees text it generates
  code to make a text node, and so on. The idea though is not to worry
  about the code that the XSP parser is generating, and focus on the
  output that you want.
  </para>
  <para>
  Since the content section is where most of the work and most of the
  important parts of your code will occur, we've reserved a whole section
  of this document to talk about it.
  </para>
  </sect2>
  
  </sect1>
  
  <sect1>
  <title>XSP's XML building tags, or "the Content section"</title>
  
  <para>
  Everything in XSP works around namespaces, so be sure you understand
  them before trying to start working with XSP. A good place to learn
  about namespaces is <ulink url="http://www.xml.com/">XML.com</ulink>,
  where they have some excellent links to namespaces resources, and some
  good articles.
  </para>
  <para>
  As soon as XSP sees an element that does not belong to the XSP namespace
  it knows to
  start the "content section". This is the main part of the code that
  generates XML nodes for passing to the next processing stage. Every node
  prior to the user root node is special to XSP (because by definition,
  they are part of the XSP namespace). It is also worth noting
  here that an actual <literal>&lt;xsp:page></literal> root element is not
  a requirement of XSP, however without it there's no way to create logic
  or structure sections.
  </para>
  
  <sect2>
  <title>Generating Elements and Attributes</title>
  <para>
  Any non XSP tags are generally passed on to the next processing stage
  verbatim. However it is possible to use logic to determine whether or
  not certain tags appear:
  <informalexample>
  <programlisting><![CDATA[
  <xsp:logic>
  	if (somecondition()) {
  <xsp:content>
  		<true_tag/>
  </xsp:content>
  	} else {
  <xsp:content>
  		<false_tag/>
  </xsp:content>
  	}
  </xsp:logic>
  ]]></programlisting>
  </informalexample>
  This example is based on the familiar concept that was brought to use by
  ASP and similar server side scripting languages, where we must break out
  of the code section in order to send tags to the browser. The
  <literal>&lt;xsp:content></literal> tag allows us to temporarily break
  out of the code section that is provided by
  <literal>&lt;xsp:logic></literal>. Note that <literal>&lt;xsp:logic></literal> is the same tag as we
  used outside of the user root element to provide class level logic.
  </para>
  <para>
  This is rather verbose, so there's another way to achieve the same
  effect, and this is using the XSP element generator tags:
  <informalexample>
  <programlisting><![CDATA[
  <xsp:logic>
  	if (somecondition()) {
  		<xsp:element name="true_tag"/>
  	} else {
  		<xsp:element name="false_tag"/>
  	}
  </xsp:logic>
  ]]></programlisting>
  </informalexample>
  </para>
  <para>
  Finally we can get <emphasis>even</emphasis> easier. Because the XML
  parser knows the difference between tags and text, we can simply use the
  tag on its own:
  </para>
  <para>
  What's interesting about this is that we don't actually need to break
  out of the "code" mode (the <literal>&lt;xsp:content></literal> tag
  here) to generate the XML tag - the XML parser actually does the hard
  work for us of figuring out what is a tag and what is Perl. Provided
  that you are aware of the limitations of this, such as not being able to
  use a CDATA section when we want to do this, and being aware that
  <literal>&lt;</literal> and <literal>&amp;</literal> signs need to be
  escaped, it is a very powerful concept.
  </para>
  <para>
  Building attributes is equally simple:
  <informalexample>
  <programlisting><![CDATA[
  <xsp:content>
  	if (somecondition()) {
  		<xsp:element name="true_tag">
  			<xsp:attribute name="myattr">My Value</xsp:attribute>
  		</xsp:element>
  	} else {
  		<xsp:element name="false_tag">
  			<xsp:attribute name="myattr">Other Value</xsp:attribute>
  		</xsp:element>
  	}
  </xsp:content>
  ]]></programlisting>
  </informalexample>
  Note how the tag to generate the attribute occurs as a child of the tag
  that creates the element.
  </para>
  <para>
  Putting this all together now, given that
  <literal>somecondition()</literal> returns true, the resulting output
  is:
  <informalexample>
  <programlisting><![CDATA[
  <true_tag myattr="My Value">
  </true_tag>
  ]]></programlisting>
  </informalexample>
  Not particularly interesting, until we realise that this is just one
  stage in the processing pipeline, and the next stage is probably an XSLT
  stylesheet that transforms <literal>&lt;true_tag></literal> into
  something with meaning to the browser, such as a table or text of some
  sort.
  </para>
  </sect2>
  
  <sect2>
  <title>Generating comments, text and processing instructions</title>
  <para>
  Generation of nodes is not limited to elements (and attributes). We can
  use the same techniques to generate comments, text and processing
  instructions. The benefits of this seem dubious, until you realise that
  again we can generate these things without breaking out of the "code"
  section, and just use XML tags.
  </para>
  <para>
  The tags to generate these types of nodes follow the same pattern as
  generating elements, so I'll cover this with a single example:
  <informalexample>
  <programlisting><![CDATA[
  <xsp:content>
  	# some perl code ...
  	<xsp:text>Hello World</xsp:text>
  	<xsp:comment>Why am I here?</xsp:comment>
  	<xsp:pi target="process-me">With some data</xsp:pi>
  </xsp:content>
  ]]></programlisting>
  </informalexample>
  The result from the above is:
  <informalexample>
  <programlisting><![CDATA[
  Hello World
  <!--Why am I here?-->
  <?process-me With some data?>
  ]]></programlisting>
  </informalexample>
  </para>
  </sect2>
  
  </sect1>
  
  <sect1>
  <title>Real World Example</title>
  </sect1>
  
  <sect1>
  <title>Conclusion</title>
  <para>
  We've seen the capabilities of XML Server Pages, and we've seen a simple
  real world example, however we've only really touched upon the power of
  XSP. The real power actually comes in when we get into
  <emphasis>taglibs</emphasis>. Taglibs allow us to design custom tags
  that we can insert into our pages, that provide us with extended
  functionality. This allows us to finally reach towards the ubiquitous
  goal of having developers work on the taglibs, and designers work on
  XSP's by inserting your company's custom tags.
  </para>
  <para>
  Taglibs provide the real power of XSP, and yet their implementation is
  not rocket science, in fact anyone who has already been reading other
  AxKit documentation will be intimately familiar with the tools to
  generate taglibs: <emphasis>stylesheets</emphasis>. We use stylesheets
  to build taglibs by developing stylesheets that transform our custom
  tags, <emphasis>and just our custom tags</emphasis> into valid XSP code,
  we can leverage our current development tools to build dynamic XML
  applications. Please read the section on the SQL taglib to see how we
  can build XSP's that connect to relational databases.
  </para>
  
  </sect1>
  </article>
  
  
  
  1.1                  xml-axkit/axkit.org/docs/xsp/index.xml
  
  Index: index.xml
  ===================================================================
  <?xml version="1.0" encoding="ISO-8859-1"?>
  <?xml-stylesheet href="/stylesheets/webpage_html.xps"
  	type="application/x-xpathscript" title="default"?>
  <webpage>
  <head>
  	<title>AxKit - XSP Documentation</title>
  </head>
  <body>
  	<section>
  		<title>XSP Documentation</title>
  		<para>
  			XSP is an XML dialect for executing code in the web server that
  			utilises embedded code, extensible tag libraries, and
  			transformation techniques.
  		</para>
  		<para>
  			XSP was originally invented for the <ulink
  			url="http://xml.apache.org/cocoon/">Cocoon project</ulink>. By
  			using their standard, scripts that are built for Cocoon are easier
  			to port to AxKit (although its worth noting that Cocoon does not
  			support XSP's written in Perl, nor does AxKit support XSP's
  			written in Java at the time of this writing).
  		</para>
  		<para>
  			Please see the links in the sidebar to find the rest of the XSP
  			documentation.
  		</para>
  	</section>
  </body>
  </webpage>
  
  
  
  1.1                  xml-axkit/axkit.org/examples/cv.css
  
  Index: cv.css
  ===================================================================
  
  
  
  
  
  1.1                  xml-axkit/axkit.org/examples/cv.xml
  
  Index: cv.xml
  ===================================================================
  <?xml version='1.0'?>
  <?xml-stylesheet 
  	href="cv.xps" 
  	type="application/x-xpathscript" 
  	title="default" 
  	alternate="no"?>
  <?xml-stylesheet 
  	href="cv-text.xps" 
  	type="application/x-xpathscript" 
  	title="text" 
  	alternate="yes"?>
  <resume>
  	<person>
  		<persname>
  			<title>Mr</title>
  			<forename>Matt</forename>
  			<middlename>David</middlename>
  			<surname>Sergeant</surname>
  		</persname>
  		<address>
  			<line>161 Waverley Crescent</line>
  			<line>Livingston</line>
  			<line>West Lothian</line>
  			<postcode>EH54 8JT</postcode>
  			<country>Scotland</country>
  		</address>
  		<phone type="Home">01506 419 813</phone>
  		<phone type="Mobile">07957 593 007</phone>
      <phone type="Mobile(2)">07718 933 770</phone>
  		<phone type="Fax">01506 419 813</phone>
  		<email>matt@sergeant.org</email>
      <sms>matt-sms@sergeant.org</sms>
  		<web_site>http://sergeant.org</web_site>
  		<dob>1974/04/01</dob>
  	</person>
  
  	<skills>
  		<area name="Languages">
  			<item time="5 years">Perl 5</item>
  			<item time="3 years">XML</item>
  			<item time="6 months">Java (JDK 1.2, Visual J++)</item>
  			<item>JavaScript</item>
  			<item time="3 years">ASP</item>
  			<item time="5 years">HTML</item>
  			<item time="3 years">(Visual) C++ (all versions)</item>
  		</area>
  		<area name="Protocols">
  			<item>HTTP</item>
  			<item>CGI</item>
  			<item>DCOM</item>
  			<item>SMTP</item>
  			<item>TCP/IP</item>
  			<item>NNTP</item>
  		</area>
  		<area name="Software">
  			<item>Linux (all versions)</item>
  			<item>Apache (1.3)</item>
  			<item>mod_perl</item>
  			<item>CVS, RCS, VSS and PVCS</item>
  			<item>NT (all versions)</item>
  			<item>IIS (3 &amp; 4)</item>
  		</area>
  		<area name="Databases">
  			<item time="2 years">Oracle</item>
  			<item time="3 years">Sybase</item>
  			<item time="2 years">MS SQL Server</item>
  			<item time="1 year">MySQL</item>
  			<item time="1 year">PostgreSQL</item>
  			<item time="3 years">Access</item>
  		</area>
  	</skills>
  
  	<education>
  		<establishment>
  			<name>University of Durham, St Cuthbert's Society</name>
  			<address>
  				<line>12 South Bailey</line>
  				<line>Durham</line>
  				<postcode>DH1 3EE</postcode>
  				<country>England</country>
  			</address>
  			<datefrom>1992/09</datefrom>
  			<dateto>1996/06</dateto>
  			<qualification>
  				<name>BSc Computer Science</name>
  				<grade>2nd Class, Level 2</grade>
  			</qualification>
  		</establishment>
  		<establishment>
  			<name>Huntington Comprehensive School</name>
  			<address>
  				<line>Huntington</line>
  				<line>York</line>
  				<postcode></postcode>
  				<country>England</country>
  			</address>
  			<datefrom></datefrom>
  			<dateto></dateto>
  			<qualification>
  				<name>A Level Maths</name>
  				<grade>A</grade>
  			</qualification>
  			<qualification>
  				<name>A Level Physics</name>
  				<grade>A</grade>
  			</qualification>
  			<qualification>
  				<name>A Level Economics</name>
  				<grade>D</grade>
  			</qualification>
  		</establishment>
  	</education>
  
  	<experience>
      <employee>
        <name>AxKit.com Limited</name>
        <address>
          <line>161 Waverley Crescent</line>
          <line>Livingston</line>
          <line>West Lothian</line>
          <postcode>EH54 8JT</postcode>
          <country>Scotland</country>
        </address>
        <datefrom>2000/08</datefrom>
        <dateto>Present</dateto>
        <role>Chief Technology Officer and Developer</role>
        <details>
          CTO and lead developer on a project to build a leading edge
          open source XML application server and content management
          system. AxKit is now one of the leading products in the open
          source community for building XML backed web sites, and has been
          used to successfully build many web sites providing alternate
          views for pages (such as WAP or HTML) and a friendly XML based
          development environment. The technologies built and designed
          by myself include XPathScript and the core engine inside AxKit.
          I also ported Cocoon's XSP technology to Perl for use in AxKit.
        </details>
      </employee>
  		<employee>
  			<name>O'Reilly and Associates</name>
  			<address>
  				<line>90 Sherman Street</line>
  				<line>Cambridge</line>
  				<line>MA</line>
  				<postcode>02140</postcode>
  				<country>United States of America</country>
  			</address>
  			<datefrom>2000/03</datefrom>
  			<dateto>Present</dateto>
  			<role>Lead Developer</role>
  			<details>
          Developing a commercial Perl based Web Discussion system, with
          Web, Email and NNTP interfaces. The project was a complete, from
          scratch development that I personally designed, architected and
          developed (with one further developer helping later). The
          technologies used included Object Oriented (OO) Perl5, mod_perl,
          DBI, MySQL, PostgreSQL, Oracle, Sybase, module design, Apache, multi
          threaded kernel development, Email and NNTP message parsing 
          (Internet RFC's 821, 977, 1341, 1641 and 2980), and much more.
  			</details>
  		</employee>
  		<employee>
  			<name>Deutsche Bank - Wood MacKenzie</name>
  			<address>
  				<line>74-77 Queen Street</line>
  				<line>Edinburgh</line>
  				<country>Scotland</country>
  			</address>
  			<datefrom>1999/10</datefrom>
  			<dateto>2000/03</dateto>
  			<role>Perl Consultant</role>
  			<details>
  				This role involved consulting on the development of a
  				product using the MediaSurface application server
  				product. Involving all aspects of development and
  				project management. In particular working towards
  				extending MediaSurface to have better templating
  				facilities and XML support.
  			</details>
  		</employee>
  		<employee>
  			<name>British Broadcasting Corporation</name>
  			<address>
  				<line>Bush House</line>
  				<line>Aldwich</line>
  				<line>London</line>
  				<country>England</country>
  			</address>
  			<datefrom>1999/07/01</datefrom>
  			<dateto>1999/08/31</dateto>
  			<role>Web Developer and Web Consultant</role>
  			<details>
  				Investigating several new technologies for the BBC's high profile web site
  				(the most used content based web site in the UK) and developing modules for
  				use on the site. One module is now used on every page on the BBC web site.
  				Investigations into new technology covered search engines, personalisation
  				technology, database backends and mod_perl. All work was conducted using Perl.
  			</details>
  		</employee>
  		<employee>
  			<name>Ericsson Mobile Communications (UK) Limited</name>
  			<address>
  				<line>The Keytech Centre</line>
  				<line>Ashwood Way</line>
  				<line>Basingstoke</line>
  				<line>Hants</line>
  				<postcode>RG23 8BG</postcode>
  				<country>England</country>
  			</address>
  			<datefrom>1998/04/06</datefrom>
  			<dateto>1999/06/30</dateto>
  			<role>Contract Developer</role>
  			<details>
  				Developing an MMI for a "black box" device. Using Java over a tiny web server. Working
  				initially with Visual J++, then with Sun JDK. Following on, working in Development IT
  				doing internet/intranet development. Leading the development teams to create interactive
  				business functionality. Key developments include large amounts of XML work, designing
  				XML search tools (based on XQL and XML-QL), XML re-formatting tools (as a pre-cursor
  				to XSL - see my web site), online XML editing protocols, and indexing techniques. The
  				culmination of this work has been a global XML-based timesheet application, to be
  				deployed all over the world to Ericsson employees. Some of this work has been made
  				available to members of the Perl community via the Perl-XML mailing list and CPAN.
  			</details>
  		</employee>
  		<employee>
  			<name>O'Reilly and Associates</name>
  			<address>
  				<line>90 Sherman Street</line>
  				<line>Cambridge</line>
  				<line>MA</line>
  				<postcode>02140</postcode>
  				<country>United States of America</country>
  			</address>
  			<datefrom>1998/12</datefrom>
  			<dateto>Present</dateto>
  			<role>Author</role>
  			<details>
  				Co-Authoring a new O'Reilly book called "XML applications with Perl". This will
  				detail ways to use
  				perl to process and generate XML. It will included details on XML::Parser, and the
  				many modules that use it. Details of my CGI to XML gateway, are also included and
  				some of my other XML modules. This work is in my "spare" time!
          [Addendum: This book has now been cancelled due to lack of time
          for all three authors. This item remains here for reference]
  			</details>
  		</employee>
  		<employee>
  			<name>O'Reilly and Associates</name>
  			<address>
  				<line>90 Sherman Street</line>
  				<line>Cambridge</line>
  				<line>MA</line>
  				<postcode>02140</postcode>
  				<country>United States of America</country>
  			</address>
  			<datefrom>1999/03</datefrom>
  			<dateto>1999/04</dateto>
  			<role>System Architect / Developer</role>
  			<details>
  				Developing the NNTP module for O'Reilly's WebBoard suite of software (see
  				http://webboard.oreilly.com). This involved designing a new system from the
  				ground up to do NNTP to a backend database, implementing authentication and
  				ensuring performance was scalable. WebBoard 4.0 was released in June 1999.
  			</details>
  		</employee>
  		<employee>
  			<name>Integrated Sales Systems Limited</name>
  			<address>
  				<line>Regal House</line>
  				<line>70 London Road</line>
  				<line>Twickenham</line>
  				<postcode>TW1 3QS</postcode>
  				<country>England</country>
  			</address>
  			<datefrom>1996/06/05</datefrom>
  			<dateto>1998/04/01</dateto>
  			<role>Software Engineer, Senior Software Engineer, Team Leader, Project Leader</role>
  			<details>
  				Employeed again as a graduate I rapidly rose through the company, taking on
  				projects such as a major upgrade for Searle Pharmaceuticals (mostly development work),
  				Designing and implementing Comms security over RAS (working with the Win32 RAS and TCP/IP
  				functions in C++), Internationalisation in the Oxygen
  				application, performance improvements and internal tools. While working in
  				Client Development I worked both on and off site for major corporations including
  				Monsanto Searle Pharmaceuticals, Schering Plough, Lombard Business Finance, Astra
  				Pharmaceuticals and Royal &amp; Sun Alliance. A major project
  				while at ISS was developing their intranet. This rapidly grew from a project
  				running on my own workstation, into a dedicated web server, serving up thousands
  				of documents, and running some of the core infrastructure at the company. The server
  				was a test bed for many new web tools - beta testing ASP (when it was called Denali),
  				working with HiP Communications to bring Perl to Win32, and testing out the new IIS 4.
  			</details>
  		</employee>
  		<employee>
  			<name>Integrated Sales Systems Limited</name>
  			<address>
  				<line>Regal House</line>
  				<line>70 London Road</line>
  				<line>Twickenham</line>
  				<postcode>TW1 3QS</postcode>
  				<country>England</country>
  			</address>
  			<datefrom>1995/06/01</datefrom>
  			<dateto>1995/09/01</dateto>
  			<role>Junior Software Engineer</role>
  			<details>
  				Working over the summer at this lively new software house. I worked in small
  				teams helping to realise their first Windows version of their aging DOS
  				application. I worked in Visual C++ and T-SQL. I also designed a database consolidation
  				routine called "Behemoth".
  			</details>
  		</employee>
  	</experience>
    
    <speaking>
     <engagement>
      <conference>O'Reilly Open Source Software Convention 2001, San
      Diego</conference>
      <talk>AxKit - XML Application Serving with mod_perl</talk>
      <talk>mod_perl as an HTTP RPC Daemon</talk>
      <talk>Exception Handling in mod_perl</talk>
      <talk>Cross Database Perl Applications</talk>
      <talk>Orchard: a new API for XML</talk>
     </engagement>
     <engagement>
      <conference>Open Source Web Solutions 2001, Stockholm</conference>
      <talk>Developing XML applications with Perl and AxKit</talk>
      <talk>Building a web portal with XML and Perl</talk>
     </engagement>
     <engagement>
      <conference>XML Dev Con 2001 New York</conference>
      <talk>Building XML Based Web Sites with Open Source Tools</talk>
     </engagement>
     <engagement>
      <conference>Apache Con 2001 San Jose</conference>
      <talk>AxKit - the XML Application server for Apache</talk>
     </engagement>
     <engagement>
      <conference>XML Dev Con Europe 2001 London</conference>
      <talk>Building XML Based Web Sites with Open Source Tools</talk>
     </engagement>
     <engagement>
      <conference>XML Dev Con Fall 2000 San Jose</conference>
      <talk>Building XML Based Web Sites with Open Source Tools</talk>
     </engagement>
     <engagement>
      <conference>XML Dev Con Fall 2000 San Jose</conference>
      <talk>Generating XML From Form Data</talk>
     </engagement>
     <engagement>
      <conference>Apache Con Europe 2000 London</conference>
      <talk>AxKit - the XML Application server for Apache</talk>
     </engagement>
     <engagement>
      <conference>XML Dev Con 2000 New York</conference>
      <talk>Generating XML From Form Data</talk>
     </engagement>
     <engagement>
      <conference>XML Dev Con 2000 New York</conference>
      <talk>XML Little Languages (compilation, optimisation and execution
      of XML languages)</talk>
     </engagement>
    </speaking>
  
  	<training>
  		<course>
  			<name>Oracle Performance Tuning</name>
  			<date></date>
  			<trainee>Oracle Training</trainee>
  		</course>
  		<course>
  			<name>Oracle Advanced SQL</name>
  			<date></date>
  			<trainee>Oracle Training</trainee>
  		</course>
  		<course>
  			<name>Object Oriented Design</name>
  			<date></date>
  			<trainee>QA Training</trainee>
  		</course>
  	</training>
  </resume>
  
  
  
  1.1                  xml-axkit/axkit.org/examples/cv.xps
  
  Index: cv.xps
  ===================================================================
  <html>
  <head>
  <%
  my $name = join(' ',
  			findvalue('/resume/person/persname/forename'), 
  			findvalue('/resume/person/persname/surname'));
  %>
  <title><%= $name %> - CV</title>
  <link rel="stylesheet" type="text/css" href="cv.css">
  </head>
  <body bgcolor="white">
  <h1 align="center"><%= $name %></h1>
  
  <center>
  <hr width="100%"></hr>
  <i>
  <%
  my ($addr) = findnodes('/resume/person/address');
  print join(' / ', findvalues('line', $addr), findvalue('postcode', $addr), findvalue('country', $addr) );
  %>
  <br><b>Tel:</b>
  <%
  foreach my $tel (findnodes('/resume/person/phone')) {
  	print " <i>", findvalue('@type', $tel), ":</i> ";
  	print findvalue('./text()', $tel);
  }
  %>
  <br>
  <b>e-mail:</b><a href="mailto:<%= findvalue('/resume/person/email') %>"><%= findvalue('/resume/person/email') %></a>
  /
  <b>gsm sms:</b><a href="mailto:<%= findvalue('/resume/person/sms') %>"><%= findvalue('/resume/person/sms') %></a>
  <p><b>DoB:</b> <%= findvalue('/resume/person/dob') %>
  </center>
  <br>
  <b><font size="+1">Education</font></b>
  <center><hr width="100%"></center>
  
  <!-- show most recent education only -->
  <%
  my ($edu) = findnodes('/resume/education/establishment[1]');
  %>
  <table>
  	<tr>
  		<td width="25"> </td>
  		<td width="400"><%= findvalue('name', $edu) %></td>
  		<td><i>Graduated:</i> <%= findvalue('dateto', $edu) %></td>
  	</tr>
  	<tr>
  		<td></td>
  		<td><%= findvalue('qualification/name', $edu) %></td>
  		<td></td>
  	</tr>
  </table>
  <br>
  <b><font size="+1">Key Skills</font></b>
  <hr width="100%">
  <table width="100%" cellpadding="1" cellspacing="0" border="1">
  <tr valign="top">
  <%
  my @areas = findnodes('/resume/skills/area');
  foreach my $area (@areas) {
  %>
  <td width="<%= 100 / @areas %>%">
  	<table width="100%" cellpadding="1" cellspacing="0" border="0">
  		<tr><th><%= findvalue('@name', $area) %></th></tr>
  		<%
  		foreach my $skill (findnodes('item', $area)) {
  		%>
  		<tr>
  			<td><%= findvalue('./text()', $skill) %></td>
  		</tr>
  		<%
  		}
  		%>
  	</table>
  </td>
  <%
  }
  %>
  </tr>
  </table>
  
  <br>
  <b><font size="+1">Current Work</font></b>
  <hr width="100%">
  <table>
  	<tr>
  		<td width="25"> </td>
  		<td width="400"><b><%= findvalue('/resume/experience/employee[1]/name') %></b></td>
  	</tr>
  </table>
  
  <br>
  <b><font size="+1">Work Experience</font></b>
  <hr width="100%">
  <table>
  
  <%
  foreach my $employee (findnodes('/resume/experience/employee[position() <= 7]')) {
  %>
  <tr>
  	<td width="25"> </td>
  	<td><b><%= findvalue('name', $employee) %></b></td>
  	<td><b><%= findvalue('datefrom', $employee) %> - <%= findvalue('dateto', $employee) %></b></td>
  </tr>
  <tr>
  	<td width="25"> </td>
  	<td colspan="2"><%= findvalue('details', $employee) %><br></td>
  </tr>
  <%	
  }
  %>
  </table>
  
  <b><font size="+1">Speaking Engagements</font></b>
  <hr width="100%">
  As one of the world's leading Perl and XML developers, I am invited to
  speak at many conferences each year. These are the talks I have given or
  will be giving in the coming year:
  <table>
  <%
  $t->{'talk'}{showtag} = 0;
  
  foreach my $engagement (findnodes('/resume/speaking/engagement')) {
  %>
  	<tr>
  		<td width="25"> </td>
  		<td><%= $engagement->findvalue('conference') %> <i>(<%= join(', ', map { apply_templates($_) } $engagement->findnodes('talk')) %>)</i></td>
  	</tr>
  <%
  }
  %>
  </table>
  
  <b><font size="+1">Training</font></b>
  <hr width="100%">
  <table>
  <%
  foreach my $course (findnodes('/resume/training/course')) {
  %>
  	<tr>
  		<td width="25"> </td>
  		<td><%= findvalue('name', $course) %> <i>(<%= findvalue('trainee', $course) %>)</i></td>
  	</tr>
  <%
  }
  %>
  </table>
  
  </body>
  </html>
  
  
  
  1.1                  xml-axkit/axkit.org/examples/cv_example.xml
  
  Index: cv_example.xml
  ===================================================================
  <?xml version="1.0"?>
  <?xml-stylesheet href="/stylesheets/webpage_html.xps" type="application/x-xpathscript"?>
  <webpage>
  	<head>
  		<title>CV Example using XPathScript</title>
  	</head>
  <body>
  <section>
  <title>Finally I get around to updating my CV!</title>
  <p>
  People ask me at least once a week to see my updated CV, but I keep it
  on a different ISP's server and never get around to updating it. This
  isn't because I don't want people to see it - but because I'm always
  working on something far more interesting :-)
  </p>
  <p>
  However I figured it makes a good example for XPathScript. So here is
  <a href="cv.xps">the XPathScript source code</a>. The original XML file is available by
  adding the querystring passthru=1 to the XML file. Link <a
  href="cv.xml?passthru=1">here</a>. The passthru option is another
  example of an AxKit plugin. The translated version created by this
  stylesheet is available <a href="cv.xml">here</a>.
  </p>
  </section>
  </body>
  </webpage>
  
  
  
  1.1                  xml-axkit/axkit.org/examples/docbook.dkb
  
  Index: docbook.dkb
  ===================================================================
  <?xml version="1.0"?>
  <?xml-stylesheet href="/stylesheets/docbook_screen.xps" 
  				type="application/x-xpathscript" 
  				media="screen"
  				title="default" ?>
  <?xml-stylesheet href="/stylesheets/docbook_screen_fancy.xps" 
  				type="application/x-xpathscript" 
  				media="screen"
  				title="fancy"
  				alternate="yes" ?>
  <?xml-stylesheet href="/stylesheets/docbook_print.xps" 
  				type="application/x-xpathscript" 
  				media="screen"
  				title="print"
  				alternate="yes" ?>
  <?xml-stylesheet href="/stylesheets/docbook_handheld.xps" 
  				type="application/x-xpathscript" 
  				media="handheld"
  				title="default"?>
  <article>
  <artheader>
  	<title>A complete DocBook XPathScript Example</title>
  	
  	<author>
  		<firstname>Matt</firstname>
  		<surname>Sergeant</surname>
  		<affiliation>
  			<address><email>matt@sergeant.org</email></address>
  		</affiliation>
  	</author>
  	
  	<copyright>
  		<year>2000</year>
  		<holder role="mailto:matt@sergeant.org">AxKit.com Ltd</holder>
  	</copyright>
  
  <!--	
  	<pubdate role="rcs">$Date: 2002/01/13 20:45:09 $</pubdate>
  	
  	<releaseinfo>$Id: docbook.dkb,v 1.1 2002/01/13 20:45:09 matts Exp $</releaseinfo>
  -->
  
  	<abstract>
  		<para>A complete example of a docbook stylesheet using AxKit</para>
  	</abstract>
  </artheader>
  
  <sect1>
  <title>Introduction</title>
  <para>
  This aims to be a complete example of rendering DocBook documents
  using <ulink url="http://axkit.org/">AxKit</ulink> and
  XPathScript. It offers several different styles to render the document
  in: Normal, Fancy and Printer. And also offers an alternative media to
  render the document for: handheld. How you select the different styles
  and media will be detailed later in this document.
  </para>
  <para>
  First a disclaimer: I'm a complete "newbie" to docbook. I also haven't
  tried rendering this document with the standard docbook tools, so I may
  have some elements wrong. If I do, you have my appologies. Hopefully you
  might be encouraged enough by this to fix the problems I've introduced
  and do it right, as it were.
  </para>
  <para>
  Note: This example remains unfinished. Please see the "Introduction to
  AxKit" on the AxKit web site for a current better example of using
  DocBook.
  </para>
  </sect1>
  
  </article>
  
  
  
  1.1                  xml-axkit/axkit.org/examples/resume.dtd
  
  Index: resume.dtd
  ===================================================================
  <!-- Resume/CV DTD -->
  
  <!--
  
  This is my first shot at a resume dtd. It hopefully encompasses quite a bit of
  detail so that you can create complex or simple resume's. It also should cover
  a fair bit of international issues. Let me know if it doesn't.
  
  Changes:
  
  	Added time attribute to skill items, so you can say how long
  	you've had that skill.
  
  	Fixed spelling of professional.
  
  	Made phone type attribute IMPLIED instead of REQUIRED.
  	Ditto for publication.
  
  Matt Sergeant, matt@sergeant.org
  
  -->
  <!ELEMENT resume (	person,
  					skills?,
  					education?,
  					professional_organisations?,
  					publications?,
  					experience?,
  					training?,
  					interests?,
  					references?)>
  
  	<!ELEMENT person (persname,address,phone*,email*,web_site?,dob?)>
  
  		<!ELEMENT persname (title,forename,middlename?,surname)>
  			<!ELEMENT title (#PCDATA)>
  			<!ELEMENT forename (#PCDATA)>
  			<!ELEMENT middlename (#PCDATA)>
  			<!ELEMENT surname (#PCDATA)>
  
  		<!ELEMENT address (line*,postcode?,country?)>
  			<!ELEMENT line (#PCDATA)>
  			<!ELEMENT postcode (#PCDATA)>
  			<!ELEMENT country (#PCDATA)>
  
  		<!ELEMENT phone (#PCDATA)>
  		<!ATTLIST phone
  					type	CDATA #IMPLIED>
  
  		<!ELEMENT email (#PCDATA)>
  		<!ELEMENT web_site (#PCDATA)>
  		<!ELEMENT dob (#PCDATA)> <!-- Date of Birth -->
  
  	<!ELEMENT skills (area)*>
  		<!ELEMENT area (item)*>
  		<!ATTLIST area name CDATA #REQUIRED>
  			<!ELEMENT item (#PCDATA)>
  			<!ATTLIST item time CDATA #IMPLIED> <!-- Length of time you've had this skill - e.g. "6 months" -->
  
  	<!ELEMENT education (establishment)*>
  		<!ELEMENT establishment (name,address,datefrom,dateto,level?,qualification*)>
  
  			<!ELEMENT name (#PCDATA)>
  			<!ELEMENT datefrom (#PCDATA)>
  			<!ELEMENT dateto (#PCDATA)>
  			<!ELEMENT qualification (name,grade?)>
  				<!ELEMENT grade (#PCDATA)>
  
  	<!ELEMENT professional_organisations (organisation)*>
  		<!ELEMENT organisation (name,offices_held,datefrom)>
  			<!ELEMENT offices_held (#PCDATA)>
  
  	<!ELEMENT publications (publication)>
  		<!ELEMENT publication (name,details)>
  		<!ATTLIST publication
  			type CDATA #IMPLIED> <!-- Specify book, article, etc -->
  
  			<!ELEMENT details ANY> <!-- Expecting markup here? -->
  
  	<!ELEMENT experience (employee)*>
  		<!ELEMENT employee (name,address,datefrom,dateto,role,details)>
  
  			<!ELEMENT role (#PCDATA)>
  
  	<!ELEMENT training (course)*>
  		<!ELEMENT course (name,date,trainee?,level?)>
  			<!ELEMENT date (#PCDATA)>
  			<!ELEMENT trainee (#PCDATA)>
  			<!ELEMENT level (#PCDATA)>
  
  	<!ELEMENT interests (#PCDATA)> <!-- Someone can fill out structure here if they want -->
  
  	<!ELEMENT references (reference)*>
  		<!ELEMENT reference (person,relationship,address)>
  			<!ELEMENT relationship (#PCDATA)> <!-- Describe relationship to this person -->
  
  
  
  1.1                  xml-axkit/axkit.org/img/arrow.gif
  
  	<<Binary file>>
  
  
  1.1                  xml-axkit/axkit.org/img/arrow_sel.gif
  
  	<<Binary file>>
  
  
  1.1                  xml-axkit/axkit.org/img/banner2.gif
  
  	<<Binary file>>
  
  
  1.1                  xml-axkit/axkit.org/img/banner3.gif
  
  	<<Binary file>>
  
  
  1.1                  xml-axkit/axkit.org/img/banner_axkit.gif
  
  	<<Binary file>>
  
  
  1.1                  xml-axkit/axkit.org/img/banner_bg.gif
  
  	<<Binary file>>
  
  
  1.1                  xml-axkit/axkit.org/img/banner_end.gif
  
  	<<Binary file>>
  
  
  1.1                  xml-axkit/axkit.org/img/bg.gif
  
  	<<Binary file>>
  
  
  1.1                  xml-axkit/axkit.org/img/bullet.gif
  
  	<<Binary file>>
  
  
  1.1                  xml-axkit/axkit.org/img/corner_logo.jpg
  
  	<<Binary file>>
  
  
  1.1                  xml-axkit/axkit.org/img/corner_logo.png
  
  	<<Binary file>>
  
  
  1.1                  xml-axkit/axkit.org/img/nav_bot.gif
  
  	<<Binary file>>
  
  
  1.1                  xml-axkit/axkit.org/img/nav_left.gif
  
  	<<Binary file>>
  
  
  1.1                  xml-axkit/axkit.org/img/nav_right.gif
  
  	<<Binary file>>
  
  
  1.1                  xml-axkit/axkit.org/img/nav_top.gif
  
  	<<Binary file>>
  
  
  1.1                  xml-axkit/axkit.org/img/pix.gif
  
  	<<Binary file>>
  
  
  1.1                  xml-axkit/axkit.org/img/rcorner_br.gif
  
  	<<Binary file>>
  
  
  1.1                  xml-axkit/axkit.org/img/rcorner_wh.gif
  
  	<<Binary file>>
  
  
  1.1                  xml-axkit/axkit.org/img/vsep.gif
  
  	<<Binary file>>
  
  
  1.1                  xml-axkit/axkit.org/stylesheets/axkit.css
  
  Index: axkit.css
  ===================================================================
  body {
      color:		 #333;
      margin:		 0px;
      font-family: Arial,Helvetica,Helv,sans-serif;
      font-size: 12pt;
  }
  
  p {
      font-family: Arial,Helvetica,Helv,sans-serif;
      font-size: 12pt;
  }
  
  .by {
   font-family: Verdana, sans-serif; 
   font-size: 10pt;
   color: black;
  }
  
  H3, .hd {
    font-family:  Verdana,Helvetica,Helv,sans-serif;
    font-size:    15pt;
    font-weight:  bold;
  }
  
  .hd2 {
    font-family:  Verdana,Helvetica,Helv,sans-serif;
    font-size:    14pt;
    font-weight:  bold;
  }
  
  .hd3 {
    font-family:  Verdana,Helvetica,Helv,sans-serif;
    font-size:    12pt;
    font-weight:  bold;
  }
  
  .hd4 {
    font-family:  Verdana,Helvetica,Helv,sans-serif;
    font-size:    10pt;
    font-weight:  bold;
  }
  
  @media screen {
  
      A.plain:hover {
          color:      transparent;
          background: transparent;
      }
      
      a:hover {
          color:              #fc6;
          text-decoration:    none;
      }
  }
  
  .toplevel {
  	color:		 #306;
  	font-family: Arial,Helvetica,Helv,sans-serif;
  	font-size:	 11pt;
  	font-weight: bold;
  }
  
  .sublevel {
  	font-family: Arial,Helvetica,Helv,sans-serif;
  	font-size:	 10pt;
  	font-weight: bold;
  }
  
  .toponomic {
  	font-family: Arial,Helvetica,Helv,sans-serif;
  	font-size:	 10pt;
  	font-weight: bold;
  }
  
  .copyright {
  	font-family: Arial,Helvetica,Helv,sans-serif;
  	font-size:	 8pt;
  	font-style:  italic;
  }
  
  
  
  1.1                  xml-axkit/axkit.org/stylesheets/axkit_html.css
  
  Index: axkit_html.css
  ===================================================================
  /*
    Generic Layout
  */
  
  
  BODY {
  	background:		#fff;
  	font-family:	Arial, Helvetica, sans-serif;
  }
  
  
  
  
  1.1                  xml-axkit/axkit.org/stylesheets/body_print_html.xps
  
  Index: body_print_html.xps
  ===================================================================
  <body 
  bgcolor="#ffffff" text="#333333" link="#9966cc" vlink="#330066" alink="#eeddff" 
  marginheight="0" marginwidth="0" 
  topmargin="0" leftmargin="0">
  
  <table width="100%" border="0" cellspacing="0" cellpadding="0">
  
  <tr style="line-height: 0;">
  <td align="left" valign="top" width="190" background="/img/banner_bg.gif"><a
  href="http://axkit.org/"><img src="/img/banner3.gif" 
  width="190" height="84" border="0" vspace="0" hspace="0" 
  alt="AxKit.org - XML Application Server" /></a></td>
  
  <% my $img_id = int rand(1_000_000); %>
  <td align="right" valign="top" width="100%" background="/img/banner_bg.gif"><a 
  class="plain" 
  href="http://kansas.valueclick.com/redirect?host=hs0185469&amp;size=468x60&amp;b=<%= $img_id %>&amp;v=0" 
  target="_top"><img vspace="0" hspace="0" border="1" align="absmiddle"
  width="468" height="60" alt="Please visit our sponsor"
  src="http://kansas.valueclick.com/cycle?host=hs0185469&amp;size=468x60&amp;b=<%= $img_id %>&amp;noscript=1" 
  /></a><img src="/img/banner_end.gif" 
  width="26" height="84" border="0" 
  vspace="0" hspace="0" alt="" align="absmiddle" /></td>
  </tr>
  </table>
  
  
  
  1.1                  xml-axkit/axkit.org/stylesheets/body_tag_html.xps
  
  Index: body_tag_html.xps
  ===================================================================
  <body 
  bgcolor="#ffffff" text="#333333" link="#9966cc" vlink="#330066" alink="#eeddff" 
  marginheight="0" marginwidth="0" 
  topmargin="0" leftmargin="0" background="/img/bg.gif">
  
  <table width="100%" border="0" cellspacing="0" cellpadding="0">
  
  <tr style="line-height: 0;">
  <td align="left" valign="top" width="190" background="/img/banner_bg.gif"><img
  	src="/img/banner3.gif" width="190" height="84" border="0" vspace="0" hspace="0" alt="AxKit.org [logo curtesy of http://xml.com]" /></td>
  
  <% my $img_id = int rand(1_000_000); %>
  <td align="right" valign="top" width="100%" background="/img/banner_bg.gif"><a 
  class="plain" 
  href="http://kansas.valueclick.com/redirect?host=hs0185469&amp;size=468x60&amp;b=<%= $img_id %>&amp;v=0" 
  target="_top"><img vspace="0" hspace="0" border="1" align="absmiddle"
  width="468" height="60" alt="Please visit our sponsor"
  src="http://kansas.valueclick.com/cycle?host=hs0185469&amp;size=468x60&amp;b=<%= $img_id %>&amp;noscript=1" 
  /></a><img src="/img/banner_end.gif" 
  width="26" height="84" border="0" 
  vspace="0" hspace="0" alt="" align="absmiddle" /></td>
  </tr>
  </table>
  
  
  
  1.1                  xml-axkit/axkit.org/stylesheets/docbook_example.xml
  
  Index: docbook_example.xml
  ===================================================================
  <?xml version="1.0"?>
  <?xml-stylesheet href="/docbook_screen.xps" 
  				type="application/x-xpathscript" 
  				media="screen"
  				title="default" ?>
  <?xml-stylesheet href="/docbook_screen_fancy.xps" 
  				type="application/x-xpathscript" 
  				media="screen"
  				title="fancy"
  				alternate="yes" ?>
  <?xml-stylesheet href="/docbook_print.xps" 
  				type="application/x-xpathscript" 
  				media="screen"
  				title="print"
  				alternate="yes" ?>
  <?xml-stylesheet href="/docbook_handheld.xps" 
  				type="application/x-xpathscript" 
  				media="handheld"
  				title="default"?>
  <article>
  <artheader>
  	<title>A complete DocBook XPathScript Example</title>
  	
  	<author>
  		<firstname>Matt</firstname>
  		<surname>Sergeant</surname>
  		<affiliation>
  			<address><email>matt@sergeant.org</email></address>
  		</affiliation>
  	</author>
  	
  	<copyright>
  		<year>2000</year>
  		<holder role="mailto:matt@sergeant.org">AxKit.com Ltd</holder>
  	</copyright>
  
  <!--	
  	<pubdate role="rcs">$Date: 2002/01/13 20:45:10 $</pubdate>
  	
  	<releaseinfo>$Id: docbook_example.xml,v 1.1 2002/01/13 20:45:10 matts Exp $</releaseinfo>
  -->
  
  	<abstract>
  		<para>A complete example of a docbook stylesheet using the
  			Apache XML Delivery Toolkit</para>
  	</abstract>
  </artheader>
  
  <sect1>
  <title>Introduction</title>
  <para>
  This aims to be a complete example of rendering DocBook documents
  using the <ulink url="http://xml.sergeant.org/axkit/">AxKit</ulink> and
  XPathScript. It offers several different styles to render the document
  in: Normal, Fancy and Printer. And also offers an alternative media to
  render the document for: handheld. How you select the different styles
  and media will be detailed later in this document.
  </para>
  <para>
  First a disclaimer: I'm a complete "newbie" to docbook. I also haven't
  tried rendering this document with the standard docbook tools, so I may
  have some elements wrong. If I do, you have my appologies. Hopefully you
  might be encouraged enough by this to fix the problems I've introduced
  and do it right, as it were.
  </para>
  </sect1>
  
  </article>
  
  
  
  1.1                  xml-axkit/axkit.org/stylesheets/docbook_print.xps
  
  Index: docbook_print.xps
  ===================================================================
  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/strict.dtd">
  <html>
  <!--#include file="spacer.xps"-->
  <!--#include file="docbook_tags.xps"-->
  <%
  
  my %links;
  my $linkid = 0;
  $t->{'ulink'}{testcode} = sub { 
  		my $node = shift;
  		my $t = shift;
  		my $url = findvalue('@url', $node);
  		if (!exists $links{$url}) {
  			$linkid++;
  			$links{$url} = $linkid;
  		}
  		my $link_number = $links{$url};
  		$t->{pre} = "<i><a href=\"$url\">";
  		$t->{post} = " [$link_number]</a></i>";
  		return 1;
  	};
  
  %>
  <head>
  <title><%= findvalue('/article/artheader/title/text()') %></title>
  <link rel="stylesheet" href="/stylesheets/axkit.css" type="text/css" />
  </head>
  <!--#include file="body_print_html.xps"-->
  
  <table width="100%">
  <tr>
  <td width="10"><%= spacer(10,0) %></td>
  <td width="*" style="font-size: small;">
  <%
  # display title/TOC page
  print apply_templates('/article/artheader/*');
  %>
  
  <hr>
  
  <%
  # display all pages
  foreach my $section (findnodes("/article/sect1")) {
  	print apply_templates($section);
  }
  %>
  
  <h1>List of Links</h1>
  <table border="1">
  <th>URL</th>
  <%
  for my $link (sort {$links{$a} <=> $links{$b}} keys %links) {
  %>
  <tr>
  <td><%= "[$links{$link}] $link" %></td>
  </tr>
  <% } %>
  </table>
  
  <%
  if (@footnotes) {
      print "<hr><table cellspacing=\"0\" cellpadding=\"3\">";
      print "<caption>Footnotes</caption>";
      my $i = 1;
      foreach my $footnote (@footnotes) {
          print "<tr><td valign=\"top\"><p>$i</p></td><td valign=\"top\"><div class=\"footnote\">$footnote</div></td></tr>";
          $i++;
      }
      print "</table><hr>";
  }
  %>
  
  </td>
  </tr>
  </table>
  </body>
  </html>
  
  
  
  1.1                  xml-axkit/axkit.org/stylesheets/docbook_screen.xps
  
  Index: docbook_screen.xps
  ===================================================================
  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/strict.dtd">
  <html>
  <!--#include file="sidebar_html.xps"-->
  <!--#include file="spacer.xps"-->
  <%
  use Apache::Request;
  
  my $apr = Apache::Request->instance($r);
  my $sect = $apr->param('section');
  
  $r->no_cache(1);
  
  my $this_page = $r->uri;
  
  my ($prev, $next);
  my $sects = findvalue('count(/article/sect1)');
  $sects = "$sects";
  if ($sects > 1) {
  	if ($sect >= $sects) {
  		$prev = $sects - 1;
  	}
  	elsif ($sect <= 1) {
  		$next = 2;
  	}
  	else {
  		$prev = $sect - 1;
  		$next = $sect + 1;
  	}
  }
  
  %>
  <!--#include file="docbook_tags.xps"-->
  <%
  $t->{'xref'}{testcode} = sub {
  		my ($node, $t) = @_;
  		my $id = findvalue('@linkend', $node);
  		my ($dest) = findnodes("id('$id')/title");
  		my $sect = findvalue("count(ancestor::sect1/preceding-sibling::sect1) + 1", $dest);
  		$t->{pre} = "<b><a href=\"$this_page?section=$sect#";
  		$t->{pre} .= $id . '">';
  		$t->{pre} .= findvalue("id('$id')/title/text()");
  		$t->{post} = '</a></b>';
  		return -1;
  	};
  %>
  <head>
  <title><%= findvalue('/article/artheader/title/text()') %></title>
  <link rel="stylesheet" href="/stylesheets/axkit.css" type="text/css" />
  </head>
  
  <!--#include file="body_tag_html.xps"-->
  
  <table width="100%" border="0" cellspacing="0" cellpadding="0">
  <tr>
  <!-- navbar -->
  <td align="left" valign="top" width="172">
  
  <!--#include file="navbar_html.xps"-->
  
  </td>
  
  <!-- whitespace -->
  <td align="left" valign="top" width="10"><%= spacer(10,1) %></td>
  
  <!-- content -->
  <td align="left" valign="top" style="font-size: small;">
  
  <%
  if (!$sect) {
  	# display title/TOC page
  	print apply_templates('/article/artheader/*');
  	my $page = 0;
  	foreach my $sect (findnodes('/article/sect1')) {
  		$page++;
  		print "<a href=\"$this_page?section=$page\">";
  		print findvalue('title/text()', $sect);
  		print "</a><br>\n";
  	}
  	%><hr><%
  }
  else {
  
  %>
  <table width="100%">
  <tr>
  <td align="left">
  <%
  	if ($prev) {
  %>
  <a href="<%=$this_page%>?section=<%=$prev%>">Prev</a>
  <% } else {
  %>
  Prev
  <% } %>
  </td><td align="center"><a href="<%=$this_page%>">Top</a></td><td align="right">
  <%
  	if ($next) {
  %>
  <a href="<%=$this_page%>?section=<%=$next%>">Next</a>
  <% } else { %>
  Next
  <% } %>
  </td></tr></table>
  <hr>
  
  <%
  	# display particular page
  	foreach my $section (findnodes("/article/sect1[$sect]")) {
  		# should only be 1
  		print apply_templates($section);
  	}
  
  if (@footnotes) {
      print "<table cellspacing=\"0\" cellpadding=\"3\">";
      print "<caption>Footnotes</caption>";
      my $i = 1;
      foreach my $footnote (@footnotes) {
          print "<tr><td valign=\"top\"><p>$i</p></td><td valign=\"top\"><div class=\"footnote\">$footnote</div></td></tr>";
          $i++;
      }
      print "</table><hr>";
  }
  
  }
  
  if ($sect) {
  %>
  <table width="100%">
  <tr>
  <td align="left">
  <%
  	if ($prev) {
  %>
  <a href="<%=$this_page%>?section=<%=$prev%>">Prev</a>
  <% } else {
  %>
  Prev
  <% } %>
  </td><td align="center"><a href="<%=$this_page%>">Top</a></td><td align="right">
  <%
  	if ($next) {
  %>
  <a href="<%=$this_page%>?section=<%=$next%>">Next</a>
  <% } else { %>
  Next
  <% } %>
  </td></tr></table>
  <hr>
  <% } 
  %>
  <table width="100%"><tr><td align="right">
  <a href="<%=$this_page%>?style=print">Printer Friendly</a>
  </td></tr>
  <tr><td align="right">
  <a href="<%=$this_page%>?passthru=1">Raw XML</a>
  </td></tr></table>
  
  </td>
  
  <!-- whitespace -->
  <td align="left" valign="top" width="10"><%= spacer(10,1) %></td>
  
  </tr>
  </table>
  
  </body>
  </html>
  
  
  
  1.1                  xml-axkit/axkit.org/stylesheets/docbook_tags.xps
  
  Index: docbook_tags.xps
  ===================================================================
  <%
  
  # support ulink URL's
  $t->{'ulink'}{testcode} = sub { 
  		my $node = shift;
  		my $t = shift;
  		$t->{pre} = "<i><a href=\"" .
  						findvalue('./@url', $node) . "\">";
  		$t->{post} = '</a></i>';
  		return 1;
  	};
  
  $t->{'title'}{testcode} = sub { 
  		my $node = shift;
  		my $t = shift;
  		if (findvalue('parent::blockquote', $node)) {
  			$t->{pre} = "<b>";
  			$t->{post} = "</b><br>\n";
  		}
  		elsif (findvalue('parent::artheader', $node)) {
  			$t->{pre} = "<h1>";
  			$t->{post} = "</h1>";
  		}
  		else {
  			my $parent = findvalue('name(..)', $node);
  			my ($level) = $parent =~ m/sect(\d+)$/;
  			$t->{pre} = "<h$level>";
  			$t->{post} = "</h$level>";
  		}
  		
  		return 1;
  	};
  
  $t->{'attribution'}{pre} = "<i>";
  $t->{'attribution'}{post} = "</i><br>\n";
  
  $t->{'para'}{pre} = '<p>';
  $t->{'para'}{post} = '</p>';
  
  $t->{'author'}{pre} = '<p>';
  
  $t->{'firstname'}{pre} = '';
  $t->{'surname'}{pre} = ' ';
  $t->{'surname'}{post} = "<p>\n";
  
  $t->{'email'}{testcode} = sub {
  		my $node = shift;
  		$t->{'email'}{pre} = "<a href=\"mailto:" . findvalue('./text()', $node) . "\">";
  		$t->{'email'}{post} = "</a>";
  		return 1;
  		};
  
  $t->{'copyright'}{pre} = "Copyright \xC2\xA9 ";
  $t->{'trademark'}{post} = "<super>TM</super>";
  $t->{'holder'}{testcode} = sub {
  		my $node = shift;
  		$t->{'holder'}{pre} = "<a href=\"" . findvalue('./@role', $node) . "\">";
  		$t->{'holder'}{post} = "</a>";
  		return 1;
  		};
  
  $t->{'abstract'}{pre} = "<p>";
  
  $t->{'sect1'}{post} = "<hr>";
  
  #$XML::XPath::Debug = 1;
  $t->{'sect1'}{testcode} = sub {
  		my ($node, $t) = @_;
  		if (my $id = findvalue('@id', $node)) {
  			$t->{pre} = "<a name=\"$id\"></a>";
  		}
  		return 1;
  	};
  
  $t->{'sect2'}{testcode} = $t->{'sect1'}{testcode};
  $t->{'sect3'}{testcode} = $t->{'sect1'}{testcode};
  $t->{'sect4'}{testcode} = $t->{'sect1'}{testcode};
  
  # lists
  
  $t->{'itemizedlist'}{pre} = '<ul>';
  $t->{'itemizedlist'}{post} = '</ul>';
  
  $t->{'orderedlist'}{pre} = '<ol>';
  $t->{'orderedlist'}{post} = '</ol>';
  
  $t->{'listitem'}{testcode} = sub {
      my ($node, $t2) = @_;
      $t2->{pre} = '<li>';
      $t2->{post} = '</li>';
      
      return 1;
  #    return '*[1]/node() | *[position() > 1]';
  };
  
  $t->{'procedure'}{pre} = '<ol>';
  $t->{'procedure'}{post} = '</ol>';
  
  $t->{'step'}{pre} = '<li>';
  $t->{'step'}{post} = '</li>';
  
  # examples, listings, etc.
  
  $t->{'programlisting'}{pre} = '<table width="100%"><tr><td bgcolor="#DDDDDD"><pre>';
  $t->{'programlisting'}{post} = '</pre></td></tr></table>';
  
  # need to support <prompt> and other tags within screen...
  $t->{'screen'} = $t->{'programlisting'};
  
  $t->{'userinput'}{pre} = '<b>';
  $t->{'userinput'}{post} = '</b>';
  
  $t->{'replaceable'}{pre} = '<i>';
  $t->{'replaceable'}{post} = '</i>';
  
  $t->{'emphasis'}{pre} = '<i>';
  $t->{'emphasis'}{post} = '</i>';
  
  $t->{'application'}{pre} = '<b>';
  $t->{'application'}{post} = '</b>';
  
  $t->{'manvolnum'}{pre} = '(';
  $t->{'manvolnum'}{post} = ')';
  
  $t->{'option'}{pre} = '<tt>';
  $t->{'option'}{post} = '</tt>';
  
  $t->{'filename'} = $t->{'option'};
  $t->{'literal'} = $t->{'option'};
  
  # references
  $t->{'anchor'}{testcode} = sub {
  		my ($node, $t) = @_;
  		$t->{pre} = '<a name="#';
  		$t->{pre} .= findvalue('@id', $node);
  		$t->{pre} .= '">';
  		$t->{post} = '</a>';
  		return 1;
  	};
  	
  $t->{'xref'}{testcode} = sub {
  		my ($node, $t) = @_;
  		$t->{pre} = '<b><a href="#';
  		my $id = findvalue('@linkend', $node);
  		$t->{pre} .= $id . '">';
  		$t->{pre} .= findvalue("id('$id')/title/text()");
  		$t->{post} = '</a></b>';
  		return -1;
  	};
  
  $t->{'link'}{testcode} = sub {
  		my ($node, $t) = @_;
  		$t->{pre} = '<a href="#';
  		$t->{pre} .= findvalue('@linkend', $node);
  		$t->{pre} .= '">';
  		$t->{post} = '</a>';
  		return 1;
  	};
  
  my @footnotes;
  
  $t->{'footnote'}{testcode} = sub {
      my ($node, $t2) = @_;
      push @footnotes, apply_templates('node()', $node);
      $t2->{pre} = '<sup>' . scalar(@footnotes) . '</sup>';
      return -1;
  };
  
  %>
  
  
  
  1.1                  xml-axkit/axkit.org/stylesheets/error.xps
  
  Index: error.xps
  ===================================================================
  <html>
  	<head><title>AxKit Error</title></head>
  <body>
  	<h1>AxKit Error</h1>
  	<tt>
  	<%= findvalue('/error/msg/text()') %>
  	</tt>
  	<hr>
  	<pre>
  	<% foreach my $node (findnodes('/error/stack_trace/bt')) { %>
  	<%= $node->findvalue('file') . ' :: ' . $node->findvalue('line') %>
  	<br>
  	<% } %>
          </pre>
  </body>
  </html>
  
  
  
  
  1.1                  xml-axkit/axkit.org/stylesheets/error.xsl
  
  Index: error.xsl
  ===================================================================
  <?xml version="1.0"?>
  <xsl:stylesheet
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      version="1.0"
  >
  
  <xsl:output method="html"/>
  
  <xsl:template match="bt">
  <tr>
      <xsl:if test="position() mod 2">
          <xsl:attribute name="bgcolor">#eeeeee</xsl:attribute>
      </xsl:if>
      <td>
          <xsl:value-of select="@level"/>
      </td>
      <xsl:apply-templates/>
  </tr>
  </xsl:template>
  
  <xsl:template match="file|line">
      <td>
          <xsl:apply-templates/>
      </td>
  </xsl:template>
  
  <xsl:template match="/">
  <html>
  <head>
  <title>Server Error</title>
  <style type="text/css">
  h2, h3, h4, p, i, td, th
     	{
  		font-family: Verdana, Helvetica, sans-serif;
  	}
        th
          {
            color: white;
          }
      </style>
  </head>
  <body bgcolor="white">
  <h2>Server Error</h2>
  <p>
  The following error occurred: <xsl:value-of select="/error/msg"/>
  </p>
  
  <h3>Stack Trace:</h3>
  <table border="0" cellpadding="3" cellspacing="0">
  
  <tr bgcolor="blue"><th>Level</th><th>File</th><th>Line #</th></tr>
  
      <xsl:apply-templates select="/error/stack_trace/*"/>
  
  </table>
  </body>
  </html>
  
  </xsl:template>
  
  </xsl:stylesheet>
  
  
  
  1.1                  xml-axkit/axkit.org/stylesheets/irc.xps
  
  Index: irc.xps
  ===================================================================
  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/strict.dtd">
  <html>
  <!--#include file="sidebar_html.xps"-->
  <!--#include file="rdf_html.xps"-->
  <!--#include file="spacer.xps"-->
  <%
  
  $t->{'dc:description'}{showtag} = 0;
  
  $t->{'link'}{pre} = '<a href="{@url}">';
  $t->{'link'}{post} = '</a>';
  
  $t->{'xlink'}{pre} = '<a href="{@uri}">';
  $t->{'xlink'}{post} = '</a>';
  
  $t->{'blog:comments'}{showtag} = 0;
  
  $t->{'rdf:Seq'}{testcode} = sub {'rdf:li'};
  
  $t->{'rdf:li'}{showtag} = 0;
  $t->{'rdf:li'}{pre} = '<div class="by">{dc:creator} : ';
  $t->{'rdf:li'}{post} = '</div>';
  
  $t->{'dc:creator'}{testcode} = sub {0};
  
  %>
  <head>
  <title><%= findvalue('/rdf:RDF/channel[1]/title') %></title>
  <meta name="Author" content="Matt Sergeant" />
  <link rel="stylesheet" href="/stylesheets/axkit.css" type="text/css" />
  </head>
  
  <!--#include file="body_tag_html.xps"-->
  
  <!-- navbar and content positioning table -->
  
  
  <table width="100%" border="0" cellspacing="0" cellpadding="0">
  <tr>
  <!-- navbar -->
  <td align="left" valign="top" width="172">
  
  <!--#include file="navbar_html.xps"-->
  
  </td>
  
  <!-- whitespace -->
  <td align="left" valign="top" width="10"><%= spacer(10,1) %></td>
  
  <!-- content -->
  <td align="left" valign="top">
  
      <table width="100%" border="0" cellspacing="5" cellpadding="0">
  
      <!-- toponomic marker -->
      <tr>
      <td align="left" valign="top">
      <span class="toponomic">
  <a href="http://axkit.org/">http://axkit.org/</a>
  ::
  <%
  {
  my $uri = $r->uri;
  $uri =~ s/^\///;
  my ($file) = $uri =~ /([^\/]+)$/;
  $uri =~ s/\Q$file\E$//;
  my $local = '';
  foreach my $part (split('/', $uri)) {
  	$local .= "/$part/index.xml";
  	my $title = findvalue("document('$local')/descendant::title[1]");
  	if ($title eq 'Index') {
  		print $part, " :: ";
  	}
  	else {
  		print "<a href=\"$local\">", $title, "</a> :: ";
  	}
  	$local =~ s/\/index\.xml$//;
  }
  if ($file ne 'index.xml') {
  	my $location = "/$file";
  	$location = "/$uri$file" if $uri;
  	print "<a href=\"$location\">", findvalue("document('$location')/descendant::title[1]"), "</a> :: ";
  }
  }
  %>
  	</span>
      </td>
      </tr>
  
      <!-- line -->
      <tr>
      <td bgcolor="#660099" style="line-height: 0;"><%= spacer(20,1) %></td>
      </tr>
  
      <tr>
      <td align="left" valign="top">
  
          <table width="100%" border="0" cellspacing="5" cellpadding="0">
          <tr>
  
          <!-- main text -->
          <td align="left" valign="top">
          <h1>Daily Churn</h1>
          <p>
          This page contains URLs and comments talked about on the 
          <a href="/support.xml">AxKit IRC channel</a>. The information is recorded
          by an IRC bot written using POE, and written out to an RSS 1.0 file, using
          RDF and dublin core metadata.
          </p>
          <% 
          my $count = 30;
          foreach my $item (findnodes("/rdf:RDF/item[position() <= $count]")) {
              if ($item->findvalue('link')) {
              %>
              <div class="hd"><%= $item->findvalue('dc:creator') %> : <a href="<%= $item->findvalue('link') %>"><%= $item->findvalue('title') %></a></div>
              <%
              }
              else {
              %>
              <div class="hd"><%= $item->findvalue('title') %></div>
              <%
              }
              %>
  <%= apply_templates($item->findnodes('blog:comments')) %>
              <br /><br />
          <% } %>
          <!-- end main -->
          </td>
          
          </tr>
          </table>
  
      </td>
      </tr>
  
      <!-- line -->
      <tr>
      <td bgcolor="#660099" style="line-height: 0;"><%= spacer(20,1) %></td>
      </tr>
  
  
      <!-- copyright -->
      <tr>
      <td align="left" valign="top">
      <span class="copyright">These pages are copyright &copy; 2000 AxKit.com Ltd</span>
      </td>
      </tr>
  
  
  
      </table>
  
  
  </td>
  
  <!-- whitespace -->
  <td align="left" valign="top" width="10"><%= spacer(10,1) %></td>
  
  </tr>
  </table>
  
  </body>
  </html>
  
  
  
  1.1                  xml-axkit/axkit.org/stylesheets/navbar_html.xps
  
  Index: navbar_html.xps
  ===================================================================
  <table width="170" border="0" cellspacing="0" cellpadding="0">
  <tr>
      <td colspan="3" style="line-height: 0;"><img
          src="/img/vsep.gif" width="170" height="25" border="0" vspace="0" hspace="0" alt="--sep--" /></td>
  </tr>
  
  <tr>
      <td colspan="3" style="line-height: 0;"><img
          src="/img/nav_top.gif" width="170" height="12" border="0" vspace="0" hspace="0" alt="Start Navigation" /></td>
  </tr>
  
  <tr>
      <td background="/img/nav_left.gif"><%= spacer(8,20) %></td>
  
      <td bgcolor="#330066">
          <!-- actual navigation -->
          <%= apply_templates(scalar findnodes('document("/sidebar.xml")')) %>
      </td>
  
      <td background="/img/nav_right.gif"><%= spacer(8,20) %></td>
  </tr>
  
  <tr>
      <td colspan="3" style="line-height: 0;"><img
          src="/img/nav_bot.gif" width="170" height="12" border="0" vspace="0" hspace="0" alt="End Navigation" /></td>
  </tr>
  </table>
  
  
  
  1.1                  xml-axkit/axkit.org/stylesheets/rdf_html.xps
  
  Index: rdf_html.xps
  ===================================================================
  <%
  
  $t->{'rss'}{testcode} = sub {
  	my ($node, $t) = @_;
  	
  	$t->{'pre'} = q(<table width="190" border="0" cellspacing="0" cellpadding="0">
              <tr><td align="center" bgcolor="#330066">
              <table width="100%" border="0" cellspacing="1" cellpadding="0">
              <tr>
              <td align="center" bgcolor="#9966cc"><table border="0" cellpadding="0"
  			cellspacing="0"><tr><td bgcolor="#660099">) . spacer(1,1) . q(</td><td align="center" bgcolor="white"><a href=");
  	$t->{'pre'} .= findvalue('channel/image/link/text()', $node);
  	$t->{'pre'} .= q("><img src=");
  				
  	$t->{'pre'} .= findvalue('channel/image/url/text()', $node);
  	
  	$t->{'pre'} .= q(" border="0" vspace="0" hspace="0" alt=");
  	$t->{'pre'} .= findvalue('channel/image/title/text()', $node);
  	
  	$t->{'pre'} .= q(" /></a></td><td bgcolor="#660099">) . spacer(1,1) . q(</td></tr></table></td>
  
              </tr>
  
              <tr>
              <td>
  
                  <table width="100%" border="0" cellspacing="0" cellpadding="5" bgcolor="#ffffff">
                  <tr>
                  <td align="left" valign="top">);
  	
  	$t->{'post'} = q(</td>
                  </tr>
                  </table>
  
              </td>
              </tr>
              </table>
              </td></tr></table>);
  	
  	return 'channel/item[position() < 5]';
  };
  
  $t->{'item'}{testcode} = sub {
  	my ($node, $t) = @_;
  	if ($node->getParentNode->getName ne 'channel') {
  		return 1;
  	}
  	
  	$t->{'pre'} = '<p><img src="/img/bullet.gif" width="10" height="10" border="0" vspace="0" hspace="0" alt=">>" />
  	<a href="' . findvalue('link/text()', $node) . '"><b>' .
  	findvalue('title/text()', $node) . '</b></a> - ' .
  	findvalue('description/text()', $node);
  	
  	$t->{'post'} = "</p>\n\n";
  	
  	return -1;
  };
  
  %>
  
  
  
  1.1                  xml-axkit/axkit.org/stylesheets/sidebar_html.xps
  
  Index: sidebar_html.xps
  ===================================================================
  <%
  $t->{'sb:sidebar'}{pre} = q(<table width="154" border="0" cellspacing="0" cellpadding="0" bgcolor="#330066">
          <tr>
          <td>
              <table width="100%" border="0" cellspacing="1" cellpadding="3">);
  			
  $t->{'sb:sidebar'}{post} = '</table></td></tr></table>';
  
  $t->{'sb:section'}{pre} = q(<tr>);
  $t->{'sb:section'}{post} = q(</span></td></tr>);
  
  $t->{'sb:title'}{pre} = q(<td align="left" valign="top" bgcolor="#eeddff">
  <span class="toplevel"><b>);
  $t->{'sb:title'}{post} = q(</b></span></td></tr> <tr><td align="left" valign="top" bgcolor="#ffffff">
  <span class="sublevel">);
  
  $t->{'sb:item'}{testcode} = sub {
  	my ($node, $t) = @_;
  	my $title = findvalue('sb:title/text()', $node);
  	my $url = findvalue('sb:url/text()', $node);
  	my $sel = '';
  	$sel = '_sel' if $r->uri eq $url;
  	$t->{pre} = qq(<a href="$url"><img src="/img/arrow$sel.gif" width="16" height="14" border="0" 
  	vspace="0" hspace="0" alt="" align="absmiddle" />$title</a><br />);
  	return -1;
  };
  
  my $sbsubpre = q(<table width="100%" border="0" cellspacing="0" cellpadding="0">
  <tr><td>) . spacer(10, 1) . q(</td><td><span class="sublevel">); # "
  
  $t->{'sb:subsection'}{testcode} = sub {
  	my ($node, $t) = @_;
  	if ($node->findvalue('@expand') eq "no") {
  		my $title = findvalue('sb:title/text()', $node);
  		my $url = findvalue('sb:url/text()', $node);
  		my $sel = '';
  		$sel = '_sel' if $r->uri eq $url;
  		$t->{pre} = qq(<a href="$url"><img src="/img/arrow$sel.gif" width="16" height="14" border="0"
  		vspace="0" hspace="0" alt="" align="absmiddle" />$title</a><br />);
  		if ($r->uri eq $url) {
  			$t->{pre} .= $sbsubpre;
  			return 'sb:item';
  		}
  		$t->{post} = '';
  		return -1;
  	}
  	else {
  		return 1;
  	}
  };
  
  $t->{'sb:subsection'}{pre} = $sbsubpre;
  $t->{'sb:subsection'}{post} = q(</span></td></tr></table>);
  
  %>
  
  
  
  1.1                  xml-axkit/axkit.org/stylesheets/spacer.xps
  
  Index: spacer.xps
  ===================================================================
  <%
  sub spacer {
  	my ($width, $height, $align) = @_;
  	return '<img src="/img/pix.gif" 
  	width="'.$width.'" height="'.$height.'"' . ($align ? " align=\"$align\"" : "") .
   ' border="0" vspace="0" hspace="0" 
  	alt="" />';
  }
  %>
  
  
  
  1.1                  xml-axkit/axkit.org/stylesheets/webpage_html.xps
  
  Index: webpage_html.xps
  ===================================================================
  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/strict.dtd">
  <html>
  <!--#include file="sidebar_html.xps"-->
  <!--#include file="rdf_html.xps"-->
  <!--#include file="spacer.xps"-->
  <%
  $t->{'section'}{showtag} = 0;
  
  $t->{'title'}{testcode} = sub {
  	my ($node, $t) = @_;
  	if ($node->getParentNode->getName eq 'section') {
  		$t->{pre} = '<h1>';
  		$t->{post} = '</h1>';
  	}
  	elsif ($node->getParentNode->getName eq 'subsection') {
  		$t->{pre} = '<h2>';
  		$t->{post} = '</h2>';
  	}
  	else {
  		$t->{showtag} = 1;
  	}
  	return 1;
  };
  
  $t->{'ulink'}{testcode} = sub {
  	my ($node, $t) = @_;
  	my $url = $node->getAttribute('url');
  	$t->{pre} = "<a href=\"$url\">";
  	$t->{post} = "</a>";
  	return 1;
  };
  
  $t->{'para'}{pre} = '<p>';
  $t->{'para'}{post} = '</p>';
  
  $t->{'emphasis'}{pre} = '<i>';
  $t->{'emphasis'}{post} = '</i>';
  
  %>
  <head>
  <title><%= findvalue('/webpage/head/title/text()') %></title>
  <meta name="Author" content="Matt Sergeant" />
  <link rel="stylesheet" href="/stylesheets/axkit.css" type="text/css" />
  </head>
  
  <!--#include file="body_tag_html.xps"-->
  
  <!-- navbar and content positioning table -->
  
  
  <table width="100%" border='0' cellspacing='0' cellpadding='0'>
  <tr>
  <!-- navbar -->
  <td align="left" valign="top" width='172'>
  
  <!--#include file="navbar_html.xps"-->
  
  </td>
  
  <!-- whitespace -->
  <td align="left" valign="top" width="10"><%= spacer(10,1) %></td>
  
  <!-- content -->
  <td align="left" valign="top">
  
      <table width="100%" border="0" cellspacing="5" cellpadding="0">
  
      <!-- toponomic marker -->
      <tr>
      <td align="left" valign="top">
      <span class="toponomic">
  <a href="http://axkit.org/">http://axkit.org/</a>
  ::
  <%
  {
  my $uri = $r->uri;
  $uri =~ s/^\///;
  my ($file) = $uri =~ /([^\/]+)$/;
  $uri =~ s/\Q$file\E$//;
  my $local = '';
  foreach my $part (split('/', $uri)) {
  	$local .= "/$part/index.xml";
  	my $title = findvalue("document('$local')/descendant::title[1]");
  	if ($title eq 'Index') {
  		print $part, " :: ";
  	}
  	else {
  		print "<a href=\"$local\">", $title, "</a> :: ";
  	}
  	$local =~ s/\/index\.xml$//;
  }
  if ($file ne 'index.xml') {
  	my $location = "/$file";
  	$location = "/$uri$file" if $uri;
  	print "<a href=\"$location\">", findvalue("document('$location')/descendant::title[1]"), "</a> :: ";
  }
  }
  %>
  	</span>
      </td>
      </tr>
  
      <!-- line -->
      <tr>
      <td bgcolor="#660099" style="line-height: 0;"><%= spacer(20,1) %></td>
      </tr>
  
      <tr>
      <td align="left" valign="top">
  
          <table width="100%" border="0" cellspacing="5" cellpadding="0">
          <tr>
  
          <!-- main text -->
          <td align="left" valign="top">
  		<%= apply_templates('/webpage/body/*') %>
  		</td>
  		
  		<%
  		if ($r->uri eq '/index.xml') {
  		%>
  		<td align="left" valign="top" width="170">
                  <%= apply_templates('document("news.xml")') %>
  		</td>
  		<% } %>
          </tr>
          </table>
  
      </td>
      </tr>
  
      <!-- line -->
      <tr>
      <td bgcolor="#660099" style="line-height: 0;"><%= spacer(20,1) %></td>
      </tr>
  
  
      <!-- copyright -->
      <tr>
      <td align="left" valign="top">
      <span class="copyright">These pages are copyright &copy; 2000 AxKit.com Ltd</span>
      </td>
      </tr>
  
  
  
      </table>
  
  
  </td>
  
  <!-- whitespace -->
  <td align="left" valign="top" width="10"><%= spacer(10,1) %></td>
  
  </tr>
  </table>
  
  </body>
  </html>
  
  
  
  1.1                  xml-axkit/axkit.org/stylesheets/new/rdf_html.xps
  
  Index: rdf_html.xps
  ===================================================================
  <%
  
  $t->{'rss'}{testcode} = sub {
  	my ($node, $t) = @_;
  	
  	warn "Executing RSS code!\n";
  	
  	$t->{'pre'} = q(<table width='190' border='0' cellspacing='0' cellpadding='0'>
              <tr><td align='center' bgcolor='#330066'>
              <table width='100%' border='0' cellspacing='1' cellpadding='0'>
              <tr>
              <td align='center' bgcolor='#9966cc'><table border='0' cellpadding='0'
  			cellspacing='0'><tr><td bgcolor='#660099'>) . spacer(1,1) . q(</td><td bgcolor='white'><a href=');
  	$t->{'pre'} .= findvalue('channel/image/link/text()', $node);
  	$t->{'pre'} .= q('><img src=');
  				
  	$t->{'pre'} .= findvalue('channel/image/url/text()', $node);
  	
  	$t->{'pre'} .= q(' border='0' vspace='0' hspace='0' alt=');
  	$t->{'pre'} .= findvalue('channel/image/title/text()', $node);
  	
  	$t->{'pre'} .= q(' /></a></td><td bgcolor='#660099'>) . spacer(1,1) . q(</td></tr></table></td>
  
              </tr>
  
              <tr>
              <td>
  
                  <table width='100%' border='0' cellspacing='0' cellpadding='5' bgcolor='#ffffff'>
                  <tr>
                  <td align='left' valign='top'>);
  	
  	$t->{'post'} = q(</td>
                  </tr>
                  </table>
  
              </td>
              </tr>
              </table>
              </td></tr></table>);
  	
  	return 'channel/item[position() < 5]';
  };
  
  $t->{'item'}{testcode} = sub {
  	my ($node, $t) = @_;
  	if ($node->getParentNode->getName ne 'channel') {
  		return 1;
  	}
  	
  	$t->{'pre'} = '<p><img src="/img/bullet.gif" width="10" height="10" border="0" vspace="0" hspace="0" alt=">>" />
  	<a href="' . findvalue('link/text()', $node) . '"><b>' .
  	findvalue('title/text()', $node) . '</b></a> - ' .
  	findvalue('description/text()', $node);
  	
  	$t->{'post'} = "</p>\n\n";
  	
  	return -1;
  };
  
  %>
  
  
  
  1.1                  xml-axkit/axkit.org/stylesheets/new/sidebar_html.xps
  
  Index: sidebar_html.xps
  ===================================================================
  <%
  $t->{'sb:sidebar'}{pre} = q(<table width='154' border='0' cellspacing='0' cellpadding='0' bgcolor='#330066'>
          <tr>
          <td>
              <table width='100%' border='0' cellspacing='1' cellpadding='3'>);
  			
  $t->{'sb:sidebar'}{post} = '</table></td></tr></table>';
  
  $t->{'sb:section'}{pre} = q(<tr>);
  $t->{'sb:section'}{post} = q(</td></tr>);
  
  $t->{'sb:title'}{pre} = q(<td align='left' valign='top' bgcolor='#eeddff'>
  <span class='toplevel'><b>);
  $t->{'sb:title'}{post} = q(</b></span></td></tr> <tr><td align='left' valign='top' bgcolor='#ffffff'>
  <span class='sublevel'>);
  
  $t->{'sb:item'}{testcode} = sub {
  	my ($node, $t) = @_;
  	my $title = findvalue('sb:title/text()', $node);
  	my $url = findvalue('sb:url/text()', $node);
  	my $sel = '';
  	$sel = '_sel' if $r->uri eq $url;
  	$t->{pre} = "<a href=\"$url\"><img src='/img/arrow$sel.gif' width='16' height='14' border='0' 
  	vspace='0' hspace='0' alt='' align='absmiddle' />$title</a><br />";
  	return -1;
  };
  
  $t->{'sb:subsection'}{testcode} = sub {
  	my ($node, $t) = @_;
  	if (findvalue('@expand', $node) eq "no") {
  		my $title = findvalue('sb:title/text()', $node);
  		my $url = findvalue('sb:url/text()', $node);
  		my $sel = '';
  		$sel = '_sel' if $r->uri eq $url;
  		$t->{pre} = "<a href=\"$url\"><img src=\"/img/arrow$sel.gif\" width='16' height='14' border='0'
  		vspace='0' hspace='0' alt='' align='absmiddle' />$title</a><br />";
  		$t->{post} = '';
  		return -1;
  	}
  	else {
  		return 1;
  	}
  };
  
  $t->{'sb:subsection'}{pre} = q(<table width="100%" border="0" cellspacing="0" cellpadding="0">
  <tr><td width="10">) . spacer(10, 1) . q(</td><td>);
  $t->{'sb:subsection'}{post} = q(</td></tr></table>);
  
  %>
  
  
  
  1.1                  xml-axkit/axkit.org/stylesheets/new/webpage_html.xps
  
  Index: webpage_html.xps
  ===================================================================
  <!--#include file="sidebar_html.xps"-->
  <!--#include file="rdf_html.xps"-->
  <%
  $t->{'section'}{showtag} = 0;
  
  $t->{'title'}{testcode} = sub {
  	my ($node, $t) = @_;
  	if ($node->getParentNode->getName eq 'section') {
  		$t->{pre} = '<h1>';
  		$t->{post} = '</h1>';
  	}
  	elsif ($node->getParentNode->getName eq 'subsection') {
  		$t->{pre} = '<h2>';
  		$t->{post} = '</h2>';
  	}
  	else {
  		$t->{showtag} = 1;
  	}
  	return 1;
  };
  
  $t->{'ulink'}{testcode} = sub {
  	my ($node, $t) = @_;
  	my $url = $node->getAttribute('url');
  	$t->{pre} = "<a href=\"$url\">";
  	$t->{post} = "</a>";
  	return 1;
  };
  
  $t->{'para'}{pre} = '<p>';
  $t->{'para'}{post} = '</p>';
  
  $t->{'emphasis'}{pre} = '<i>';
  $t->{'emphasis'}{post} = '</i>';
  
  sub spacer {
  	my ($width, $height, $align) = @_;
  	return "<img src='/img/pix.gif' 
  	width='$width' height='$height'" . ($align ? " align='$align'" : '') .
   " border='0' vspace='0' hspace='0' 
  	alt=''>";
  }
  
  %>
  <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
  <html>
  <head>
  <title><%= findvalue('/webpage/head/title/text()') %></title>
  <meta name='Author' content='Matt Sergeant'>
  <link rel='stylesheet' href='/stylesheets/axkit.css' type='text/css' />
  </head>
  
  <body 
  bgcolor='#ffffff' text='#333333' link='#9966cc' vlink='#330066' alink='#eeddff' 
  marginheight='0' marginwidth='0' 
  topmargin='0' leftmargin='0' background='/img/bg.gif'>
  
  
  <!-- banner table -->
  
  
  <table width='100%' border='0' cellspacing='0' cellpadding='0'>
  
  <tr style='line-height: 0;'>
  <td align='left' valign='top' width='352' background='/img/banner_bg.gif'><img
  	src='/img/banner2.gif' width='352' height='84' border='0' vspace='0' hspace='0' alt='AxKit.org [logo curtesy of http://xml.com]' /></td>
  
  
  <td align='right' valign='top' width='100%' background='/img/banner_bg.gif'><img
  	src='/img/banner_end.gif' width='26' height='84' border='0' vspace='0' hspace='0' alt='' /></td>
  </tr>
  </table>
  
  <!-- navbar and content positioning table -->
  
  
  <table width='100%' border='0' cellspacing='0' cellpadding='0'>
  <tr>
  <!-- navbar -->
  <td align='left' valign='top' width='170'>
  
      <table width='100%' border='0' cellspacing='0' cellpadding='0'>
      <tr>
      <td colspan='3' style='line-height: 0;'><img
          src='/img/vsep.gif' width='170' height='25' border='0' vspace='0' hspace='0' alt='--sep--' /></td>
      </tr>
  
      <tr>
      <td colspan='3' style='line-height: 0;'><img
          src='/img/nav_top.gif' width='170' height='12' border='0' vspace='0' hspace='0' alt='Start Navigation' /></td>
      </tr>
  
  
      <tr>
      <td background='/img/nav_left.gif'><%= spacer(8,20) %></td>
  
  
  
      <td bgcolor='#330066'>
          <!-- actual navigation -->
  		
  		<%= apply_templates('document("/sidebar_new.xml")') %>
  		
  	</td>
  	
      <td background='/img/nav_right.gif'><%= spacer(8,20) %></td>
      </tr>
  
      <tr>
      <td colspan='3' style='line-height: 0;'><img
          src='/img/nav_bot.gif' width='170' height='12' border='0' vspace='0' hspace='0' alt='End Navigation' /></td>
      </tr>
      </table>
  
  </td>
  
  <!-- whitespace -->
  <td align='left' valign='top' width='10'><%= spacer(10,1) %></td>
  
  <!-- content -->
  <td align='left' valign='top'>
  
      <table width='100%' border='0' cellspacing='5' cellpadding='0'>
  
      <!-- toponomic marker -->
      <tr>
      <td align='left' valign='top'>
      <span class='toponomic'>
  <a href="http://axkit.org/">http://axkit.org/</a>
  ::
  <%
  {
  my $uri = $r->uri;
  $uri =~ s/^\///;
  my ($file) = $uri =~ /([^\/]+)$/;
  $uri =~ s/\Q$file\E$//;
  my $local = '';
  foreach my $part (split('/', $uri)) {
  	$local .= "/$part/index.xml";
  	my $title = findvalue("document('$local')//title[1]/text()");
  	if ($title eq 'Index') {
  		print $part, " :: ";
  	}
  	else {
  		print "<a href=\"$local\">", , "</a> :: ";
  	}
  	$local =~ s/\/index\.xml$//;
  }
  if ($file ne 'index.xml') {
  	my $location = "/$file";
  	$location = "/$uri$file" if $uri;
  	print "<a href=\"$location\">", findvalue("document('$location')//title[1]/text()"), "</a> :: ";
  }
  }
  %>
  	</span>
      </td>
      </tr>
  
      <!-- line -->
      <tr>
      <td bgcolor='#660099' style='line-height: 0;'><%= spacer(20,1) %></td>
      </tr>
  
      <tr>
      <td align='left' valign='top'>
  
          <table width='100%' border='0' cellspacing='5' cellpadding='0'>
          <tr>
  
          <!-- main text -->
          <td align='left' valign='top'>
  		<%= apply_templates('/webpage/body/*') %>
  		</td>
  		
  		<%
  		if ($r->uri eq '/index.xml') {
  		%>
  		<td align='left' valign='top' width='170'>
  			<%= apply_templates('document("news.xml")') %>
  		</td>
  		<% } %>
          </tr>
          </table>
  
      </td>
      </tr>
  
      <!-- line -->
      <tr>
      <td bgcolor='#660099' style='line-height: 0;'><%= spacer(20,1) %></td>
      </tr>
  
  
      <!-- copyright -->
      <tr>
      <td align='left' valign='top'>
      <span class='copyright'>These pages are copyright &copy; 2000 AxKit.com Ltd</span>
      </td>
      </tr>
  
  
  
      </table>
  
  
  </td>
  
  <!-- whitespace -->
  <td align='left' valign='top' width='10'><%= spacer(10,1) %></td>
  
  </tr>
  </table>
  
  </body>
  </html>
  
  
  
  1.1                  xml-axkit/examples/.htaccess
  
  Index: .htaccess
  ===================================================================
  SetHandler perl-script
  PerlHandler AxKit
  
  AxAddStyleMap application/x-xpathscript Apache::AxKit::Language::XPathScript
  AxAddStyleMap text/xsl Apache::AxKit::Language::Sablot
  
  
  
  1.1                  xml-axkit/examples/default.notxslt
  
  Index: default.notxslt
  ===================================================================
  <?xml version="1.0" encoding="ISO-8859-1"?>
  <html xmlns:t="http://sergeant.org/notxslt"
  	xmlns="http://www.w3.org/1999/xhtml">
  <head>
  	<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  	<title><t:select match="/page/head/title/text()"/></title>
  </head>
  <body bgcolor="white">
  	<h1><t:select match="/page/head/title/text()"/></h1>
  	
  	<t:exec match="set_var('title_no', 0)"/>
  	
  	<t:for-each match="/page/body/section">
  		<t:exec match="set_var('title_no', $title_no + 1)"/>
  		<t:select match="concat('&lt;a href=&quot;#', $title_no, '&quot;>')"/>
  		<t:select match="./title/text()"/>
  		<t:select match="'&lt;/a>'"/>
  		<br class="hack"/>
  		<t:exec match="set_var('subtitle_no', 0)"/>
  		<t:for-each match="./subsection">
  			<t:exec match="set_var('subtitle_no', $subtitle_no + 1)"/>
  			<t:select match="concat(' - &lt;a href=&quot;#', $title_no, '.', $subtitle_no, '&quot;>')"/>
  			<t:select match="./title/text()"/>
  			<t:select match="'&lt;/a>'"/>
  			<br class="hack"/>
  		</t:for-each>
  	</t:for-each>
  	
  	
  	<t:exec match="set_var('title_no', 0)"/>
  	
  	<t:for-each match="/page/body/section">
  		<t:exec match="set_var('title_no', $title_no + 1)"/>
  		<t:select match="concat('&lt;a name=&quot;', $title_no, '&quot;>&lt;/a>')"/>
  		<h2><t:select match="./title/text()"/></h2>
  		<t:for-each match="./title">
  			<t:exec match="set_var('title_pos', position())"/>
  		</t:for-each>
  		<t:select match="./*[(position() > 2) and ( name() != 'subsection' )]"/>
  		
  		<t:exec match="set_var('subtitle_no', 0)"/>
  		<t:for-each match="./subsection">
  			<t:exec match="set_var('subtitle_no', $subtitle_no + 1)"/>
  			<t:select match="concat('&lt;a name=&quot;', $title_no, '.', $subtitle_no, '&quot;>&lt;/a>')"/>
  			<h3><t:select match="./title/text()"/></h3>
  			<t:select match="./*[position() > 2]"/>
  			<br class="hack"/>
  		</t:for-each>
  		
  	</t:for-each>
  	
  	<br class="hack"/>
  	<br class="hack"/>
  
  	<div align="center">
  	<small>This page is copyright Fastnet Software Ltd, 2000.
  			Contact <a href="mailto:matt@sergeant.org">Matt Sergeant</a> for details.</small>
  	<br class="hack"/>
  	<small>This page is automatically generated using XML and XPath and a team of small rodents</small>
  	</div>
  </body>
  </html>
  
  
  
  
  1.1                  xml-axkit/examples/default.xps
  
  Index: default.xps
  ===================================================================
  <%
  $t->{'a'}{pre} = '<i>';
  $t->{'a'}{post} = '</i>';
  $t->{'a'}{showtag} = 1;
  
  my ($i, $j);
  $t->{'title'}{testcode} =
          sub {
                  my $node = shift;
                  if (findvalue('parent::section = true()', $node)) {
  						$i++; $j = 0;
                          $t->{'title'}{pre} = "<a name=\"$i\"></a><h2>";
  						$t->{'title'}{post} = '</h2>';
                  }
                  elsif (findvalue('parent::subsection = true()', $node)) {
  						$j++;
                          $t->{'title'}{pre} = "<a name=\"$i\.$j\"></a><h3>";
  						$t->{'title'}{post} = '</h3>';
                  }
  				else {
  					$t->{'title'}{pre} = '<title>';
  					$t->{'title'}{post} = '</title>';
  				}
                  return 1;
          };
  		
  $t->{'section'}{post} = '<p>';
  $t->{'subsection'}{pre} = '<blockquote>';
  $t->{'subsection'}{post} = '</blockquote><br>';
  
  %><html>
  <head>
  	<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  	<%= apply_templates('/page/head/title') %>
  </head>
  <body bgcolor="white">
  	<h1><%= findvalue('/page/head/title/text()') %></h1>
  
  <!-- table of contents -->
  <%
  my $x = 0;
  foreach my $section (findnodes('/page/body/section')) {
  	$x++;
  	print "<a href=\"#$x\">", findvalue('./title/text()', $section), "</a><br>\n";
  	my $y = 0;
  	foreach my $subsect (findnodes('./subsection', $section)) {
  		$y++;
  		print " - <a href=\"#$x.$y\">", findvalue('./title/text()', $subsect), "</a><br>\n";
  	}
  }
  %>
  
  <!-- Main document body -->
  <%= apply_templates('/page/body/section') %>
  
  <!-- Footer -->
  <br>
  <div align="center">
  <small>This page is copyright Fastnet Software Ltd, 2000.
  Contact <a href="mailto:matt@sergeant.org">Matt Sergeant</a>
  for details and availability.</small><br>
  <small>This page was generated by XPathScript, using the
  <a href="http://xml.sergeant.org/axkit/"
  >Apache XML Delivery Toolkit</a>.</small>
  </div>
  </body>
  </html>
  
  
  
  
  1.1                  xml-axkit/examples/docbook.dkb
  
  Index: docbook.dkb
  ===================================================================
  <?xml version="1.0"?>
  <?xml-stylesheet href="/docbook_screen.xps" 
  				type="application/x-xpathscript" 
  				media="screen"
  				title="default" ?>
  <?xml-stylesheet href="/docbook_screen_fancy.xps" 
  				type="application/x-xpathscript" 
  				media="screen"
  				title="fancy"
  				alternate="yes" ?>
  <?xml-stylesheet href="/docbook_print.xps" 
  				type="application/x-xpathscript" 
  				media="screen"
  				title="print"
  				alternate="yes" ?>
  <?xml-stylesheet href="/docbook_handheld.xps" 
  				type="application/x-xpathscript" 
  				media="handheld"
  				title="default"?>
  <article>
  <artheader>
  	<title>A complete DocBook XPathScript Example</title>
  	
  	<author>
  		<firstname>Matt</firstname>
  		<surname>Sergeant</surname>
  		<affiliation>
  			<address><email>matt@sergeant.org</email></address>
  		</affiliation>
  	</author>
  	
  	<copyright>
  		<year>2000</year>
  		<holder role="mailto:matt@sergeant.org">Fastnet Software Ltd</holder>
  	</copyright>
  
  <!--	
  	<pubdate role="rcs">$Date: 2002/01/13 20:45:11 $</pubdate>
  	
  	<releaseinfo>$Id: docbook.dkb,v 1.1 2002/01/13 20:45:11 matts Exp $</releaseinfo>
  -->
  
  	<abstract>
  		<para>A complete example of a docbook stylesheet using the
  			Apache XML Delivery Toolkit</para>
  	</abstract>
  </artheader>
  
  <sect1>
  <title>Introduction</title>
  <para>
  This aims to be a complete example of rendering DocBook documents
  using the <ulink url="http://xml.sergeant.org/axkit/">AxKit</ulink> and
  XPathScript. It offers several different styles to render the document
  in: Normal, Fancy and Printer. And also offers an alternative media to
  render the document for: handheld. How you select the different styles
  and media will be detailed later in this document.
  </para>
  <para>
  First a disclaimer: I'm a complete "newbie" to docbook. I also haven't
  tried rendering this document with the standard docbook tools, so I may
  have some elements wrong. If I do, you have my appologies. Hopefully you
  might be encouraged enough by this to fix the problems I've introduced
  and do it right, as it were.
  </para>
  </sect1>
  
  </article>
  
  
  
  1.1                  xml-axkit/examples/docbook_print.xps
  
  Index: docbook_print.xps
  ===================================================================
  <!--#include file="docbook_tags.xps"-->
  <html>
  <head>
  	<title><%= findvalue('/article/artheader/title/text()') %></title>
  </head>
  <body bgcolor="white">
  
  <%
  # display title/TOC page
  print apply_templates('/article/artheader/*');
  %>
  
  <hr>
  
  <%
  # display particular page
  foreach my $section (findnodes("/article/sect1")) {
  	# should only be 1
  	print apply_templates($section);
  }
  %>
  
  </body>
  </html>
  
  
  
  1.1                  xml-axkit/examples/docbook_screen.xps
  
  Index: docbook_screen.xps
  ===================================================================
  <%
  use Apache::Request;
  
  my $apr = Apache::Request->new($r);
  my $sect = $apr->param('section');
  
  $r->no_cache(1);
  
  my $this_page = $r->uri;
  
  my ($prev, $next);
  my $sects = findvalue('count(/article/sect1)');
  $sects = "$sects";
  if ($sects > 1) {
  	if ($sect >= $sects) {
  		$prev = $sects - 1;
  	}
  	elsif ($sect <= 1) {
  		$next = 2;
  	}
  	else {
  		$prev = $sect - 1;
  		$next = $sect + 1;
  	}
  }
  
  %>
  <!--#include file="docbook_tags.xps"-->
  <html>
  <head>
  	<title><%= findvalue('/article/artheader/title/text()') %></title>
  </head>
  <body bgcolor="white">
  
  <%
  if (!$sect) {
  	# display title/TOC page
  	print apply_templates('/article/artheader/*');
  	my $page = 0;
  	foreach my $sect (findnodes('/article/sect1')) {
  		$page++;
  		print "<a href=\"$this_page?section=$page\">";
  		print findvalue('title/text()', $sect);
  		print "</a><br>\n";
  	}
  	%><hr><%
  }
  else {
  
  %>
  <table width="100%">
  <tr>
  <td align="left">
  <%
  	if ($prev) {
  %>
  <a href="<%=$this_page%>?section=<%=$prev%>">Prev</a>
  <% } else {
  %>
  Prev
  <% } %>
  </td><td align="center"><a href="<%=$this_page%>">Top</a></td><td align="right">
  <%
  	if ($next) {
  %>
  <a href="<%=$this_page%>?section=<%=$next%>">Next</a>
  <% } else { %>
  Next
  <% } %>
  </td></tr></table>
  <hr>
  
  <%
  	# display particular page
  	foreach my $section (findnodes("/article/sect1[$sect]")) {
  		# should only be 1
  		print apply_templates($section);
  	}
  }
  %>
  <table width="100%"><tr><td align="right">
  <a href="<%=$this_page%>?style=print">Printer Friendly</a>
  </td></tr>
  <tr><td align="right">
  <a href="<%=$this_page%>?passthru=1">Raw XML</a>
  </td></tr></table>
  </body>
  </html>
  
  
  
  1.1                  xml-axkit/examples/docbook_tags.xps
  
  Index: docbook_tags.xps
  ===================================================================
  <%
  
  # support ulink URL's
  $t->{'ulink'}{testcode} = sub { 
  		my $node = shift;
  		my $t = shift;
  		$t->{pre} = "<a href=\"" .
  						findvalue('./@url', $node) . "\">";
  		$t->{post} = '</a>';
  		return 1;
  	};
  
  $t->{'title'}{testcode} = sub { 
  		my $node = shift;
  		my $t = shift;
  		if (findvalue('parent::blockquote', $node)) {
  			$t->{pre} = "<b>";
  			$t->{post} = "</b><br>\n";
  		}
  		elsif (findvalue('parent::artheader', $node)) {
  			$t->{pre} = "<h1>";
  			$t->{post} = "</h1>";
  		}
  		else {
  			my $parent = findvalue('name(..)', $node);
  			my ($level) = $parent =~ m/sect(\d+)$/;
  			$t->{pre} = "<h$level>";
  			$t->{post} = "</h$level>";
  		}
  		
  		return 1;
  	};
  
  $t->{'attribution'}{pre} = "<i>";
  $t->{'attribution'}{post} = "</i><br>\n";
  
  $t->{'para'}{pre} = '<p>';
  $t->{'para'}{post} = '</p>';
  
  $t->{'author'}{pre} = '<p>';
  
  $t->{'firstname'}{pre} = '';
  $t->{'surname'}{pre} = ' ';
  $t->{'surname'}{post} = "<p>\n";
  
  $t->{'email'}{testcode} = sub {
  		my $node = shift;
  		$t->{'email'}{pre} = "<a href=\"" . findvalue('./text()', $node) . "\">";
  		$t->{'email'}{post} = "</a>";
  		return 1;
  		};
  
  $t->{'copyright'}{pre} = "Copyright \xC2\xA9 ";
  $t->{'trademark'}{post} = "<super>TM</super>";
  $t->{'holder'}{testcode} = sub {
  		my $node = shift;
  		$t->{'holder'}{pre} = "<a href=\"" . findvalue('./@role', $node) . "\">";
  		$t->{'holder'}{post} = "</a>";
  		return 1;
  		};
  
  $t->{'abstract'}{pre} = "<p>";
  
  $t->{'sect1'}{post} = "<hr>";
  
  #$XML::XPath::Debug = 1;
  $t->{'sect1'}{testcode} = sub {
  		my ($node, $t) = @_;
  		if (my $id = findvalue('./@id', $node)) {
  			$t->{pre} = "<a name=\"$id\"></a>";
  		}
  		return 1;
  	};
  
  # lists
  
  $t->{'itemizedlist'}{pre} = '<ul>';
  $t->{'itemizedlist'}{post} = '</ul>';
  
  $t->{'orderedlist'}{pre} = '<ol>';
  $t->{'orderedlist'}{post} = '</ol>';
  
  $t->{'listitem'}{pre} = '<li>';
  $t->{'listitem'}{post} = '</li>';
  
  $t->{'procedure'}{pre} = '<ol>';
  $t->{'procedure'}{post} = '</ol>';
  
  $t->{'step'}{pre} = '<li>';
  $t->{'step'}{post} = '</li>';
  
  # examples, listings, etc.
  
  $t->{'programlisting'}{pre} = '<table width="100%"><tr><td bgcolor="#DDDDDD"><pre>';
  $t->{'programlisting'}{post} = '</pre></td></tr></table>';
  
  # need to support <prompt> and other tags within screen...
  $t->{'screen'} = $t->{'programlisting'};
  
  $t->{'userinput'}{pre} = '<b>';
  $t->{'userinput'}{post} = '</b>';
  
  $t->{'replaceable'}{pre} = '<i>';
  $t->{'replaceable'}{post} = '</i>';
  
  $t->{'emphasis'}{pre} = '<i>';
  $t->{'emphasis'}{post} = '</i>';
  
  $t->{'application'}{pre} = '<b>';
  $t->{'application'}{post} = '</b>';
  
  $t->{'manvolnum'}{pre} = '(';
  $t->{'manvolnum'}{post} = ')';
  
  $t->{'option'}{pre} = '<tt>';
  $t->{'option'}{post} = '</tt>';
  
  $t->{'filename'} = $t->{'option'};
  $t->{'literal'} = $t->{'option'};
  
  # references
  $t->{'anchor'}{testcode} = sub {
  		my ($node, $t) = @_;
  		$t->{pre} = '<a name="#';
  		$t->{pre} .= findvalue('@id', $node);
  		$t->{pre} .= '">';
  		$t->{post} = '</a>';
  		return 1;
  	};
  	
  $t->{'xref'}{testcode} = sub {
  		my ($node, $t) = @_;
  		$t->{pre} = '<a href="#';
  		my $id = findvalue('@linkend', $node);
  		$t->{pre} .= $id . '">';
  		$t->{pre} .= findvalue("id('$id')/title/text()");
  		$t->{post} = '</a>';
  		return -1;
  	};
  
  $t->{'link'}{testcode} = sub {
  		my ($node, $t) = @_;
  		$t->{pre} = '<a href="#';
  		$t->{pre} .= findvalue('@linkend', $node);
  		$t->{pre} .= '">';
  		$t->{post} = '</a>';
  		return 1;
  	};
  
  %>
  
  
  
  1.1                  xml-axkit/examples/index.xml
  
  Index: index.xml
  ===================================================================
  <?xml version="1.0"?>
  <?xml-stylesheet href="default.xps" 
  		type="application/x-xpathscript"?>
  <page>
  	<head>
  		<title>Welcome to AxKit!</title>
  	</head>
  <body bgcolor="white">
  
  <section>
  <title>Congratulations!</title>
  If this page displays as HTML, you have managed to successfully 
  install AxKit. If not, please check your error log and re-check
  your installation.
  </section>
  
  <section>
  <title>AxKit</title>
  AxKit can be found <a href="http://xml.sergeant.org/axkit/">here</a>.
  </section>
  
  </body>
  </page>
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/Cache.pm
  
  Index: Cache.pm
  ===================================================================
  # $Id: Cache.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
  
  package Apache::AxKit::Cache;
  use strict;
  
  use Apache;
  use Apache::Constants qw(OK DECLINED);
  use Apache::AxKit::Exception;
  use Digest::MD5 ();
  use Compress::Zlib;
  use Fcntl qw(:flock O_RDWR O_WRONLY O_CREAT O_RDONLY);
  
  # use vars qw/$COUNT/;
  
  sub new {
      my $class = shift;
      my ($r, $xmlfile, @extras) = @_;
      
      my $gzip = 0;
      if ($xmlfile =~ /.gzip/) {
          $gzip++;
  #        @extras = grep(!/gzip/, @extras);
      }
      
      my $key = Digest::MD5->new->add(
              join(':', 
                  $r->get_server_name,
                  $r->get_server_port,
                  $xmlfile,
                  @extras
              ))->hexdigest;
      
      AxKit::Debug(7, "Cache: key = $key");
  
      my $primary = substr($key,0,2,'');
      my $secondary = substr($key,0,2,'');
      
  #    warn "New for: $xmlfile:" . join(':', @extras). "\n";
      
      my $cachedir = $AxKit::Cfg->CacheDir();
      
      my $no_cache;
  
      if ($AxKit::Cfg->NoCache()) {
          $no_cache = 1;
      }
      
      if (!$no_cache) {
         if (!-e $cachedir) {
             if (!mkdir($cachedir, 0777)) {
                 AxKit::Debug(2, "Can't create cache directory '$cachedir': $!");
                 $no_cache = 1;
             }
         }
  
         if (!-e "$cachedir/$primary") {
             if (!mkdir("$cachedir/$primary", 0777)) {
                 AxKit::Debug(1, "Can't create cache directory '$cachedir/$primary': $!");
                 $no_cache = 1;
             }
         }
         
         if (!-e "$cachedir/$primary/$secondary") {
             if (!mkdir("$cachedir/$primary/$secondary", 0777)) {
                 AxKit::Debug(1, "Can't create cache directory '$cachedir/$primary/$secondary': $!");
                 $no_cache = 1;
             }
         }
     }
  
      my $self = bless { 
          apache => $r,
          key => $key, 
          no_cache => $no_cache, 
          dir => $cachedir,
          file => "$cachedir/$primary/$secondary/$key",
          gzip => $gzip,
  #        extras => \@extras,
          }, $class;
  
      if (my $alternate = $AxKit::Cfg->CacheModule()) {
          AxKit::reconsecrate($self, $alternate);
      }
      
  #     AxKit::Debug(7, "Cache->new Count: ".++$COUNT);
      
      return $self;
  }
  
  sub _get_stats {
      my $self = shift;
      return if $self->{mtime};
      my @stats = stat($self->{file});
      my $exists = -e _ && -r _;
      $self->{file_exists} = $exists;
      $self->{mtime} = $stats[9];
  }
  
  # sub DESTROY {
  #     AxKit::Debug(7, "Cache->DESTROY Count: ".--$COUNT);
  # }
  
  sub write {
      my $self = shift;
      return if $self->{no_cache};
      AxKit::Debug(7, "[Cache] writing cache file $self->{file}");
      my $fh = Apache->gensym();
      my $tmp_filename = $self->{file}."new$$";
      if (sysopen($fh, $tmp_filename, O_WRONLY|O_CREAT)) {
          # flock($fh, LOCK_EX);
          # seek($fh, 0, 0);
          # truncate($fh, 0);
          print $fh $_[0];
          close $fh;
          rename($tmp_filename, $self->{file}) 
                  || throw Apache::AxKit::Exception::IO( -text => "Couldn't rename cachefile: $!");
      }
      else {
          throw Apache::AxKit::Exception::IO( -text => "Couldn't open cachefile for writing: $!");
      }
      
      if ($self->{gzip} && $AxKit::Cfg->GzipOutput) {
          AxKit::Debug(3, 'Creating gzip output cache');
          my $fh = Apache->gensym();
          if (sysopen($fh, $self->{file}.'new.gz', O_RDWR|O_CREAT)) {
              flock($fh, LOCK_EX);
              seek($fh, 0, 0);
              truncate($fh, 0);
              print $fh ''.Compress::Zlib::memGzip($_[0]);
              close $fh;
              rename($self->{file}.'new.gz', $self->{file}.'.gz')
                      || throw Apache::AxKit::Exception::IO( -text => "Couldn't rename gzipped cachefile: $!");
          }
          else {
              throw Apache::AxKit::Exception::IO( -text => "Couldn't open gzipped cachefile for writing: $!");
          }
      }
  }
  
  sub read {
      my $self = shift;
      return if $self->{no_cache};
      my $fh = Apache->gensym();
      if (sysopen($fh, $self->{file}, O_RDONLY)) {
          flock($fh, LOCK_SH);
          local $/;
          return <$fh>;
          # close($fh);
          # close unlocks automatically
      }
      return '';
  }
  
  sub get_fh {
      my $self = shift;
      return if $self->{no_cache};
      my $fh = Apache->gensym();
      if (sysopen($fh, $self->{file}, O_RDONLY)) {
          flock($fh, LOCK_SH);
          return $fh;
      }
      else {
          throw Apache::AxKit::Exception::IO( -text => "Cannot open cache file for reading: $!");
      }
  }
  
  sub set_type {
      my $self = shift;
      return if $self->{no_cache};
      
      my $fh = Apache->gensym();
      if (sysopen($fh, $self->{file}.'newtype', O_RDWR|O_CREAT)) {
          flock($fh, LOCK_EX);
          seek($fh, 0, 0);
          truncate($fh, 0);
          print $fh $_[0];
          close $fh;
          rename($self->{file}.'newtype', $self->{file}.'.type') 
                  || throw Apache::AxKit::Exception::IO( -text => "Couldn't rename type cachefile: $!");
      }
      else {
          throw Apache::AxKit::Exception::IO( -text => "Couldn't open type cachefile for writing: $!");
      }
  }
  
  sub get_type {
      my $self = shift;
      return if $self->{no_cache};
      my $fh = Apache->gensym();
      if (sysopen($fh, $self->{file}.'.type', O_RDONLY)) {
          flock($fh, LOCK_SH);
          local $/;
          return <$fh>;
          # close($fh);
          # close unlocks automatically
      }
      return '';
  }
  
  sub deliver {
      my $self = shift;
      return if $self->{no_cache};
      my $r = $self->{apache};
  
      {
          # get content-type
          AxKit::Debug(4, "Cache: Getting content-type");
          if (my $type = $self->get_type) {
              AxKit::Debug(4, "Cache: setting content-type: $type");
              $r->content_type($type);
          }
      }
      
      if ($r->content_type eq 'changeme' && !$r->notes('axkit_passthru_type')) {
          $AxKit::Cfg->AllowOutputCharset(1);
          $r->content_type('text/html; charset=' . ($AxKit::Cfg->OutputCharset || "UTF-8"));
      }
      elsif ($r->notes('axkit_passthru_type')) {
          $r->content_type($AxKit::OrigType);
      }
  
      
      my ($transformer, $doit) = AxKit::get_output_transformer();
      
      if ($doit) {
          AxKit::Debug(4, "Cache: Transforming content and printing to browser");
          $r->send_http_header() unless lc($r->dir_config('Filter')) eq 'on';
          $r->print( $transformer->( $self->read() ) );
          return OK;
      }
      else {
          AxKit::Debug(4, "Cache: Sending untransformed content to browser");
  
          # Make sure we unset PATH_INFO or wierd things can happen!
          $ENV{PATH_INFO} = '';
          $r->path_info('');
          
          if ($self->{gzip} && $AxKit::Cfg->DoGzip) {
              AxKit::Debug(4, 'Cache: Delivering gzipped output');
              $r->filename($self->{file}.'.gz');
          }
          else {
              $r->filename($self->{file});
          }
          
          return DECLINED;
      }
      
  }
  
  sub reset {
      my $self = shift;
      unlink $self->{file};
  }
  
  sub mtime {
      my $self = shift;
      $self->_get_stats;
      return $self->{mtime} if exists $self->{mtime};
      return ($self->{mtime} = (stat($self->{file}))[9]);
  }
  
  sub has_changed {
      my $self = shift;
      my $time = shift;
      return $self->mtime > $time;
  }
  
  sub exists {
      my $self = shift;
      return if $self->{no_cache};
      $self->_get_stats;
      return $self->{file_exists} if exists $self->{file_exists};
      return ($self->{file_exists} = -e $self->{file});
  }
  
  sub key {
      my $self = shift;
      return $self->{key};
  }
  
  sub no_cache {
      my $self = shift;
  
      return $self->{no_cache} unless @_;
  
      if ($_[0]) {
          AxKit::Debug(8, "Turning off cache!");
          $self->{no_cache} = 1;
          $self->reset();
      }
      
      return $self->{no_cache};
  }
  
  1;
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/CharsetConv.pm
  
  Index: CharsetConv.pm
  ===================================================================
  # $Id: CharsetConv.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
  
  package Apache::AxKit::CharsetConv;
  # Copyright (c) 2000 Michael Piotrowski
  # Originally copied from Text::Iconv
  
  use strict;
  use vars qw($VERSION @ISA @EXPORT_OK);
  
  require Exporter;
  require DynaLoader;
  
  @ISA = qw(Exporter DynaLoader);
  
  @EXPORT_OK = qw( convert );
  
  $VERSION = '1.0';
  
  bootstrap Apache::AxKit::CharsetConv $VERSION;
  
  1;
  __END__
  
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/CharsetConv.xs
  
  Index: CharsetConv.xs
  ===================================================================
  /* $Id: CharsetConv.xs,v 1.1 2002/01/13 20:45:11 matts Exp $ */
  /* XSUB for Perl module Apache::AxKit::CharsetConv  */
  /* Originally from Text::Iconv distribution, */
  /* all credits to Michael Piotrowski - this is a verbatim copy */
  /* included in AxKit to reduce the number of required extra modules */
  
  #ifdef __cplusplus
  extern "C" {
  #endif
  #include "EXTERN.h"
  #include "perl.h"
  #include "XSUB.h"
  #include <iconv.h>
  #ifdef __cplusplus
  }
  #endif
  
  
  /*****************************************************************************/
  
  static int raise_error = 0;
  
  SV *do_conv(iconv_t iconv_handle, SV *string)
  {
     char    *ibuf;         /* char* to the content of SV *string */
     char    *obuf;         /* temporary output buffer */
     size_t  inbytesleft;   /* no. of bytes left to convert; initially
  			     this is the length of the input string,
  			     and 0 when the conversion has finished */
     size_t  outbytesleft;  /* no. of bytes in the output buffer */
     size_t  l_obuf;        /* length of the output buffer */
     char *icursor;         /* current position in the input buffer */
     /* The Single UNIX Specification (version 1 and version 2), as well
        as the HP-UX documentation from which the XPG iconv specs are
        derived, are unclear about the type of the second argument to
        iconv() (here called icursor): The manpages say const char **,
        while the header files say char **. */
     char    *ocursor;      /* current position in the output buffer */
     size_t  ret;           /* iconv() return value */
     SV      *perl_str;     /* Perl return string */
     
     perl_str = newSVpv("", 0);
  
     /* Get length of input string. That's why we take an SV* instead of
        a char*: This way we can convert UCS-2 strings because we know
        their length. */
  
     inbytesleft = SvCUR(string);
     ibuf        = SvPV(string, inbytesleft);
     
     /* Calculate approximate amount of memory needed for the temporary
        output buffer and reserve the memory. The idea is to choose it
        large enough from the beginning to reduce the number of copy
        operations when converting from a single byte to a multibyte
        encoding. */
     
     if(inbytesleft <= MB_LEN_MAX)
     {
        outbytesleft = MB_LEN_MAX + 1;
     }
     else
     {
        outbytesleft = 2 * inbytesleft;
     }
  
     l_obuf = outbytesleft;
     obuf   = (char *) New(0, obuf, outbytesleft, char); /* Perl malloc */
  
     /**************************************************************************/
  
     icursor = ibuf;
     ocursor = obuf;
  
     /**************************************************************************/
     
     while(inbytesleft != 0)
     {
        ret = iconv(iconv_handle, (const char**)&icursor, &inbytesleft,
  		                &ocursor, &outbytesleft);
        
        if(ret == (size_t) -1)
        {
  	 switch(errno)
  	 {
  	    case EILSEQ:
  	       /* Stop conversion if input character encountered which
  		  does not belong to the input char set */
  	       if (raise_error)
  		  croak("Character not from source char set: %s",
  			strerror(errno));
  	       Safefree(obuf);   
  	       return(&PL_sv_undef);
  	    case EINVAL:
  	       /* Stop conversion if we encounter an incomplete
                    character or shift sequence */
  	       if (raise_error)
  		  croak("Incomplete character or shift sequence: %s",
  			strerror(errno));
  	       Safefree(obuf);   
  	       return(&PL_sv_undef);
  	    case E2BIG:
  	       /* If the output buffer is not large enough, copy the
                    converted bytes to the return string, reset the
                    output buffer and continue */
  	       sv_catpvn(perl_str, obuf, l_obuf - outbytesleft);
  	       ocursor = obuf;
  	       outbytesleft = l_obuf;
  	       break;
  	    default:
  	       if (raise_error)
  		  croak("iconv error: %s", strerror(errno));
  	       Safefree(obuf);   
  	       return(&PL_sv_undef);
  	 }
        }
     }
  
     /* Copy the converted bytes to the return string, and free the
        output buffer */
     
     sv_catpvn(perl_str, obuf, l_obuf - outbytesleft);
     Safefree(obuf); /* Perl malloc */
  
     return perl_str;
  }
  
  typedef iconv_t Apache__AxKit__CharsetConv;
  
  /*****************************************************************************/
  /* Perl interface                                                            */
  
  MODULE = Apache::AxKit::CharsetConv	PACKAGE = Apache::AxKit::CharsetConv      PREFIX = iconv_t_
  
  PROTOTYPES: DISABLE
  
  int
  raise_error(...)
     CODE:
        if (items > 0 && SvIOK(ST(0))) /* if called as function */
           raise_error = SvIV(ST(0));
        if (items > 1 && SvIOK(ST(1))) /* if called as class method */
           raise_error = SvIV(ST(1));
        RETVAL = raise_error;
     OUTPUT:
        RETVAL
  
  Apache::AxKit::CharsetConv
  new(self, fromcode, tocode)
     char *fromcode
     char *tocode
     CODE:
     if((RETVAL = iconv_open(tocode, fromcode)) == (iconv_t)-1)
     {
        switch(errno)
        {
  	 case ENOMEM:
  	    croak("Insufficient memory to initialize conversion: %s -> %s", 
                    fromcode, tocode);
  	 case EINVAL:
  	    croak("Unsupported conversion: %s -> %s", fromcode, tocode);
  	 default:
  	    croak("Couldn't initialize conversion: %s -> %s", fromcode, tocode);
        }
     }
     OUTPUT:
        RETVAL
  
  SV*
  convert(self, string)
     Apache::AxKit::CharsetConv self
     SV *string
     CODE:
        RETVAL = do_conv(self, string);
     OUTPUT:
        RETVAL
  
  void
  DESTROY(self)
     Apache::AxKit::CharsetConv self
     CODE:
        /* printf("Now in Apache::AxKit::CharsetConv::DESTROY\n"); */
        (void) iconv_close(self);
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/ConfigReader.pm
  
  Index: ConfigReader.pm
  ===================================================================
  # $Id: ConfigReader.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
  
  package Apache::AxKit::ConfigReader;
  
  use strict;
  
  # use vars qw/$COUNT/;
  
  sub new {
      my $class = shift;
      my $r = shift;
      
      my $cfg = AxKit::get_config($r) || {};
      
  #    use Apache::Peek 'Dump';
  #    Dump($cfg);
  #     use Data::Dumper;
  #     $Data::Dumper::Indent = 1;
  #     warn("Cfg: ", Data::Dumper->Dump([$cfg], ['cfg']));
  
      my $self = bless { apache => $r, cfg => $cfg, output_charset_ok => 0 }, $class;
      
      if (my $alternate = $cfg->{ConfigReader} || $r->dir_config('AxConfigReader')) {
          AxKit::reconsecrate($self, $alternate);
      }
      
      return $self;
  }
  
  # sub DESTROY {
  #     AxKit::Debug(7, "ConfigReader->DESTROY count: ".--$COUNT);
  # }
  
  # returns a hash reference consisting of key = type, value = module
  sub StyleMap {
      my $self = shift;
      if ($self->{cfg}->{StyleMap}) {
          return $self->{cfg}->{StyleMap};
      }
      # no StyleMap, try dir_config
      my %hash = split /\s*(?:=>|,)\s*/, $self->{apache}->dir_config('AxStyleMap');
      return \%hash;
  }
  
  # returns the location of the cache dir
  sub CacheDir {
      my $self = shift;
      if (my $cachedir = 
              $self->{cfg}->{CacheDir} 
              ||
              $self->{apache}->dir_config('AxCacheDir')) {
          return $cachedir;
      }
      
      use File::Basename;
      my $dir = dirname($self->{apache}->filename());
      return $dir . "/.xmlstyle_cache";
  }
  
  sub ProviderClass {
      my $self = shift;
      if (my $alternate = $self->{cfg}{Provider} || 
              $self->{apache}->dir_config('AxProvider')) {
          return $alternate;
      }
      
      return 'Apache::AxKit::Provider::File';
  }
  
  sub DependencyChecks {
      my $self = shift;
      return $self->{cfg}{DependencyChecks};
  }
  
  sub StyleProviderClass {
      my $self = shift;
      if (my $alternate = $self->{cfg}{StyleProvider} || 
              $self->{apache}->dir_config('AxStyleProvider')) {
          return $alternate;
      }
      
      return 'Apache::AxKit::Provider::File';
  }
  
  sub NoCache {
      my $self = shift;
      return $self->{cfg}{NoCache} || 
              $self->{apache}->dir_config('AxNoCache');
  }
  
  sub PreferredStyle {
      my $self = shift;
      
      return
          $self->{apache}->notes('preferred_style')
                  ||
          $self->{cfg}->{Style}
                  ||
          $self->{apache}->dir_config('AxPreferredStyle')
                  ||
          '';
  }
  
  sub PreferredMedia {
      my $self = shift;
      
      return
          $self->{apache}->notes('preferred_media')
                  ||
          $self->{cfg}->{Media}
                  ||
          $self->{apache}->dir_config('AxPreferredMedia')
                  ||
          'screen';
  }
  
  sub CacheModule {
      my $self = shift;
      return $self->{cfg}{CacheModule}
          || $self->{apache}->dir_config('AxCacheModule');
  }
  
  sub DebugLevel {
      my $self = shift;
      return $self->{cfg}{DebugLevel} || 
              $self->{apache}->dir_config('AxDebugLevel') || 
              0;
  }
  
  sub DebugTime {
      my $self = shift;
      return $self->{apache}->dir_config('AxDebugTime') || 0;
  }
  
  sub StackTrace {
      my $self = shift;
      return $self->{cfg}{StackTrace} ||
              $self->{apache}->dir_config('AxStackTrace') ||
              0;
  }
  
  sub LogDeclines {
      my $self = shift;
      return $self->{cfg}{LogDeclines} ||
              $self->{apache}->dir_config('AxLogDeclines') ||
              0;
  }
  
  sub AllowOutputCharset {
      my $self = shift;
      
      my $oldval = $self->{output_charset_ok};
      if (@_) {
          $self->{output_charset_ok} = shift;
      }
      return $oldval;
  }
      
  sub OutputCharset {
      my $self = shift;
  
      return unless $self->{output_charset_ok};
      
  #    warn "OutputCharset\n";
      unless ($self->{cfg}{TranslateOutput} ||
              $self->{apache}->dir_config('AxTranslateOutput')) {
          return;
      }
      
  #    warn "Checking OutputCharset\n";
      
      if (my $charset = $self->{cfg}{OutputCharset}
          || $self->{apache}->dir_config('AxOutputCharset')) {
          return $charset;
      }
      
  #    warn "Checking Accept-Charset\n";
      # check HTTP_ACCEPT_CHARSET
      if (my $ok_charsets = $ENV{HTTP_ACCEPT_CHARSET}) {
          my @charsets = split(/,\s*/, $ok_charsets);
          my $retcharset;
          my $retscore = 0;
          foreach my $charset (@charsets) {
              my $score;
  
              ($charset, $score) = split(/;\s*q=/, $charset, 2);
              $score = 1 unless (defined($score) && ($score =~ /^(\+|\-)?\d+(\.\d+)?$/));
             
              if ($score > $retscore || $charset =~ /^utf\-?8$/i) { # we like utf8
                  $retcharset = $charset;
                  $retscore = $score;
              }
          }
          
          $retcharset =~ s/iso/ISO/;
          $retcharset =~ s/(us\-)?ascii/US-ASCII/;
          
          return undef if $retcharset =~ /^utf\-?8$/;
  	return undef if $retcharset eq '*';
  # warn "Charset: '$retcharset'\n";
          return $retcharset;
      }
      
  }
  
  sub ErrorStyles {
      my $self = shift;
      
      my $style = $self->{cfg}{ErrorStylesheet};
      my ($type, $href);
      if (!$style) {
          ($type, $href) = split(/\s*=>\s*/,
                  ($self->{apache}->dir_config('AxErrorStylesheet') || ''),
                  2);
          return [] unless $href;
      }
      else {
          ($type, $href) = @$style;
      }
      
      my $style_map = $self->StyleMap;
      
      my $module = $style_map->{ $type };
      
      if (!$module) {
          throw Apache::AxKit::Exception::Error(
                  -text => "ErrorStylesheet: No module mapping found for type '$type'"
                  );
      }
      
      return [{href => $href, type => $type, module => $module}];
  }
  
  sub GzipOutput {
      my $self = shift;
      return $self->{cfg}{GzipOutput}
          || $self->{apache}->dir_config('AxGzipOutput');
  }
  
  sub DoGzip {
      # should I actually send GZip for this request?
      my $self = shift;
      return unless $self->GzipOutput;
      
      AxKit::Debug(5, 'Should we zip the output?');
      my $r = $self->{apache};
      my($can_gzip);
      
      AxKit::Debug(5, 'Getting Vary header');
      my @vary;
      @vary = $r->header_out('Vary') if $r->header_out('Vary');
      push @vary, "Accept-Encoding", "User-Agent";
      AxKit::Debug(5, 'Setting Vary header');
      $r->header_out('Vary',
                      join ", ",
                      @vary
                  );
      my($accept_encoding) = $r->header_in("Accept-Encoding") || '';
      $can_gzip = 1 if index($accept_encoding,"gzip")>=0;
      unless ($can_gzip) {
          my $user_agent = $r->header_in("User-Agent");
          if ($user_agent =~ m{
                               ^Mozilla/
                               \d+
                               \.
                               \d+
                               [\s\[\]\w\-]+
                               (
                                \(X11 |
                                Macint.+PPC,\sNav
                               )
                              }x
             ) {
              $can_gzip = 1;
          }
      }
  
      AxKit::Debug(5, $can_gzip ? 'Setting gzip' : 'Not setting gzip');
      $r->header_out('Content-Encoding','gzip') if $can_gzip;
  
      return $can_gzip;
  }
  
  sub GetMatchingProcessors {
      my $self = shift;
      my ($media, $style, $doctype, $dtd, $root, $styles, $provider) = @_;
      return @$styles if @$styles;
      
      $style ||= '#default';
  
      my $list = $self->{cfg}{Processors}{$media}{$style};
  
      my $processors = $self->{apache}->dir_config('AxProcessors');
      if( $processors ) {
        foreach my $processor (split(/\s*,\s*/, $processors) ) {
  	my ($pmedia, $pstyle, @processor) = split(/\s+/, $processor);
  	next unless ($pmedia eq $media and $pstyle eq $style);
  	push (@$list, [ 'NORMAL', @processor ] );
        }
      }
      
      my @results;
      
      for my $directive (@$list) {
          my $type = $directive->[0];
          my $style_hash = {
                      type => $directive->[1], 
                      href => $directive->[2],
                      title => $style,
                  };
          if ($type eq 'NORMAL') {
              push @results, $style_hash;
          }
          elsif ($type eq 'DocType') {
              if ($doctype eq $directive->[3]) {
                  push @results, $style_hash;
              }
          }
          elsif ($type eq 'DTD') {
              if ($dtd eq $directive->[3]) {
                  push @results, $style_hash;
              }
          }
          elsif ($type eq 'Root') {
              if ($root eq $directive->[3]) {
                  push @results, $style_hash;
              }
          }
          elsif ($type eq 'URI') {
              my $uri = $provider->apache_request->uri;
              if ($uri =~ /$directive->[3]/) {
                  push @results, $style_hash;
              }
          }
          else {
              warn "Unrecognised directive type: $type";
          }
      }
      
      # list any dynamically chosen stylesheets here
      $list = $self->{cfg}{DynamicProcessors};
      foreach my $package (@$list) {
          AxKit::load_module($package);
          no strict 'refs';
          my($handler) = $package.'::handler';
          push @results, $handler->($provider, $media, $style, 
                                    $doctype, $dtd, $root);
      }   
      
      return @results;
  }
  
  sub XSPTaglibs {
      my $self = shift;
      
      my @others;
      
      @others = eval { keys %{ $self->{cfg}{XSPTaglibs} } };
      warn $@ if $@;
      
      if (@others) {
          return @others;
      }
      
      local $^W;
      @others = split(/\s+/, $self->{apache}->dir_config('AxAddXSPTaglibs'));
      
      return @others;
  }
  
  sub OutputTransformers {
      my $self = shift;
  
      my @filters;
  
      if( my $o_t = $self->{cfg}{OutputTransformers} ) {
          @filters = @$o_t;
      }
      else {
          @filters = split(/\s+/, 
                  $self->{apache}->dir_config('AxOutputTransformers'));    
      }
  
      return @filters;
  }
  
  sub Plugins {
      my $self = shift;
      my @plugs;
  
      if (my $plugins = $self->{cfg}{Plugins}) {
          @plugs = @$plugins;
      }
      else {
          @plugs = split(/\s+/,
                  $self->{apache}->dir_config('AxPlugins'));
      }
      return @plugs;
  }
  
  1;
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/Exception.pm
  
  Index: Exception.pm
  ===================================================================
  # $Id: Exception.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
  
  package Apache::AxKit::Exception;
  use Error;
  @ISA = ('Error');
  
  sub new {
      my $class = shift;
      my $self = $class->SUPER::new(@_);
      
      if ($AxKit::Cfg->StackTrace) {
          my $i = $Error::Depth + 1;
          my ($pkg, $file, $line) = caller($i++);
          my @stacktrace;
          while ($pkg) {
              push @stacktrace, { '-package' => $pkg, '-file' => $file, '-line' => $line};
              ($pkg, $file, $line) = caller($i++);
          }
          $self->{'stacktrace'} = \@stacktrace;
      }
      
      return $self;
  }
  
  sub stacktrace_list {
      my $E = shift;
      return $E->{'stacktrace'} || [];
  }
  
  use overload 'bool' => 'bool';
  
  sub bool {
      my $E = shift;
      1;
  }
  
  sub value {
      my $E = shift;
      exists $E->{'-value'} ? $E->{'-value'} : 1;
  }
  
  package Apache::AxKit::Exception::Declined;
  @ISA = ('Apache::AxKit::Exception');
  
  package Apache::AxKit::Exception::Error;
  @ISA = ('Apache::AxKit::Exception');
  
  package Apache::AxKit::Exception::OK;
  @ISA = ('Apache::AxKit::Exception');
  
  package Apache::AxKit::Exception::Retval;
  @ISA = ('Apache::AxKit::Exception');
  
  package Apache::AxKit::Exception::IO;
  @ISA = ('Apache::AxKit::Exception');
  
  # a hack to fix broken Error.pm 0.13 (fixed in 0.14)
  package Error;
  use overload bool => sub { 1 };
  
  1;
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/Language.pm
  
  Index: Language.pm
  ===================================================================
  # $Id: Language.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
  
  package Apache::AxKit::Language;
  
  use strict;
  use Apache::Constants;
  
  sub handler {
  	my $class = shift;
  	my ($r, $xml, $style) = @_;
  	
  	die "Need to subclass handler() method";
  }
  
  sub get_mtime {
  	my $class = shift;
  	my $provider = shift;
  	return $provider->mtime();
  }
  
  sub stylesheet_exists { 1; }
  
  1;
  __END__
  
  =head1 NAME
  
  Apache::AxKit::Language - base class for all processors
  
  =head1 DESCRIPTION
  
  This base class principally provides the get_mtime function for
  determining the modification time of the stylesheet. Other modules
  are free to override this function - possibly to provide facilities
  for determining the minimum mtime of an XML-based stylesheet that includes
  external entities.
  
  =cut
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/Makefile.PL
  
  Index: Makefile.PL
  ===================================================================
  # $Id: Makefile.PL,v 1.1 2002/01/13 20:45:11 matts Exp $
  
  package Apache::AxKit::CharsetConv;
  
  use ExtUtils::MakeMaker;
  
  ######################################################
  # Standard bits required for have_library and friends
  my %config;
  
  $|=1; # flush output
  
  while($_ = shift @ARGV) {
      my ($k, $v) = split /=/, $_, 2;
      $config{$k} = $v;
  }
  
  $DEBUG = $AxKit::DEBUG || delete $config{DEBUG};
  ######################################################
  
  if (have_library("iconv", "iconv") || have_library("iconv", "libiconv")) {
      # probably FreeBSD or Win32
      print "Found libiconv. Might be *BSD or Win32...\n\n";
      $config{LIBS} .= " -liconv";
  }
  elsif (have_library("c", "iconv")) {
      # other unix
  }
  else {
      print <<"REASON";
  AxKit needs the iconv system library to work properly. This comes by
  default with most Unix systems, however it may be that you do not
  have the development libraries installed, or possibly that you just
  don't have iconv available on your system. For *BSD systems, iconv
  is available in your distribution's ports collection. For Win32
  systems, a google search will often suffice to find a Win32 library
  version.
  REASON
      exit(0);
  }
  
  ######################################################
  # test iconv() param types for const
  # print "checking if iconv() param 2 is const... ";
  # my $cfile = gensym();
  # open($cfile, ">conftest.c") || die "Cannot write to file conftest.c";
  # print $cfile <<"SRC";
  # #include "iconv.h"
  # int main() { return 0; }
  # int t() {
  #     char *foo = "foo";
  #     size_t ret;
  #     iconv_t iconv_handle;
  #     size_t inbytesleft;
  #     size_t outbytesleft;
  #     char *ocursor;
  #     ret = iconv(iconv_handle, &foo, &inbytesleft,
  #                             &ocursor, &outbytesleft);
  #     return 0;
  # }
  # SRC
  # close $cfile;
  # my $output = $is_Win32 ? 
  #     eval {
  #         backtick(sprintf($CC, $config{INC})) .
  #         backtick(sprintf($LINK, $config{LIBS}, " iconv.lib"));
  #     }
  #     :
  #     eval {
  #         backtick(sprintf($LINK, $config{INC}, $config{LIBS}, '2>&1'));
  #     };
  # 
  # if ($@) {
  #     warn $@ if $DEBUG;
  # }
  # rm_f("conftest*");
  # if ($output =~ /warning: passing arg 2/) {
  #     print "yes\n";
  #     $config{DEFINE} .= uc(" -Diconv_second_param_is_const");
  # }
  # else {
  #     print "no\n";
  # }
  
  ######################################################
  
  if ($DEBUG) {
      print "calling WriteMakefile with config:\n";
      foreach my $k (keys %config) {
          print "$k = $config{$k}\n";
      }
  }
  
  foreach my $k (keys %config) {
      push @ARGV, "$k=$config{$k}";
  }
  
  #%config = () if $] > 5.00560; 
  rm_f($DEVNULL) if $is_Win32;
  
  WriteMakefile(
      'NAME' => 'Apache::AxKit::CharsetConv',
      'VERSION_FROM' => 'CharsetConv.pm',
      %config,
  );
  
  sub MY::constants {
      package MY;
      my $class = shift;
      my $inherited = $class->SUPER::constants(@_);
  #    warn "CONSTANTS: $inherited\n";
      $inherited =~ s|\s\Q../blib\E| ../../../blib|g;
      return $inherited;
  }
  
  #################################################################
  # Functions
  #################################################################
  
  use Config;
  use Cwd;
  use Symbol;
  use File::Spec;
  
  use vars qw/$DEVNULL $is_Win32/;
  
  BEGIN {
      $is_Win32 = ($^O =~ /Win32/);
      if ($is_Win32) {
          $DEVNULL = 'DEVNULL';
      }
      else {
          $DEVNULL = eval { File::Spec->devnull };
          if ($@) { $DEVNULL = '/dev/null' }
      }
  }
  
  sub rm_f {
      my @files = @_;
      my @realfiles;
      foreach (@files) {
          push @realfiles, glob($_);
      }
      if (@realfiles) {
          chmod(0777, @realfiles);
          unlink(@realfiles);
      }
  }
  
  sub rm_fr {
      my @files = @_;
      my @realfiles;
      foreach (@files) {
          push @realfiles, glob($_);
      }
      foreach my $file (@realfiles) {
          if (-d $file) {
              # warn("$file is a directory\n");
              rm_fr("$file/*");
              rm_fr("$file/.exists");
              rmdir($file) || die "Couldn't remove $file: $!";
          }
          else {
              # warn("removing $file\n");
              chmod(0777, $file);
              unlink($file);
          }
      }
  }
  
  sub xsystem {
      my $command = shift;
      if ($DEBUG) {
          print $command, "\n";
          if (system($command) != 0) {
              die "system call to '$command' failed";
          }
          return 1;
      }
      open(OLDOUT, ">&STDOUT");
      open(OLDERR, ">&STDERR");
      open(STDOUT, ">$DEVNULL");
      open(STDERR, ">$DEVNULL");
      my $retval = system($command);
      open(STDOUT, ">&OLDOUT");
      open(STDERR, ">&OLDERR");
      if ($retval != 0) {
          die "system call to '$command' failed";
      }
      return 1;
  }
  
  sub backtick {
      my $command = shift;
      if ($DEBUG) {
          print $command, "\n";
          my $results = `$command`;
          chomp $results;
          if ($? != 0) {
              die "backticks call to '$command' failed";
          }
          return $results;
      }
      open(OLDOUT, ">&STDOUT");
      open(OLDERR, ">&STDERR");
      open(STDOUT, ">$DEVNULL");
      open(STDERR, ">$DEVNULL");
      my $results = `$command`;
      my $retval = $?;
      open(STDOUT, ">&OLDOUT");
      open(STDERR, ">&OLDERR");
      if ($retval != 0) {
          die "backticks call to '$command' failed";
      }
      chomp $results;
      return $results;
  }
  
  sub try_link0 {
      my ($src, $opt) = @_;
      my $cfile = gensym();
      # local $config{LIBS};
      # $config{LIBS} .= $opt;
      unless (mkdir(".testlink", 0777)) {
          rm_fr(".testlink");
          mkdir(".testlink", 0777) || die "Cannot create .testlink dir: $!";
      }
      chdir(".testlink");
      open($cfile, ">Conftest.xs") || die "Cannot write to file Conftest.xs: $!";
  print $cfile <<EOT;
  #ifdef __cplusplus
  extern "C" {
  #endif
  #include <EXTERN.h>
  #include <perl.h>
  #include <XSUB.h>
  #ifdef __cplusplus
  }
  #endif
  
  EOT
      print $cfile $src;
      print $cfile <<EOT;
  
  MODULE = Conftest          PACKAGE = Conftest
  
  PROTOTYPES: DISABLE
  
  EOT
      close($cfile);
      open($cfile, ">Conftest.pm") || die "Cannot write to file Conftest.pm: $!";
      print $cfile <<'EOT';
  package Conftest;
  $VERSION = 1.0;
  require DynaLoader;
  @ISA = ('DynaLoader');
  bootstrap Conftest $VERSION;
  1;
  EOT
      close($cfile);
      open($cfile, ">Makefile.PL") || die "Cannot write to file Makefile.PL: $!";
      print $cfile <<'EOT';
  use ExtUtils::MakeMaker;
  my %config;
  while($_ = shift @ARGV) {
      my ($k, $v) = split /=/, $_, 2;
      warn("$k = $v\n");
      $config{$k} = $v;
  }
  WriteMakefile(NAME => "Conftest", VERSION_FROM => "Conftest.pm", %config);
  EOT
      close($cfile);
      open($cfile, ">test.pl") || die "Cannot write to file test.pl: $!";
      print $cfile <<EOT;
  use Test; BEGIN { plan tests => 1; } END { ok(\$loaded) }
  use Conftest; \$loaded++;
  EOT
      close($cfile);
      xsystem("$^X Makefile.PL " . join(' ', map { "'$_=$config{$_}'" } keys %config));
      xsystem("$Config{make} test 'OTHERLDFLAGS=$opt'");
  }
  
  sub try_link {
      my $start_dir = cwd();
      my $result = eval {
          try_link0(@_);
      };
      warn $@ if $DEBUG && $@;
      chdir($start_dir);
      rm_fr(".testlink");
      return $result;
  }
  
  sub have_library {
      my ($lib, $func) = (@_, "blank");
      printf("checking for %s() in -l%s... ", $func, $lib) if $func ne "blank";
      printf("looking for -l%s... ", $lib) if $func eq "blank";
  
      my $result;
      if ($func) {
          my $libs = $is_Win32 ? " $lib.lib  " : "-l$lib";
          if ($is_Win32) {
              $result = try_link(<<"SRC", $libs);
  #include <windows.h>
  #include <winsock.h>
  blank() { return 0; }
  int t() { ${func}(); return 0; }
  SRC
              unless ($result) {
                  $result = try_link(<<"SRC", $libs);
  #include <windows.h>
  #include <winsock.h>
  blank() { return 0; }
  int t() { void ((*p)()); p = (void ((*)()))${func}; return 0; }
  SRC
              }
          }
          else {
  
              $result = try_link(<<"SRC", $libs);
  blank() { return 0; }
  int t() { ${func}(); return 0; }
  SRC
          }
      }
  
      unless ($result) {
          print "no\n";
          return 0;
      }
  
      if ($func ne "main") {
          $config{DEFINE} .= uc(" -Dhave_$func");
      }
  
      print "yes\n";
      return 1;
  }
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/Provider.pm
  
  Index: Provider.pm
  ===================================================================
  # $Id: Provider.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
  
  package Apache::AxKit::Provider;
  use strict;
  
  use Apache::AxKit::Exception;
  use Apache::Constants qw(OK DECLINED);
  #use XML::Parser;
  
  # use vars qw/$COUNT/;
  
  sub new {
      my $class = shift;
      my $apache = shift;
      my $self = bless { apache => $apache }, $class;
      
      if (my $alternate = $AxKit::Cfg->ProviderClass()) {
          AxKit::reconsecrate($self, $alternate);
      }
      
      $self->init(@_);
      
      AxKit::add_depends($self->key());
      
      return $self;
  }
  
  sub init {
      # blank - override to provide functionality
  }
  
  # sub DESTROY {
  #     AxKit::Debug(7, "Provider->DESTROY Count: " . --$COUNT);
  # }
  
  sub apache_request {
      my $self = shift;
      return $self->{apache};
  }
  
  sub has_changed {
      my $self = shift;
      my $time = shift;
      return $self->mtime > $time;
  }
  
  sub decline {
      my $self = shift;
      
      AxKit::Debug(4, "provider declined");
      return DECLINED;
  }
  
  sub get_ext_ent_handler {
      my $self = shift;
      return sub {
          my ($e, $base, $sysid, $pubid) = @_;
  #        warn "ext_ent: base => $base, sys => $sysid, pub => $pubid\n";
          if ($sysid =~ /^http:/) {
              if ($pubid) {
                  return ''; # do not bring in public DTD's
              }
              eval {
                  require HTTP::GHTTP;
              };
              if ($@) {
                  require LWP::Simple;
                  import LWP::Simple;
                  return get($sysid) || die "Cannot get $sysid";
              }
              my $r = HTTP::GHTTP->new($sysid);
              $r->process_request;
              return $r->get_body;
          }
          elsif ($sysid =~ /^(https|ftp):/) {
              if ($pubid) {
                  return ''; # do not bring in public DTD's
              }
              die "Cannot download https (SSL) or ftp URL's yet. Patches welcome";
          }
          
  #        warn "File provider ext_ent_handler called with '$sysid'\n";
          my $provider = Apache::AxKit::Provider->new(
                  AxKit::Apache->request,
                  uri => $sysid,
  #                rel => $self,
                  );
  #        warn "Got provider with key: ", $provider->key, "\n";
          my $str = $provider->get_strref;
  #        warn "Returning string with length: ", length($$str), "\n";
          return $$str;
      };
  }
  
  sub get_styles {
      my $self = shift;
      my ($media, $pref_style) = @_;
      
      if ($pref_style eq '#default') {
          undef $pref_style;
      }
      
      my $xml_styles = [];
      my $vals = [];
      
      my $key = $self->key();
      
      # need to extract the following from the XML file:
      #   DocType Public Identifier
      #   DTD filename
      #   Root element name (including namespace)
      # use three element array @$vals
      
      if (defined &Apache::AxKit::Provider::xs_get_styles_fh) {
          AxKit::Debug(2, "using XS get_styles (libxml2)\n");
          my ($xs_styles, $doctype, $dtd, $root) = 
                  $self->xs_get_styles($media, $pref_style);
          @$xml_styles = @$xs_styles;
          @$vals = ($doctype, $dtd, $root);
      }
      else {
          require XML::Parser;
          
          AxKit::Debug(4, "get_styles: creating XML::Parser");
          
          my $xml_parser = XML::Parser->new(
                  Namespaces => 1,
                  ErrorContext => 2,
                  Handlers => {
                      Start => \&parse_start,
                      Doctype => \&parse_dtd,
                      Proc => \&parse_pi,
                  },
              );
      
          my $to_parse;
          eval { 
              $to_parse = $self->get_fh();
          };
          if ($@) {
              $to_parse = ${ $self->get_strref(); };
          }
          
          AxKit::Debug(4, "get_styles: calling XML::Parser->parse('$key')");
          $xml_parser->parse(
                  $to_parse,
                  XMLStyle_preferred => $pref_style,
                  XMLStyle_media => $media,
                  XMLStyle_style => $xml_styles,
                  XMLStyle_vals => $vals,
                  XMLStyle_style_screen => [],
                  );
              
          AxKit::Debug(4, "get_styles: parse returned successfully");
      }
      
      # Let GetMatchingProcessors to process the @$styles array
      AxKit::Debug(4, "Calling GetMatchingProcessors with ($media, $pref_style, $vals->[0], $vals->[1], $vals->[2])");
      my @styles = $AxKit::Cfg->GetMatchingProcessors($media,
  		$pref_style, @$vals[0 .. 2], $xml_styles, $self);
      
      if (!@styles) {
          throw Apache::AxKit::Exception::Declined(
                  reason => "No styles defined for '$key'"
                  );
      }
      
      # get mime-type => module mapping
      my $style_mapping = $AxKit::Cfg->StyleMap;
  
      AxKit::Debug(3, "get_styles: loading style modules");    
      for my $style (@styles) {
          my $mapto;
          AxKit::Debug(4, "get_styles: looking for mapping for style type: '$style->{type}'");
          if (!( $mapto = $style_mapping->{ $style->{type} } )) {
              throw Apache::AxKit::Exception::Declined(
                      reason => "No implementation mapping available for type '$style->{type}'"
                      );
          }
  
          $style->{module} = $mapto;
  
          # first load module if it's not already loaded.
          eval {
              AxKit::load_module($mapto);
          };
          if ($@) {
              throw Apache::AxKit::Exception::Error(
                      -text => "Load of '$mapto' failed with: $@"
                      );
          }
      }
      
      return \@styles;
  }
  
  sub xs_get_styles {
      my $self = shift;
      my ($media, $pref_style) = @_;
      
      my $bits;
      eval {
          my $fh = $self->get_fh();
          AxKit::Debug(4, "calling xs_get_styles_fh()\n");
          $bits = xs_get_styles_fh($self->apache_request, $fh);
      };
      if ($@) {
          my $strref = $self->get_strref();
          AxKit::Debug(4, "calling xs_get_styles_str()\n");
          $bits = xs_get_styles_str($self->apache_request, $$strref);
      }
      
      my @xml_stylesheet = @{$bits->[0]};
      my %attribs = @{$bits->[2]};
      my $element = $bits->[1];
      
      # resolve namespaces (libxml doesn't!)
      if ($attribs{'xmlns'} && $element !~ /:/) {
          $element = "{$attribs{xmlns}}$element";
      }
      elsif ($element =~ /^(.*):(.*)$/) {
          my ($prefix, $el) = ($1, $2);
          my $ns = $attribs{"xmlns:$prefix"};
          $element = "{$ns}$el";
      }
      
      my $e = {
          XMLStyle_style => [], 
          XMLStyle_style_screen => [],
          XMLStyle_preferred => $pref_style,
          XMLStyle_media => $media,
          };
      
      foreach my $pi (@xml_stylesheet) {
          parse_pi($e, "xml-stylesheet", $pi);
      }
      
      if (!@{$e->{XMLStyle_style}} && !$e->{XMLStyle_style_persistant}) {
          if ($e->{XMLStyle_style_screen_persistant}) {
              push @{$e->{XMLStyle_style}}, @{$e->{XMLStyle_style_screen_persistant}};
          }
          if (@{$e->{XMLStyle_style_screen}}) {
              push @{$e->{XMLStyle_style}}, @{$e->{XMLStyle_style_screen}};
          }
      }
      elsif ($e->{XMLStyle_style_persistant}) {
          unshift @{$e->{XMLStyle_style}}, @{$e->{XMLStyle_style_persistant}};
      }
      
      AxKit::Debug(4, "xs_get_styles returned: $bits->[3], $bits->[4], $element\n");
      
      return ($e->{XMLStyle_style}, $bits->[3], $bits->[4], $element);
  }
  
  sub parse_start {
      my ($e, $el) = @_;
      my $ns = $e->namespace($el);
      # use James Clark's universal name format
      $e->{XMLStyle_vals}[2] = $ns ? "{$ns}$el" : $el;
      
      if (!@{$e->{XMLStyle_style}} && !$e->{XMLStyle_style_persistant}) {
          if ($e->{XMLStyle_style_screen_persistant}) {
              push @{$e->{XMLStyle_style}}, @{$e->{XMLStyle_style_screen_persistant}};
          }
          if (@{$e->{XMLStyle_style_screen}}) {
              push @{$e->{XMLStyle_style}}, @{$e->{XMLStyle_style_screen}};
          }
      }
      elsif ($e->{XMLStyle_style_persistant}) {
          unshift @{$e->{XMLStyle_style}}, @{$e->{XMLStyle_style_persistant}};
      }
      
      $e->finish();
  }
  
  sub parse_dtd {
      my ($e, $name, $sysid, $pubid) = @_;
      
      $e->{XMLStyle_vals}[0] = $pubid;
      $e->{XMLStyle_vals}[1] = $sysid;
  }
  
  sub parse_pi {
      my $e = shift;
      my ($target, $data) = @_;
      if ($target ne 'xml-stylesheet') {
          return;
      }
      
      my $style;
      
      $data = ' ' . $data;
      
      while ($data =~ /\G
              \s+
              (href|type|title|media|charset|alternate)
              \s*
              =
              \s*
              (["']) # match quotes "'
              ([^\2<]*?)
              \2     # balance quotes "'
              /gcx) {
          my ($attr, $val) = ($1, $3);
          AxKit::Debug(10, "parse_pi: $attr = $val");
          $style->{$attr} = $val;
      }
      
      if (!exists($style->{href}) || !exists($style->{type})) {
          # href and type are #REQUIRED
          AxKit::Debug(3, "pi_get_styles: Invalid <?xml-stylesheet?> processing instruction\n");
          return;
      }
      
      my $mediamatch = 0;
  
      $style->{media} ||= 'screen'; # default according to TR/REC-html40
      $style->{alternate} ||= 'no'; # default according to TR/xml-stylesheet
  
      # See http://www.w3.org/TR/REC-html40/types.html#type-media-descriptors
      # for details of what we're doing here.
      my @mediatypes = split(/,\s*/, $style->{media});
      
      # strip anything after first non-(A-Za-z0-9\-) character (see REC-html40)
      @mediatypes = map { $_ =~ s/[^A-Za-z0-9\-].*$//; $_; } @mediatypes;
  
  #    warn "media types are ", join(',', @mediatypes), " [$style->{media}] [$e->{XMLStyle_media}]\n";
  
      # remove unwanted entries
      @mediatypes = grep /^(screen|tty|tv|projection|handheld|print|braille|aural|all)$/, @mediatypes;
  
      if (grep { $_ eq $e->{XMLStyle_media} } @mediatypes) {
          # found a match for the preferred media type!
  #        warn "Media matches!\n";
          $mediamatch++;
      }
      
      if (grep { $_ eq 'all' } @mediatypes) {
          # always match on media type "all"
  #        warn "Media is \"all\"\n";
          $mediamatch++;
      }
      
      if ($e->{XMLStyle_preferred}) {
          # warn "someone picked a \"title\" : $e->{XMLStyle_preferred}. Use persistant and alternate styles\n";
          if (
                  ($style->{alternate} eq 'no') 
                  && (!exists $style->{title})
              )
          {
              # warn "This is a persistant style - always make it first\n";
              if ($mediamatch) {
                  push @{$e->{XMLStyle_style_persistant}}, $style;
              }
              elsif ($style->{media} eq 'screen') {
                  # store away in case we need the screen matches
                  push @{$e->{XMLStyle_style_screen_persistant}}, $style;
              }
          }
          elsif (lc($style->{title}) eq lc($e->{XMLStyle_preferred})) 
          {
              # warn "matching style\n";
              if ($mediamatch) {
                  push @{$e->{XMLStyle_style}}, $style;
              }
              elsif ($style->{media} eq 'screen') {
                  push @{$e->{XMLStyle_style_screen}}, $style;
              }
          }
      }
      else {
          # warn "no \"title\" selected. Use persistent and preferred styles\n";
          if (
                  ($style->{alternate} eq 'no')
                  && (!exists $style->{title})
              ) 
          {
              if ($mediamatch) {
                  # This is the persistant style
                  push @{ $e->{XMLStyle_style_persistant} }, $style;
              }
              elsif ($style->{media} eq 'screen') {
                  push @{$e->{XMLStyle_style_screen_persistant}}, $style;
              }
          }
          elsif (
                  ($style->{alternate} eq 'no')
                  && (exists $style->{title})
                  )
          {
              if ($mediamatch) {
                  push @{ $e->{XMLStyle_style} }, $style;
              }
              elsif ($style->{media} eq 'screen') {
                  push @{ $e->{XMLStyle_style_screen} }, $style;
              }
          }
      }
  }
  
  1;
  __END__
  
  =head1 NAME
  
  Apache::AxKit::Provider - base Provider class
  
  =head1 SYNOPSIS
  
  Override the base Provider class and enable it using:
  
      AxProvider MyClass
      
      # alternatively use:
      # PerlSetVar AxProvider MyClass
  
  =head1 DESCRIPTION
  
  The Provider class is used to read in the data source for the given URL.
  The default Provider is Provider::File, which reads from the filesystem,
  although obviously you can read from just about anywhere.
  
  Should you wish to override the default Provider, these are the methods
  you need to implement:
  
  =head2 process()
  
  Determine whether or not to process this URL. For example, you don't want
  to process a directory request, or if the resource doesn't exist. Return 1
  to tell AxKit to process this URL, or die with a Declined exception (with 
  a reason) if you do not wish to process this URL.
  
  =head2 mtime()
  
  Return the last modification time in days before the current time.
  
  =head2 get_styles()
  
  Extract the stylesheets from the XML resource. Should return an array
  ref of styles. The style entries are hashes refs with required keys
  'href' and 'type'.
  
  =head2 get_fh()
  
  This method should return an open filehandle, or die if that's not possible.
  
  =head2 get_strref()
  
  This method returns a reference to a scalar containing the contents of the
  stylesheet, or die if that's not possible. At least one of get_fh or 
  get_strref B<must> work.
  
  =head2 key()
  
  This method should return a "key" value that is unique to this URL.
  
  =head2 get_ext_ent_handler()
  
  This should return a sub reference that can be used instead of 
  XML::Parser's default external entity handler. See the XML::Parser
  documentation for what this sub should do (or look at the code in
  the File provider).
  
  =head2 exists()
  
  Return 1 if the resource exists (and is readable).
  
  =cut
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/typemap
  
  Index: typemap
  ===================================================================
  TYPEMAP
  Apache::AxKit::CharsetConv T_PTROBJ
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/Language/AxPoint.pm
  
  Index: AxPoint.pm
  ===================================================================
  # $Id: AxPoint.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
  
  package Apache::AxKit::Language::AxPoint;
  
  @ISA = ('Apache::AxKit::Language');
  
  use strict;
  
  use Apache;
  use Apache::Request;
  use Apache::AxKit::Language;
  use Apache::AxKit::Provider;
  use PDFLib 0.02;
  use XML::XPath;
  use File::Basename ();
  
  my @xindent;
  
  sub stylesheet_exists () { 0; }
  
  sub handler {
      my $class = shift;
      my ($r, $xml_provider, undef, $last_in_chain) = @_;
  
      my $xpath = XML::XPath->new();
      
      my $source_tree;
      
      my $xml_parser = XML::Parser->new(
              ErrorContext => 2,
              Namespaces => $XML::XPath::VERSION < 1.07 ? 1 : 0,
              ParseParamEnt => 1,
              );
      
      my $parser = XML::XPath::XMLParser->new(parser => $xml_parser);
      
      if (my $entity_handler = $xml_provider->get_ext_ent_handler()) {
          $xml_parser->setHandlers(
                  ExternEnt => $entity_handler,
                  );
      }
      
      AxKit::Debug(6, "AxPoint: Getting XML Source");
      
      if (my $dom = $r->pnotes('dom_tree')) {
          # dom_tree is an XML::XPath DOM
          $source_tree = $dom;
          delete $r->pnotes()->{'dom_tree'};
      }
      elsif (my $xml = $r->pnotes('xml_string')) {
          eval {
              $source_tree = $parser->parse($xml);
          };
          if ($@) {
              throw Apache::AxKit::Exception::Error(-text => "Parse of xml_string failed: $@");
          }
      }
      else {
          $source_tree = get_source_tree($xml_provider, $parser);
      }
      
      $xpath->set_context($source_tree);
  
      AxKit::Debug(7, "AxPoint: creating pdf");
      
      my $pdf = PDFLib->new();
      $pdf->papersize("slides");
      $pdf->set_border_style("solid", 0);
      
      $pdf->info(Title => $xpath->findvalue("/slideshow/title"));
      $pdf->info(Creator => $xpath->findvalue("/slideshow/metadata/speaker"));
      
      my ($logo_node) = $xpath->findnodes("/slideshow/metadata/logo");
      my ($bg_node) = $xpath->findnodes("/slideshow/metadata/background");
  
      AxKit::Debug(7, "AxPoint: loading main bg/logo images");
      
      my ($logo, $bg);
      if ($logo_node) {
          $logo = $pdf->load_image(
              filename => $logo_node->string_value,
              filetype => get_filetype($logo_node->string_value),
              );
          if (!$logo) {
              AxKit::Debug(7, "AxPoint: failed to load logo " . $logo_node->string_value);
              $pdf->finish;
              die "Cannot load image $logo_node!";
          }
      }
      
      if ($bg_node) {
          $bg = $pdf->load_image(
              filename => $bg_node->string_value,
              filetype => get_filetype($bg_node->string_value),
              );
          if (!$bg) {
              AxKit::Debug(7, "AxPoint: failed to load logo " . $bg_node->string_value);
              $pdf->finish;
              die "Cannot load image $bg_node!";
          }
      }
  
      AxKit::Debug(7, "AxPoint: Creating new_page sub");
      
      my $new_page = sub {
          my ($node, $trans) = @_;
          
          $pdf->start_page;
          
          my $transition = $trans || $node->findvalue('ancestor-or-self::*/@transition') || 'replace';
          
          $pdf->set_parameter("transition", lc($transition)) if $transition;
      
          $pdf->add_image(img => $bg, x => 0, y => 0, scale => $bg_node->findvalue('@scale') || 1.0)
                  if $bg_node;
          
          if ($logo_node) {
              my $logo_scale = $logo_node->findvalue('@scale') || 1.0;
              my $logo_w = $logo->width * $logo_scale;
              $pdf->add_image(img => $logo, x => 612 - $logo_w, y => 0, scale => $logo_scale);
          }
      
          $pdf->set_font(face => "Helvetica", size => 18.0);
      
          @xindent = ();
              
          $pdf->set_text_pos(80, 300);
      };
      
      AxKit::Debug(7, "AxPoint: creating front page");
      # title page
      $new_page->($xpath->findnodes('/')->get_node(1));
      
      $pdf->set_font(face => "Helvetica-Bold", size => 24);
      
      my $root_bookmark = $pdf->add_bookmark(text => "Title", open => 1);
      
      $pdf->print_boxed($xpath->findvalue("/slideshow/title"),
              x => 20, y => 50, w => 570, h => 300, mode => "center");
      
      $pdf->print_line("");
      $pdf->print_line("");
      $pdf->print_line("");
      $pdf->print_line("");
      
      my ($x, $y) = $pdf->get_text_pos();
      
      $pdf->set_font(face => "Helvetica-Bold", size => 20);
      
      $pdf->add_link(link => "mailto:" . $xpath->findvalue("/slideshow/metadata/email"),
              x => 20, y => $y - 10, w => 570, h => 24);
      $pdf->print_boxed($xpath->findvalue("/slideshow/metadata/speaker"),
              x => 20, y => 40, w => 570, h => $y - 24, mode => "center");
      
      $pdf->print_line("");
      (undef, $y) = $pdf->get_text_pos();
      
      $pdf->add_link(link => $xpath->findvalue("/slideshow/metadata/link"),
              x => 20, y => $y - 10, w => 570, h => 24);
      $pdf->print_boxed($xpath->findvalue("/slideshow/metadata/organisation"),
              x => 20, y => 40, w => 570, h => $y - 24, mode => "center");
  
      AxKit::Debug(7, "AxPoint: creating slides");
      
      foreach my $slideset ($xpath->findnodes("/slideshow/*[name() = 'slideset' or name() = 'slide']")) {
          if ($slideset->getName() eq 'slide') {
              process_slide($pdf, $new_page, $slideset, $root_bookmark);
          }
          else {
              process_slideset($pdf, $new_page, $slideset, $root_bookmark);
          }
      }
  
      AxKit::Debug(7, "AxPoint: outputting pdf");
      
      $r->content_type("application/pdf");
  
      AxKit::Debug(5, "finish pdf");
      
      $pdf->finish;
      
      $r->print( $pdf->get_buffer );
  }
  
  sub process_slideset {
      my ($pdf, $new_page, $slideset, $parent_bookmark) = @_;
      $new_page->($slideset);
      
      my $slide_bookmark = $pdf->add_bookmark(
              text => $slideset->findvalue("title"), 
              level => 2, 
              parent_of => $parent_bookmark, 
              open => 1,
              );
      
      $pdf->set_font(face => "Helvetica", size => 24);
      $pdf->print_boxed($slideset->findvalue("title"),
              x => 20, y => 50, w => 570, h => 200, mode => "center");
  
      my ($x, $y) = $pdf->get_text_pos();
      $pdf->add_link(link => $slideset->findvalue('title/@href'),
          x => 20, y => $y - 5, w => 570, h => 24) if $slideset->findvalue('title/@href');
  
      if (my $subtitle = $slideset->findvalue("subtitle")) {
        $pdf->set_font(face => "Helvetica", size => 18);
        $pdf->print_boxed($subtitle,
            x => 20, y => 20, w => 570, h => 200, mode => "center");
        if (my $href = $slideset->findvalue('subtitle/@href')) {
            ($x, $y) = $pdf->get_text_pos();
            $pdf->add_link(link => $href,
                x => 20, y => $y - 5, w => 570, h => 18);
        }
      }
      
      foreach my $slide ($slideset->findnodes("slide")) {
          process_slide($pdf, $new_page, $slide, $slide_bookmark);
      }
  }
  
  sub process_slide {
      my ($pdf, $new_page, $slide, $parent_bookmark, $do_up_to) = @_;
      
      $pdf->end_page;
      my @images;
      foreach my $image ($slide->findnodes("image")) {
          push @images, $pdf->load_image(
                  filename => $image->string_value,
                  filetype => get_filetype($image->string_value),
                  );
      }
      
      if ($do_up_to) {
          my @nodes = $slide->findnodes("point|source_code|image");
          my $do_to_node = $nodes[$do_up_to - 1];
          $new_page->($slide, $do_to_node->findvalue('@transition'));
      }
      else {
          $new_page->($slide);
      }
      
      my $h = 300;
      if (my $title = $slide->findvalue("title")) {
          $pdf->add_bookmark(text => $title, level => 3, parent_of => $parent_bookmark) unless $do_up_to;
          $pdf->set_font(face => "Helvetica", size => 24);
          $pdf->print_boxed($title, x => 20, y => 350, 
                  w => 570, h => 70, mode => "center");
  
          my ($x, $y) = $pdf->get_text_pos();
          $pdf->add_link(link => $slide->findvalue('title/@href'),
                  x => 20, y => $y - 5, w => 570, h => 24) if $slide->findvalue('title/@href');
  
          $h = 370;
      }
      
      $pdf->set_text_pos(60, $h);
  
      my $new_do_up_to = 1;
      foreach my $item ($slide->findnodes("point|source_code|image")) {
          if (!$do_up_to && $item->findvalue('@transition')) {
              process_slide($pdf, $new_page, $slide, $parent_bookmark, $new_do_up_to);
          }
  
          if ($do_up_to) {
              last if $do_up_to == $new_do_up_to;
          }
          
          if ($item->getName eq "point") {
              point($pdf, $item->findvalue('@level') || 1, $item->string_value, $item->findvalue('@href'));
          }
          elsif ($item->getName eq 'source_code') {
              source_code($pdf, $item->string_value, $item->getAttribute('fontsize'));
          }
          elsif ($item->getName eq 'image') {
              image($pdf, $item->getAttribute('scale') || 1, shift @images, $item->findvalue('@href'));
          }
          
          $new_do_up_to++;
      }
      
  }
  
  ###########################################################
  # functions
  ###########################################################
  
  sub new_page {
      my ($pdf, $node, $logo, $bg) = @_;
      
      $pdf->start_page;
      
      my $transition = $node->findvalue('ancestor-or-self::node()/@transition');
      
      $pdf->set_parameter("transition", lc($transition)) if $transition;
  
      $pdf->add_image(img => $bg, x => 0, y => 0, scale => 1.0);
  
      $pdf->add_image(img => $logo, x => 420, y => 0, scale => 0.4);
  
      $pdf->set_font(face => "Helvetica", size => 18.0);
  
      @xindent = ();
          
      $pdf->set_text_pos(80, 300);
      
  }
  
  sub bullet {
      my ($pdf, $level) = @_;
      
      my ($char, $size);
      if ($level == 1) {
          $char = "l";
          $size = 18;
      }
      elsif ($level == 2) {
          $char = "u";
          $size = 16;
      }
      elsif ($level == 3) {
          $char = "p";
          $size = 14;
      }
      
      my $leading = $pdf->get_value("leading");
      $pdf->set_value("leading", $leading + 4);
      $pdf->set_value("leading", $leading + 20) if $level == 1;
      
      $pdf->print_line("");
      
      my ($x, $y) = $pdf->get_text_pos;
      
      if (!@xindent || $level > $xindent[0]{level}) {
          unshift @xindent, {level => $level, x => $x};
      }
      
      $pdf->set_font(face => "ZapfDingbats", size => $size - 4, encoding => "builtin");
      $pdf->print($char);
      $pdf->set_font(face => "Helvetica", size => $size);
      $pdf->print("   ");
      return $size;
  }
  
  sub point {
      my ($pdf, $level, $text, $href) = @_;
      
      my ($x, $y) = $pdf->get_text_pos;
      
      if (@xindent && $level <= $xindent[0]{level}) {
          my $last;
          while ($last = shift @xindent) {
              if ($last->{level} == $level) {
                  $pdf->set_text_pos($last->{x}, $y);
                  $x = $last->{x};
                  last;
              }
          }
      }
      
      if ($level == 1) {
          $pdf->set_text_pos(80, $y);
      }
      
      my $size = bullet($pdf, $level);
      
      ($x, $y) = $pdf->get_text_pos;
      
      $pdf->print_boxed($text, x => $x, y => 0, w => 570 - $x, h => $y + $size);
      $pdf->add_link(link => $href,
          x => 20, y => $y - 5 + $level, w => 570, h => $size) if $href;
  
  }
  
  sub source_code {
      my ($pdf, $text, $size) = @_;
      
      my ($x, $y) = $pdf->get_text_pos;
      
      $pdf->set_font(face => "Courier", size => $size || 14);
      
      $y -= 10 if @xindent;
      
      $pdf->print_boxed($text, x => 80, y => 0, w => 500, h => $y);
      
  }
  
  sub image {
      my ($pdf, $scale, $file_handle, $href) = @_;
      
      $pdf->print_line("");
      
      my ($x, $y) = $pdf->get_text_pos;
      
      my ($imgw, $imgh) = (
              $pdf->get_value("imagewidth", $file_handle->img),
              $pdf->get_value("imageheight", $file_handle->img)
              );
      
      $imgw *= $scale;
      $imgh *= $scale;
      
      $pdf->add_image(img => $file_handle,
              x => (612 / 2) - ($imgw / 2),
              y => ($y - $imgh),
              scale => $scale);
      $pdf->add_link(link => $href, x => 20, y => $y - $imgh, w => 570, h => $imgh) if $href;
      
      $pdf->set_text_pos($x, $y - $imgh);
  }
  
  sub get_filetype {
      my $filename = shift;
  
      AxKit::Debug(8, "AxPoint: get_filetype($filename)");
      
      my ($suffix) = $filename =~ /([^\.]+)$/;
      $suffix = lc($suffix);
      if ($suffix eq 'jpg') {
          return 'jpeg';
      }
      return $suffix;
  }
  
  sub get_source_tree {
      my ($provider, $parser) = @_;
      my $source_tree;
      AxKit::Debug(7, "AxPoint: reparsing file");
      eval {
          my $fh = $provider->get_fh();
          # warn("parsing FH $fh with parser $parser\n");
          local $/;
          my $contents = <$fh>;
          # warn("FH contains: $contents\n");
          $source_tree = $parser->parse($contents);
          # warn("Parse completed\n");
          close($fh);
      };
      if ($@) {
          # warn("parse_fh failed\n");
          $source_tree = $parser->parse(${ $provider->get_strref() });
      }
  
      # warn("get_source_tree = $source_tree\n");
      AxKit::Debug(7, "AxPoint: returning source tree"); 
      return $source_tree;
  }
  
  1;
  
  __END__
  
  =head1 NAME
  
  AxPoint - An AxKit PDF Slideshow generator
  
  =head1 SYNOPSIS
  
    AxAddStyleMap application/x-axpoint Apache::AxKit::Language::AxPoint
    
    AxAddRootProcessor application/x-axpoint NULL slideshow
  
  =head1 DESCRIPTION
  
  I got sick of not being able to do pretty slideshows about AxKit without
  resorting to the bloated OpenOffice. So I decided to write something that
  allows you to do simple slideshows with bullet points and a few other
  niceties using XML, AxKit, and rendering to PDF.
  
  I discovered that PDF can do transitions, and full screen mode (even on Linux),
  and so it makes the perfect medium for doing a slideshow.
  
  Note: This module requires PDFLib.pm from CPAN. It also requires the library
  B<pdflib>, which is available under the Alladin license from
  http://www.pdflib.com/. The Alladin license does not allow re-distribution
  of modified binaries, so it is not strictly open source, but it is free to
  use even for commercial use.
  
  =head1 Usage
  
  The SYNOPSIS section describes how to set this thing up in AxKit, so here I
  will focus on the syntax of AxPoint XML files. I tend to use the suffix ".axp"
  for my AxPoint files, but you are free to use whatever you please.
  
  The easiest way to describe all the features is with a complete presentation
  first:
  
    <slideshow transition="glitter">
      <title>AxPoint Example</title>
      <metadata>
        <speaker>Matt Sergeant</speaker>
        <email>matt@axkit.com</email>
        <organisation>AxKit.com Ltd</organisation>
        <link>http://axkit.com/</link>
        <logo>ax_logo.png</logo>
        <background>bg.png</background>
      </metadata>
      
      <slide transition="dissolve">
        <title>Top Level Slide</title>
        <point>This is a top level slide</point>
        <point>It is a child of the &lt;slideshow> tag</point>
        <point>Can have it's own transition</point>
      </slide>
      
      <slideset transition="blinds">
        <title>Slidesets</title>
        <subtitle>Slidesets can have subtitles</subtitle>
        
        <slide>
          <title>Slideset Example</title>
          <point>A slideset groups slides under a particular title</point>
          <point>Slidesets can have a transition for the group of slides</point>
        </slide>
      </slideset>
      
      <slideset>
        <title>Slide Tags</title>
        
        <slide>
          <title>Bullet Points</title>
          <point>Level 1 bullet : &lt;point></point>
          <point level="1">Another Level 1 bullet : &lt;point level="1"></point>
          <point level="2">Level 2 bullet : &lt;point level="2"></point>
          <point level="3">Level 3 bullet : &lt;point level="3"></point>
          <point>Back to level 1</point>
        </slide>
        
        <slide>
          <title>Source Core</title>
          <source_code><![CDATA[
  Source code
  uses   fixed       font
  Don't forget to use CDATA sections so you can
   <include/><some/><xml/>
          ]]></source_code>
        </slide>
        
        <slide>
          <title>Pictures</title>
          <point>Images are very simple, and always centered</point>
          <image scale="0.5" href="file_large.jpg">file.jpg</image>
        </slide>
        
      </slideset>
      
      <slideset transition="dissolve">
        <title href="http://foo.bar/aslidesettitle">Slideset titles can have href attributes</title>
        <subtitle href="http://foo.bar/aslidesetsubtitle">Slideset subtitles too</subtitle>
        
        <slide>
          <title href="http://foo.bar/aslidetitle">Don't forget links for slide titles</title>
          <point href="http://foo.bar/apoint">...and for points of various levels</point>
        </slide>
      </slideset>
      
      
    </slideshow>
  
  
  It's not very complex. And with good reason: generating PDFs can be slow. With this simple schema
  we can generate a 100 slide PDF in about 1 second. There are many deficiencies with the tagset,
  most significantly that no bold or italics or any coloring can be applied to the text by using
  tags. The reason being that this is quite hard to do with PDFLib - you have to do text measurement
  yourself and do the word-wrapping yourself, and so on. The way it is setup right now is very
  simple to use and implement.
  
  =head1 AUTHOR
  
  Matt Sergeant, matt@axkit.com
  
  =cut
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/Language/LibXSLT.pm
  
  Index: LibXSLT.pm
  ===================================================================
  # $Id: LibXSLT.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
  
  package Apache::AxKit::Language::LibXSLT;
  
  use strict;
  use vars qw/@ISA $VERSION %DEPENDS/;
  use XML::LibXSLT 1.30;
  use XML::LibXML;
  use Apache;
  use Apache::Request;
  use Apache::AxKit::Language;
  use Apache::AxKit::Provider;
  use File::Basename qw(dirname);
  
  @ISA = 'Apache::AxKit::Language';
  
  $VERSION = 1.0; # this fixes a CPAN.pm bug. Bah!
  
  my %style_cache;
  
  sub reset_depends {
      %DEPENDS = ();
  }
  
  sub add_depends {
      $DEPENDS{shift()}++;
  }
  
  sub get_depends {
      return keys %DEPENDS;
  }
  
  sub handler {
      my $class = shift;
      my ($r, $xml, $style, $last_in_chain) = @_;
      
      my ($xmlstring, $xml_doc);
      
      AxKit::Debug(7, "[LibXSLT] getting the XML");
      
      if (my $dom = $r->pnotes('dom_tree')) {
          $xml_doc = $dom;
          delete $r->pnotes()->{'dom_tree'};
      }
      else {
          $xmlstring = $r->pnotes('xml_string');
      }
      
      my $parser = XML::LibXML->new();
      local $XML::LibXML::match_cb = \&match_uri;
      local $XML::LibXML::open_cb = \&open_uri;
      local $XML::LibXML::read_cb = \&read_uri;
      local $XML::LibXML::close_cb = \&close_uri;
  
      if (!$xml_doc && !$xmlstring) {
          eval {
              my $fh = $xml->get_fh();
              $xml_doc = $parser->parse_fh($fh, $r->uri());
          };
          if ($@) {
              $xmlstring = ${$xml->get_strref()};
              $xml_doc = $parser->parse_string($xmlstring, $r->uri());
          }
      } 
      elsif ($xmlstring) {
          $xml_doc = $parser->parse_string($xmlstring, $r->uri());
      }
  
      $xml_doc->process_xinclude();
      
      AxKit::Debug(7, "[LibXSLT] parsing stylesheet");
  
      my $stylesheet;
      my $cache = $style_cache{$style->key()};
      if (!$style->has_changed($cache->{mtime})) {
          my $changed = 0;
          DEPENDS:
          foreach my $depends (@{ $cache->{depends} }) {
              my $p = Apache::AxKit::Provider->new($r, key => $depends);
              if ( $p->has_changed( $cache->{mtime} ) ) {
                  $changed = 1;
                  last DEPENDS;
              }
          }
          if (!$changed) {
              AxKit::Debug(7, "[LibXSLT] stylesheet cached");
              $stylesheet = $style_cache{$style->key()}{style};
          }
      }
      
      if (!$stylesheet) {
          my $style_doc;
          reset_depends();
          my $style_uri = $style->apache_request->uri();
          AxKit::Debug(7, "[LibXSLT] parsing stylesheet $style_uri");
          eval {
              my $fh = $style->get_fh();
              $style_doc = $parser->parse_fh($fh, $style_uri);
          };
          if ($@) {
              my $stylestring = ${$style->get_strref()};
              $style_doc = $parser->parse_string($stylestring, $style_uri);
          }
          
          $stylesheet = XML::LibXSLT->parse_stylesheet($style_doc);
          
          $style_cache{$style->key()} = 
                  { style => $stylesheet, mtime => time, depends => [ get_depends() ] };
      }
  
      # get request form/querystring parameters
      my @params;
      my $cgi = Apache::Request->instance($r);
      @params = fixup_params(map { $_ => $cgi->param($_) } $cgi->param);
      
      AxKit::Debug(7, "[LibXSLT] performing transformation");
  
      my $results = $stylesheet->transform($xml_doc, @params);
      
      if ($last_in_chain && $XML::LibXSLT::VERSION >= 1.03) {
          my $encoding = $stylesheet->output_encoding;
          my $type = $stylesheet->media_type;
          $r->content_type("$type; charset=$encoding");
      }
  
      $stylesheet->output_fh($results, $r) if $last_in_chain;
      
      $r->pnotes('dom_tree', $results);
       
  #         warn "LibXSLT returned $output \n";
  #         print $stylesheet->output_string($results);
      
  }
  
  sub fixup_params {
      my @results;
      while (@_) {
          push @results, XML::LibXSLT::xpath_to_string(
                  splice(@_, 0, 2)
                  );
      }
      return @results;
  }
  
  sub match_uri {
      my $uri = shift;
  #    warn("match: $uri\n");
      AxKit::Debug(8, "LibXSLT match_uri: $uri");
      return $uri !~ /^\w+:/; # only handle URI's without a scheme
  }
  
  sub open_uri {
      my $uri = shift;
  #    warn("open: $uri\n");
      my $provider = Apache::AxKit::Provider->new(
          AxKit::Apache->request(),
          uri => $uri,
          );
      add_depends($provider->key());
      my $str = $provider->get_strref;
      return $$str;
  }
  
  sub close_uri {
  }
  
  sub read_uri {
      return substr($_[0], 0, $_[1], "");
  }
  
  1;
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/Language/NotXSLT.pm
  
  Index: NotXSLT.pm
  ===================================================================
  package Apache::AxKit::Language::NotXSLT;
  
  # $Id: NotXSLT.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
  
  use strict;
  use vars qw(@ISA $VERSION $PREFIX $cache);
  
  $VERSION = '0.04';
  
  use Apache;
  use Apache::Constants;
  use Apache::File;
  use XML::XPath;
  use XML::XPath::Node;
  use XML::XPath::XMLParser;
  use Apache::AxKit::Language;
  
  @ISA = 'Apache::AxKit::Language';
  
  sub handler {
  	my $class = shift;
  	my ($r, $xml, $style) = @_;
  	
  
  #	warn "In NotXSLT with $xmlfile and $stylesheet\n";
  
  	my $source_finder = XML::XPath->new;
  	
  	my $parser = XML::XPath::XMLParser->new;
  	
  	my $source_tree;
  	
  	my $mtime = $xml->mtime();
  	my $key = $xml->key();
  	
  	if (exists $cache->{$key} && 
  			$cache->{$key}{mtime} <= $mtime) {
  		$source_tree = $cache->{$key}{tree};
  	}
  	else {
  		eval {
  			$source_tree = $parser->parse(${$xml->get_strref});
  		};
  		if ($@) {
  			die "Parse of '$key' failed with $@";
  		}
  		$cache->{$key}{mtime} = $mtime;
  		$cache->{$key}{tree} = $source_tree;
  	}
  	
  	my $template_tree;
  	
  	$mtime = $style->mtime();
  	
  	my $stylekey = $style->key();
  	if (exists $cache->{$stylekey} &&
  			$cache->{$stylekey}{mtime} <= $mtime) {
  		$template_tree = $cache->{$stylekey}{tree}
  	}
  	else {
  		eval {
  			$template_tree = $parser->parse(${$style->get_strref});
  		};
  		if ($@) {
  			die "Parse of stylesheet '$stylekey' failed with $@";
  		}
  		$cache->{$stylekey}{mtime} = $mtime;
  		$cache->{$stylekey}{tree} = $template_tree;
  	}
  	
  	my $root_node;
  	
  	# get namespace prefix for template
  	# here we search through all children of the root node
  	# because there could be comments or PI's here.
  	# The root node MUST contain a xmlns:<prefix>="..."
  	ROOT:
  	foreach my $node ($template_tree->getChildNodes) {
  		if ($node->getNodeType == ELEMENT_NODE) {
  			# first element node!
  			$root_node = $node;
  			foreach my $ns ($node->getNamespaceNodes) {
  				if ($ns->getExpanded eq 'http://sergeant.org/notxslt') {
  					$PREFIX = $ns->getPrefix;
  					last ROOT;
  				}
  			}
  			die "Not a template file - no namespace declaration matching\nhttp://sergeant.org/notxslt in file '$stylekey'";
  		}
  	}
  	
  	my $string = parse($source_finder, $source_tree, $root_node);
  	$r->print($string);
  	
  	return OK;
  	
  }
  
  sub parse_style {
  	my $data = shift;
  	my $style;
  	while ($data =~ /\G\s*$XML::XPath::Parser::NCName\s*=\s*(["'])([^\2<]*?)\2/gco) {
  		my ($attr, $val) = ($1, $3);
  		$style->{$attr} = $val;
  	}
  	
  	if (!exists($style->{href}) || !exists($style->{type})) {
  		warn "Incorrect <?xml-stylesheet?> processing instruction\n";
  		return;
  	}
  	
  	return $style;
  }
  
  sub escape {
  	my $text = shift;
  	$text =~ s/&/&amp;/g;
  	$text =~ s/</&lt;/g;
  	$text =~ s/>/&gt;/g;
  	$text =~ s/"/&quot;/g;
  	$text =~ s/'/&apos;/g;
  	return $text;
  }
  
  sub parse {
  	my ($xp, $context, $node) = @_;
  	
  	# examine node
  	
  	my $string;
  	
  #	print "Node type is ", ref($node), "\n";
  	for ($node->getNodeType) {
  		if ($_ == ELEMENT_NODE) {
  			local $^W;
  			if ($node->getPrefix eq $PREFIX) {
  				$string .= process_template_node($xp, $context, $node);
  			}
  			else {
  				# gather attributes
  				my @attribs;
  				$string .= "<" . $node->getName;
  				foreach my $attr ($node->getAttributeNodes) {
  					$string .= $attr->toString;
  				}
  				
  				if (@{$node->getChildNodes}) {
  					$string .= ">";
  					# process children
  					foreach my $n ($node->getChildNodes) {
  						$string .= parse($xp, $context, $n);
  					}
  					$string .= "</" . $node->getName . ">";
  				}
  				else {
  					$string .= " />";
  				}
  			}
  		}
  		elsif ($_ == TEXT_NODE) {
  			$string .= $node->toString;
  		}
  		elsif ($_ == COMMENT_NODE) {
  			# ignore comments for now.
  		}
  	}
  	
  	return $string;
  }
  
  sub process_template_node {
  	my ($xp, $context, $node) = @_;
  
  	my $string;
  	
  	if ($node->getLocalName eq "select") {
  		# find match in $xp, and output.
  		my $match;
  		foreach my $attrib ($node->getAttributeNodes) {
  			if ($attrib->getName eq 'match') {
  				$match = $attrib->getValue;
  			}
  		}
  		
  		die "No 'match' attribute on $PREFIX:select element" unless $match;
  		
  		my $results = $xp->find($match, $context);
  		
  		die "Match '$match' failed!\n" unless defined $results;
  		
  		if ($results->isa('XML::XPath::NodeSet')) {
  			foreach my $result ($results->get_nodelist) {
  				$string .= $result->toString;
  #				$string .= escape(XML::XPath::XMLParser::as_string($result));
  			}
  		}
  		else {
  			$string .= $results->value;
  #			$string .= escape($results->value);
  		}
  	}
  	elsif ($node->getLocalName eq "for-each") {
  		# find match, get matching nodes, loop over matching nodes
  		my $match;
  		foreach my $attrib ($node->getAttributeNodes) {
  			if ($attrib->getName eq 'match') {
  				$match = $attrib->getValue;
  			}
  		}
  		
  		die "No 'match' attribute on $PREFIX:for-each element" unless $match;
  		
  		my $results = $xp->find($match, $context);
  		
  		if ($results->isa('XML::XPath::NodeSet')) {
  			foreach my $result ($results->get_nodelist) {
  				foreach my $kid ($node->getChildNodes) {
  					$string .= parse($xp, $result, $kid);
  				}
  			}
  		}
  		else {
  			die "$PREFIX:for-each match doesn't match a set of nodes";
  		}
  	}
  	elsif ($node->getLocalName eq "exec") {
  		# find match and ignore results.
  		my $match;
  		foreach my $attrib ($node->getAttributeNodes) {
  			if ($attrib->getName eq 'match') {
  				$match = $attrib->getValue;
  			}
  		}
  		
  		die "No 'match' attribute on $PREFIX:exec element" unless $match;
  		
  		my $results = $xp->find($match, $context);
  	}
  	elsif ($node->getLocalName eq "verbatim") {
  		my $match;
  		foreach my $attrib ($node->getAttributeNodes) {
  			if ($attrib->getName eq 'match') {
  				$match = $attrib->getValue;
  			}
  		}
  		
  		die "No 'match' attribute on $PREFIX:verbatim element" unless $match;
  		
  		my $results = $xp->find($match, $context);
  		
  		if ($results->isa('XML::XPath::NodeSet')) {
  			foreach my $node ($results->get_nodelist) {
  				if ($node->getNodeType == ELEMENT_NODE) {
  					$string .= "<" . $node->getName;
  					foreach my $attr ($node->getAttributeNodes) {
  						$string .= $attr->toString;
  					}
  
  					if ($node->getChildNodes) {
  						$string .= ">";
  						# process children
  						foreach my $n ($node->getChildNodes) {
  							$string .= parse($xp, $context, $n);
  						}
  						$string .= "</" . $node->getName . ">";
  					}
  					else {
  						$string .= " />";
  					}
  				}
  				elsif ($node->getNodeType == TEXT_NODE) {
  					$string .= $node->getData;
  				}
  			}
  		}
  		else {
  			die "$PREFIX:verbatim match doesn't match a set of nodes";
  		}
  	}
  	
  	return $string;
  }
  
  {
  	
  	# here we add some functions to the XPath function library.
  
  	package XML::XPath::Function;
  	
  	use XML::XPath::Literal;
  
  	# why oh why wasn't set_var implemented in the first place???
  	sub set_var {
  		my $self = shift;
  		my ($node, @params) = @_;
  		if (@params != 2) {
  			die "Usage: set_var('name', value)\n";
  		}
  		$self->{pp}->set_var($params[0], $params[1]);
  	}
  	
  	# and the same goes for sprintf!
  	# (I think the excuse there is "Java doesn't have sprintf". Bah!)
  	sub sprintf {
  		my $self = shift;
  		my ($node, @params) = @_;
  		if (!@params) {
  			die "Usage: sprintf(Literal [, args]*)\n";
  		}
  		
  		my @vals = map("$_", @params);
  		my $format = shift @vals;
  		my $val = CORE::sprintf($format, @vals);
  		return XML::XPath::Literal->new($val);
  	}
  	
  }
  
  
  1;
  __END__
  
  =head1 NAME
  
  Apache::AxKit::Language::NotXSLT - Matt's non-xslt template processor
  
  =head1 SYNOPSIS
  
  	AxAddStyleMap application/x-notxslt => \
  		Apache::AxKit::Language::NotXSLT
  
  =head1 DESCRIPTION
  
  This module implements an XML template system that looks a bit like
  XSLT, but isn't. Hence the name. It uses XML::XPath, and should be
  fast enough to use on small web sites dynamically, rather than using
  static transformation.
  
  =head1 AUTHOR
  
  Matt Sergeant, matt@sergeant.org
  
  =head1 SEE ALSO
  
  XML::XPath(1).
  
  =cut
  
  # $Log: NotXSLT.pm,v $
  # Revision 1.1  2002/01/13 20:45:11  matts
  # Initial file checkin
  #
  # Revision 1.9  2000/06/12 16:14:26  matt
  # Doc fixes
  #
  # Revision 1.8  2000/06/02 17:07:27  matt
  # Fixed to use Providers
  #
  # Revision 1.7  2000/05/31 11:23:33  matt
  # die rather than return DECLINED
  #
  # Revision 1.6  2000/05/19 15:47:13  matt
  # Brown bag release of XSP
  # Removed content_type/encoding from other modules
  #
  # Revision 1.5  2000/05/10 21:21:24  matt
  # Support for cascading via xml_string
  #
  # Revision 1.4  2000/05/08 13:10:31  matt
  # Updated to new XML::XPath 0.50
  #
  # Revision 1.3  2000/05/06 11:11:58  matt
  # Implemented Languages as subclass of Language.pm
  #
  # Revision 1.2  2000/05/02 10:32:05  matt
  # Rename to AxKit
  #
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/Language/PassiveTeX.pm
  
  Index: PassiveTeX.pm
  ===================================================================
  # $Id: PassiveTeX.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
  
  package Apache::AxKit::Language::PassiveTeX;
  
  @ISA = ('Apache::AxKit::Language');
  
  use strict;
  
  use Apache;
  use Apache::Request;
  use Apache::AxKit::Language;
  use Apache::AxKit::Provider;
  use File::Copy ();
  use File::Temp ();
  use File::Path ();
  use File::Basename qw(dirname);
  use Cwd;
  
  my $olddir;
  my $tempdir;
  
  sub stylesheet_exists () { 0; }
  
  sub handler {
      my $class = shift;
      my ($r, $xml_provider, undef, $last_in_chain) = @_;
      
      $tempdir = File::Temp::tempdir();
      if (!$tempdir) {
          die "Cannot create tempdir: $!";
      }
      
      AxKit::Debug(8, "Got tempdir: $tempdir");
      
      $olddir = cwd;
      
      if (my $dom = $r->pnotes('dom_tree')) {
          my $source_text = $dom->toString;
          delete $r->pnotes()->{'dom_tree'};
          my $fh = Apache->gensym();
          chdir($tempdir) || fail("Cannot cd: $!");
          open($fh, ">temp.fo") || fail("Cannot write: $!");
          print $fh $source_text;
          close($fh) || fail("Cannot close: $!");
      }
      elsif (my $source_text = $r->pnotes('xml_string')) {
          # ok...
          my $fh = Apache->gensym();
          chdir($tempdir) || fail("Cannot cd: $!");
          open($fh, ">temp.fo") || fail("Cannot write: $!");
          print $fh $source_text;
          close($fh) || fail("Cannot close: $!");
      }
      else {
          my $text = eval { ${$xml_provider->get_strref()} };
          if ($@) {
              my $fh = $xml_provider->get_fh();
              chdir($tempdir) || fail("Cannot cd: $!");
              File::Copy::copy($fh, "temp.fo");
          }
          else {
              my $fh = Apache->gensym();
              chdir($tempdir) || fail("Cannot cd: $!");
              open($fh, ">temp.fo") || fail("Cannot write: $!");
              print $fh $text;
              close($fh) || fail("Cannot close: $!");
          }
      }
      
      chdir($tempdir) || fail("Cannot cd: $!");
      
      local $ENV{TEXINPUTS} = dirname($r->filename()); 
      AxKit::Debug(8, "About to shell out to pdfxmltex - hope you have passivetex installed...");
      my $retval = system("pdfxmltex temp.fo");
      $retval >>= 8;
      
      if ($retval) {
          fail("pdfxmltex exited with $retval");
      }
      
      my $pdfh = Apache->gensym();
      open($pdfh, "temp.pdf") || fail("Cannot open PDF: $!");
      
      $AxKit::Cfg->AllowOutputCharset(0);
      
      $r->content_type("application/pdf");
      
      local $/;
      
      $r->print(<$pdfh>);
  }
  
  sub cleanup {
      chdir $olddir;
      File::Path::rmtree($tempdir);
  }
  
  sub fail {
      cleanup();
      die @_;
  }
  
  1;
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/Language/Query.pm
  
  Index: Query.pm
  ===================================================================
  package Apache::AxKit::Language::Query;
  
  use strict;
  
  use DBI;
  use XML::DOM;
  
  sub handler {
  	my $r = shift;
  	my ($xmlfile, $stylesheet) = @_;
  	
  	my $vals;
  	open(STYLE, $stylesheet) || die "Can't open stylesheet '$stylesheet': $!";
  	flock(STYLE, 1);
  	while(my $line = <STYLE>) {
  		$line =~ s/\r?\n//; # chomp no good if people editing on Win32
  		$line =~ s/#.*//; # strip comments
  		next if $line =~ /^\s*$/; # ignore blank (or whitespace) lines
  		my ($key, $val) = split(/\s*=\s*/, $line, 2);
  		next unless $key;
  		$vals->{$key} = $val;
  	}
  	
  	my $dsn;
  	if ($vals->{CONNECT_STRING}) {
  		$dsn = $vals->{CONNECT_STRING};
  	}
  	else {
  		$dsn = "dbi:$vals->{DRIVER}:$vals->{CONNECT_EXTRA}";
  	}
  	
  	my %attr;
  	foreach my $key (keys %$vals) {
  		next unless $key =~ /^ATTR_(.*)$/;
  		$attr{$1} = $vals->{$key};
  	}
  
  	my $dbh = DBI->connect($dsn, $vals->{USER}, $vals->{PASSWORD}, \%attr);
  	
  	
  }
  
  1;
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/Language/Sablot.pm
  
  Index: Sablot.pm
  ===================================================================
  # $Id: Sablot.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
  
  package Apache::AxKit::Language::Sablot;
  
  use strict;
  use vars qw/@ISA $xslt_processor $handler/;
  use XML::Sablotron 0.40 ();
  use Apache;
  use Apache::Request;
  use Apache::Log;
  use Apache::AxKit::Language;
  
  @ISA = 'Apache::AxKit::Language';
  
  sub handler {
      my $class = shift;
      my ($r, $xml, $style) = @_;
  
      my ($xmlstring);
      
      if (my $dom = $r->pnotes('dom_tree')) {
          $xmlstring = $dom->toString;
          delete $r->pnotes()->{'dom_tree'};
      }
      else {
          $xmlstring = $r->pnotes('xml_string');
      }
      
      if (!$xmlstring) {
          $xmlstring = eval {${$xml->get_strref()}};
          if ($@) {
              my $fh = $xml->get_fh();
              local $/;
              $xmlstring = <$fh>;
          }
      }
          
      my $stylestring = ${$style->get_strref()};
      
      my $retcode;
  
      # get request form/querystring parameters
      my $cgi = Apache::Request->instance($r);
      my @xslt_params;
      foreach my $param ($cgi->param) {
          push @xslt_params, $param, $cgi->param($param);
      }
      
      # get and register handler object
      $handler->set_apache($r);
      $handler->set_ext_ent_handler($xml->get_ext_ent_handler());
          
      $retcode = $xslt_processor->RunProcessor(
              "arg:/template", "arg:/xml_resource", "arg:/result",
              \@xslt_params,
              ["template" => $stylestring, "xml_resource" => $xmlstring]
              );
      
      $xslt_processor->ClearError();
  
      if ($retcode) {
          throw Apache::AxKit::Exception::Declined(
                  reason => "Sablotron failed to process XML file"
                  );
      }
  
      print $xslt_processor->GetResultArg("result");
      
      $xslt_processor->FreeResultArgs();
  }
  
  END {
      $xslt_processor->UnregHandler(0, $handler);
      $xslt_processor->UnregHandler(1, $handler);
      $xslt_processor->UnregHandler(3, $handler);
  }
  
  package Apache::AxKit::Language::Sablot::Handler;
  
  sub set_apache {
      my $self = shift;
      $self->{apache} = shift;
  }
  
  sub set_ext_ent_handler {
      my $self = shift;
      $self->{ext_ent_handler} = shift;
  }
  
  my @levels = qw(debug info warn error crit);
  
  sub MHMakeCode {
      my $self = shift;
      my $processor = shift;
  
      my ($severity, $facility, $code) = @_;
      return $code;
  }
  
  sub MHLog {
      my $self = shift;
      my $processor = shift;
      
      my $r = $self->{apache};
      
      my ($code, $level, @fields) = @_;
      return 1 unless $r->dir_config('AxSablotLogMessages');
      no strict 'refs';
      my $method = $levels[$level];
      $r->log->$method("[AxKit] [Sablotron] Log: ", join(' :: ', @fields));
      return 1;
  }
  
  sub MHError {
      my $self = shift;
      my $processor = shift;
      
      my $r = $self->{apache};
      
      my ($code, $level, @fields) = @_;
      no strict 'refs';
      my $method = $levels[$level];
      $r->log->$method("[AxKit] [Sablotron] [$code] Error: ", join(' :: ', @fields));
      return 1;
  }
  
  sub SHGetAll {
      my $self = shift;
      my $processor = shift;
      my ($scheme, $rest) = @_;
      
      my $handler = $self->{ext_ent_handler};
      
      my $uri = $scheme . $rest;
      
      $uri =~ s/^axkit\///;
      
      AxKit::Debug(8, "Sablot: Looking up URI: $uri\n");
      
      return $handler->(undef, undef, $uri);
  }
  
  sub XHDocumentInfo {
      my ($self, $processor, $type, $encoding) = @_;
      AxKit::Apache->request->content_type("$type; charset=$encoding");
  }
  
  BEGIN {
      
      sub new {
          my $class = shift;
          return bless {}, $class;
      }
      
      package Apache::AxKit::Language::Sablot;
  
      $xslt_processor = XML::Sablotron->new();
      $xslt_processor->SetBase("axkit:");
  
      $handler = Apache::AxKit::Language::Sablot::Handler->new();
      
      $xslt_processor->RegHandler(0, $handler);
      $xslt_processor->RegHandler(1, $handler);
      $xslt_processor->RegHandler(3, $handler);
  }
  
  1;
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/Language/XMLNewsNITF.pm
  
  Index: XMLNewsNITF.pm
  ===================================================================
  # $Id: XMLNewsNITF.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
  
  package Apache::AxKit::Language::XMLNewsNITF;
  
  use strict;
  use vars qw/@ISA/;
  use Apache::Constants;
  use XMLNews::HTMLTemplate;
  use Apache::AxKit::Language;
  
  @ISA = 'Apache::AxKit::Language';
  
  sub handler {
  	my $class = shift;
  	my ($r, $xml, $style) = @_;
  	
  	my $template_processor = XMLNews::HTMLTemplate->new();
  	
  	$template_processor->readTemplate($style->get_fh());
  	
  	$template_processor->applyTemplate($r, $xml->get_fh(), undef);
  	
  	return OK;
  }
  
  1;
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/Language/XMLNewsRDF.pm
  
  Index: XMLNewsRDF.pm
  ===================================================================
  # $Id: XMLNewsRDF.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
  
  package Apache::AxKit::Language::XMLNewsRDF;
  
  use strict;
  use vars qw/@ISA/;
  use Apache::Constants;
  use XMLNews::HTMLTemplate;
  use Apache::AxKit::Language;
  
  @ISA = 'Apache::AxKit::Language';
  
  sub handler {
  	my $class = shift;
  	my ($r, $xml, $style) = @_;
  	
  	my $template_processor = XMLNews::HTMLTemplate->new();
  	
  	$template_processor->readTemplate($style->get_fh());
  	
  	$template_processor->applyTemplate($r, undef, $xml->get_fh());
  	
  	return OK;
  }
  
  1;
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/Language/XPathScript.pm
  
  Index: XPathScript.pm
  ===================================================================
  # $Id: XPathScript.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
  
  package Apache::AxKit::Language::XPathScript;
  
  use strict;
  use vars qw(@ISA $VERSION $stash );
  
  use Apache;
  use Apache::File;
  use XML::XPath 1.00;
  use XML::XPath::XMLParser;
  use XML::XPath::Node;
  use XML::XPath::NodeSet;
  use XML::Parser;
  use Apache::AxKit::Provider;
  use Apache::AxKit::Language;
  use Apache::AxKit::Cache;
  use Apache::AxKit::Exception;
  use Apache::AxKit::CharsetConv;
  
  @ISA = 'Apache::AxKit::Language';
  
  $VERSION = '0.05';
  
  sub handler {
      my $class = shift;
      my ($r, $xml_provider, $style_provider) = @_;
      
      my $xpath = XML::XPath->new();
      
      my $source_tree;
      
      my $xml_parser = XML::Parser->new(
              ErrorContext => 2,
              Namespaces => $XML::XPath::VERSION < 1.07 ? 1 : 0,
              ParseParamEnt => 1,
              );
      
      my $parser = XML::XPath::XMLParser->new(parser => $xml_parser);
      
      local $Apache::AxKit::Language::XPathScript::local_ent_handler;
      
      if (my $entity_handler = $xml_provider->get_ext_ent_handler()) {
  #        warn "XPathScript: setting entity_handler\n";
          $xml_parser->setHandlers(
                  ExternEnt => $entity_handler,
                  );
          $Apache::AxKit::Language::XPathScript::local_ent_handler = $entity_handler;
      }
      
      AxKit::Debug(6, "XPathScript: Getting XML Source");
      
      if (my $dom = $r->pnotes('dom_tree')) {
          # dom_tree is an XML::LibXML DOM
          $source_tree = $parser->parse($dom->toString);
          delete $r->pnotes()->{'dom_tree'};
      }
      elsif (my $xml = $r->pnotes('xml_string')) {
  #        warn "Parsing from string : $xml\n";
          eval {
              $source_tree = $parser->parse($xml);
          };
          if ($@) {
              throw Apache::AxKit::Exception::Error(-text => "Parse of xml_string failed: $@");
          }
      }
      else {
          $source_tree = get_source_tree($xml_provider, $parser);
      }
      
      $xpath->set_context($source_tree);
      
      my $mtime = $style_provider->mtime();
  
      my $style_key = $style_provider->key();
      my $package = get_package_name($style_key);
      
      AxKit::Debug(6, "Checking stylesheet mtime: $mtime\n");
      if ($stash->{$style_key}
              && exists($stash->{$style_key}{mtime})
              && !$style_provider->has_changed($stash->{$style_key}{mtime})
              && check_inc_mtime($stash->{$style_key}{mtime}, $style_provider, $stash->{$style_key}{includes})) {
          # cached... just exec.
          AxKit::Debug(7, "Using stylesheet cache\n");
      }
      else {
          # recompile stylesheet.
          AxKit::Debug(6, "Recompiling stylesheet: $style_key\n");
          compile($package, $style_provider);
          $stash->{$style_key}{mtime} = get_mtime($class, $style_provider);
      }
      
      my $old_status = $r->status;
      
      no strict 'refs';
      my $cv = \&{"$package\::handler"};
  
      local $Apache::AxKit::Language::XPathScript::xp = $xpath;
      my $t = {};
      local $Apache::AxKit::Language::XPathScript::trans = $t;
      local $Apache::AxKit::Language::XPathScript::style_provider = $style_provider;
      
      AxKit::Debug(7, "Running XPathScript script\n");
      local $^W;
      eval {
          $cv->($r, $xpath, $t);
      };
      if ($@) {
          AxKit::Debug(1, "XPathScript error: $@");
          throw $@;
      }
      
      if (!$r->pnotes('xml_string') && 
          !$r->dir_config('XPSNoApplyTemplatesOnEmptyOutput')) { # no output? Try apply_templates
          print Apache::AxKit::Language::XPathScript::Toys::apply_templates();
      }
      
  #    warn "Run\n";
  
      $Apache::AxKit::Language::XPathScript::xp = undef;
      $Apache::AxKit::Language::XPathScript::trans = undef;
      $Apache::AxKit::Language::XPathScript::style_provider = undef;
  #    warn "Returning $old_status\n";
      return $r->status($old_status);
  }
  
  sub get_source_tree {
      my ($provider, $parser) = @_;
      my $source_tree;
      AxKit::Debug(7, "XPathScript: reparsing file");
      eval {
          my $fh = $provider->get_fh();
          # warn("parsing FH $fh with parser $parser\n");
          local $/;
          my $contents = <$fh>;
          # warn("FH contains: $contents\n");
          $source_tree = $parser->parse($contents);
          # warn("Parse completed\n");
          close($fh);
      };
      if ($@) {
          # warn("parse_fh failed\n");
          $source_tree = $parser->parse(${ $provider->get_strref() });
      }
  
      # warn("get_source_tree = $source_tree\n");
      return $source_tree;
  }
  
  sub check_inc_mtime {
      my ($mtime, $provider, $includes) = @_;
      
      for my $inc (@$includes) {
  #        warn "Checking mtime for $inc\n";
          my $inc_provider = Apache::AxKit::Provider->new(
                  AxKit::Apache->request, 
                  uri => $inc,
                  rel => $provider,
                  );
          if ($inc_provider->has_changed($mtime)) {
  #            warn "$inc newer (" . $inc_provider->mtime() . ") than last compile ($mtime) causing recompile\n";
              return;
          }
      }
      return 1;
  }
  
  sub extract {
      my ($provider,$scalar_output) = @_;
      
      my $contents;
      eval { 
          my $fh = $provider->get_fh();
          local $/;
          $contents = <$fh>;
      };
      if ($@) {
          $contents = ${ $provider->get_strref() };
      }
      
      my $r = AxKit::Apache->request();
      if (my $charset = $r->dir_config('XPathScriptCharset')) {
          
          AxKit::Debug(8, "XPS: got charset: $charset");
          
          my $map = Apache::AxKit::CharsetConv->new($charset, "utf-8") || die "No such charset: $charset";
          $contents = $map->convert($contents);
      }
      
      my $key = $provider->key();
      $stash->{$key}{includes} = [];
      
      AxKit::Debug(10, "XPathScript: extracting from '$key' contents: $contents\n");
      
      my $script;
      
      my $line = 1;
      
      while ($contents =~ /\G(.*?)(<!--\#include|<%=?)/gcs) {
          my ($text, $type) = ($1, $2);
          $line += $text =~ tr/\n//;
          $text =~ s/\|/\\\|/g;
          if($scalar_output) {
              $script .= "\$__OUTPUT.=q|$text|;";
          } else {
              $script .= "print q|$text|;";
          }
          $script .= "\n#line $line $key\n";
          if ($type eq '<%=') {
              $contents =~ /\G(.*?)%>/gcs || die "No terminating '%>' after line $line ($key)";
              my $perl = $1;
              if(!$scalar_output) {
                  $script .= "print(do { $perl });\n";
              } else {
                  $script .= "\$__OUTPUT.=join('',(do { $perl }));\n";
              }
              $line += $perl =~ tr/\n//;
          }
          elsif ($type eq '<!--#include') {
              my %params;
              while ($contents =~ /\G(\s+(\w+)\s*=\s*(["'])([^\3]*?)\3|\s*-->)/gcs) {
                  last if $1 eq '-->';
                  $params{$2} = $4;
              }
              
              if (!$params{file}) {
                  die "No matching file attribute in #include at line $line ($key)";
              }
              
              AxKit::Debug(10, "About to include file $params{file}");
              $script .= include_file($params{file}, $provider, $scalar_output);
              AxKit::Debug(10, "include done");
          }
          else {
              $contents =~ /\G(.*?)%>/gcs || die "No terminating '%>' after line $line ($key)";
              my $perl = $1;
              $perl =~ s/;?$/;/s; # add on ; if its missing. As in <% $foo = 'Hello' %>
              $script .= $perl;
              $line += $perl =~ tr/\n//;
          }
      }
      
      if ($contents =~ /\G(.*)/gcs) {
          my ($text) = ($1);
          $text =~ s/\|/\\\|/g;
          if ($scalar_output) {
              $script .= "\$__OUTPUT.=q|$text|;";
          } else {
              $script .= "print q|$text|;";
          }
      }
      
      return $script;
  }
  
  sub compile {
      my ($package, $provider) = @_;
      
      my $script = extract($provider);
      
      my $eval = join('',
              'package ',
              $package,
              '; use Apache qw(exit);',
              'use XML::XPath::Node;',
              'Apache::AxKit::Language::XPathScript::Toys->import;',
              'sub handler {',
              'my ($r, $xp, $t) = @_;',
              "\n#line 1 " . $provider->key() . "\n",
              $script,
              "\n}",
              );
      
      local $^W;
      
      AxKit::Debug(10, "Compiling script:\n$eval\n");
      eval $eval;
      if ($@) {
          AxKit::Debug(1, "Compilation failed: $@");
          throw $@;
      }
  }
  
  sub include_file {
      my ($filename, $provider, $script_output) = @_;
  
      # return if already included
      my $key = $provider->key();
      return '' if grep {$_ eq $filename} @{$stash->{$key}{includes}};
      
      push @{$stash->{$key}{includes}}, $filename;
      
      my $inc_provider = Apache::AxKit::Provider->new(
              AxKit::Apache->request,
              uri => $filename,
              rel => $provider,
              );
      
      return extract($inc_provider, $script_output);
  }
  
  sub XML::XPath::Function::document {
      # warn "Document function called\n";
      return unless $Apache::AxKit::Language::XPathScript::local_ent_handler;
      my $self = shift;
      my ($node, @params) = @_;
      die "document: Function takes 1 parameter\n" unless @params == 1;
  
      my $xml_parser = XML::Parser->new(
              ErrorContext => 2,
              Namespaces => $XML::XPath::VERSION < 1.07 ? 1 : 0,
              # ParseParamEnt => 1,
              );
  
      my $parser = XML::XPath::XMLParser->new(parser => $xml_parser);
  
      my $results = XML::XPath::NodeSet->new();
      my $uri = $params[0];
      my $newdoc;
      if ($uri =~ /^\w\w+:/) { # assume it's scheme://foo uri
          eval {
              # warn "Trying to parse $params[0]\n";
              $newdoc = $parser->parse(
                      $Apache::AxKit::Language::XPathScript::local_ent_handler->(
                          undef, undef, $uri
                      )
                  );
              # warn "Parsed OK into $newdoc\n";
          };
          if (my $E = $@) {
              if ($E->isa('Apache::AxKit::Exception::IO')) {
                  AxKit::Debug(2, $E);
              }
              else {
                  throw Apache::AxKit::Exception::Error(-text => "Parse of '$uri' failed: $E");
              };
          }
      }
      else {
          # warn("Parsing local: $uri\n");
          my $provider = Apache::AxKit::Provider->new(
                      AxKit::Apache->request, 
                      uri => $uri,
                  );
          $newdoc = get_source_tree($provider, $parser);
      }
  
      $results->push($newdoc) if $newdoc;
      return $results;
  }
  
  sub get_mtime {
      my $class = shift;
      my ($provider) = @_;
  #    warn "get_mtime\n";
      my $mtime = $provider->mtime();
      my $filename = $provider->key();
  #    warn "mtime: $filename = $mtime\n";
      if (!$stash->{$filename}) {
          # compile stylesheet
          compile(get_package_name($filename), $provider);
      
          $stash->{$filename}{mtime} = $mtime;
          return 0;
      }
  
      for my $inc (@{$stash->{$filename}{includes}}) {
          my $inc_provider = Apache::AxKit::Provider->new(
                  AxKit::Apache->request, 
                  uri => $inc,
                  rel => $provider,
                  );
          
  #        warn "Checking mtime of $inc\n";
          if ($inc_provider->has_changed($mtime)) {
              $mtime = $inc_provider->mtime();
          }
      }
      
  #    warn "returning $mtime\n";
      return $mtime;
  }
  
  sub get_package_name {
      my $filename = shift;
      # Escape everything into valid perl identifiers
      $filename =~ s/([^A-Za-z0-9_\/])/sprintf("_%2x",unpack("C",$1))/eg;
  
      # second pass cares for slashes and words starting with a digit
      $filename =~ s{
                    (/+)       # directory
                    (\d?)      # package's first character
                   }[
                     "::" . (length $2 ? sprintf("_%2x",unpack("C",$2)) : "")
                    ]egx;
  
      return "Apache::AxKit::Language::XPathScript::ROOT$filename";
  }
  
  {
      package Apache::AxKit::Language::XPathScript::Toys;
      
      use XML::XPath::Node;
      use Apache::AxKit::Exception;
  
      use vars '@ISA', '@EXPORT';
      use Exporter;
      @ISA = ('Exporter');
      @EXPORT = qw(
              findnodes 
              findvalue
              findvalues
              findnodes_as_string
              apply_templates
              matches
              set_namespace
              import_template
              DO_SELF_AND_KIDS
              DO_SELF_ONLY
              DO_NOT_PROCESS
              );
      
      sub DO_SELF_AND_KIDS () { return 1; }
      sub DO_SELF_ONLY () { return -1; }
      sub DO_NOT_PROCESS () { return 0; }
      sub MAX_DEPTH () { return 32; }
      
      sub import_template {
          my ($filename, $local_changes) = @_;
          my ($script) = Apache::AxKit::Language::XPathScript::include_file($filename,$Apache::AxKit::Language::XPathScript::style_provider, 1);
          # changes may be local to this imported template, or global (default).
          my ($setup_t);
          if ($local_changes) {
              $setup_t = 'local $Apache::AxKit::Language::XPathScript::trans = clone($Apache::AxKit::Language::XPathScript::trans);';
          }
          
          $script = join('',
                       'use strict;',
                       'sub { ',
                       'my ($node, $real_local_t) = @_;',
                       'local $Apache::AxKit::Language::XPathScript::xp = $node;',
                       $setup_t,
                       'my ($t) = $Apache::AxKit::Language::XPathScript::trans;',
                       'my ($__OUTPUT);',
                       $script,';',
                       '$real_local_t->{pre} = $__OUTPUT;',
                       'return -1;',
                       '}');
          return eval($script);
      }
  
      sub findnodes {
          $Apache::AxKit::Language::XPathScript::xp->findnodes(@_);
      }
  
      sub findvalue {
          $Apache::AxKit::Language::XPathScript::xp->findvalue(@_);
      }
      
      sub findvalues {
          my @nodes = findnodes(@_);
          map { findvalue('.', $_) } @nodes;
      }
  
      sub findnodes_as_string {
          $Apache::AxKit::Language::XPathScript::xp->findnodes_as_string(@_);
      }
      
      sub matches {
          $Apache::AxKit::Language::XPathScript::xp->matches(@_);
      }
      
      sub set_namespace {
          eval {
              $Apache::AxKit::Language::XPathScript::xp->set_namespace(@_);
          };
          if ($@) {
              AxKit::Debug(3, "set_namespace failed: $@");
          }
      }
      
      # quieten warnings when compiling this module
      sub apply_templates (;$@);
      
      sub apply_templates (;$@) {
          unless (@_) {
              return apply_templates(findnodes('/'));
          }
          
          my ($arg1, @args) = @_;
  
          if (!ref($arg1)) {
              # called with a path to find
  #            warn "apply_templates with path '$arg1'\n";
              $arg1 = findnodes($arg1, @args);
  #            return apply_templates($nodes);
          }
          
          my $retval = '';
          if (ref($arg1) eq "HASH") {
  #            warn "apply_templates with a hash\n";
              local $Apache::AxKit::Language::XPathScript::trans = $arg1;
              return apply_templates(@args);
          } 
          elsif ($arg1->isa('XML::XPath::NodeSet')) {
  #            warn "apply_templates with a NodeSet\n";
              foreach my $node ($arg1->get_nodelist) {
                  $retval .= translate_node($node);
              }
          }
          else {
  #            warn "apply_templates with a list of " , 1 + @args, " nodes? : ", ref($arg1), "\n";
              $retval .= translate_node($arg1);
              foreach my $node (@args) {
                  $retval .= translate_node($node);
              }
          }
          
          return $retval;
      }
      
      sub _apply_templates {
          my @nodes = @_;
          
          my $retval = '';
          foreach my $node (@nodes) {
              $retval .= translate_node($node);
          }
          
          return $retval;
      }
      
      sub translate_node {
          my $node = shift;
          
          local $^W;
                  
          my $translations = $Apache::AxKit::Language::XPathScript::trans;
          
          if ($node->isTextNode) {
              my $trans = $translations->{'text()'};
              if (!$trans) { return $node->toString; }
              if (my $code = $trans->{testcode}) {
                  my $t = {};
                  my $retval = $code->($node, $t);
                  if ($retval && %$t) {
                      foreach my $tkey (keys %$t) {
                          $trans->{$tkey} = $t->{$tkey};
                      }
                  }
              }
              return $trans->{pre} . $node->toString . $trans->{post};
          }
  
          if (!$node->isElementNode) {
              # don't output top-level PI's
              if ($node->isPINode) {
                  my $retstring = eval {
                      if ($node->getParentNode->getParentNode) {
                          return $node->toString;
                      }
                      return '';
                  };
                  
                  return $retstring || '';
              }
              return $node->toString;
          }
          
  #        warn "translate_node: ", $node->getName, "\n";
          
          my $node_name = $node->getName;
  
          my $trans = $translations->{$node_name};
  
          if (!$trans) {
              $node_name = '*';
              $trans = $translations->{$node_name};
          }
          
          if (!$trans) {
  #            warn "Default trans\n";
              if (my @children = $node->getChildNodes) {
                  return start_tag($node) . 
                      _apply_templates(@children) .
                      end_tag($node);
              }
              else {
                  return empty_tag($node);
              }
          }
          
          local $^W;
          
          my $dokids = 1;
          my $search;
         my $testcode_output = 0;
          my $t = {};
          if ($trans->{testcode}) {
  #            warn "eval testcode\n";
             $testcode_output = 1;
              my $result;
              my $testcode = $trans->{testcode};
              my $depth = 0;
              while (1) {
                  $result = $testcode->($node, $t);
  #                warn "Testcode returned: $result\n";
                  if (defined($t->{testcode}) &&
                        ref($t->{testcode}) eq "CODE") {
                      if ($depth++ > MAX_DEPTH) {
                          die "Max Depth of ", MAX_DEPTH, " reached on testcode eval!";
                      }
                      $testcode = $t->{testcode};
                      $t = {};
                  } else {
                      last;
                  }
              }
              
  #            warn "Here with $result\n";
              
  
              if ($result =~ /\D/) {
                  $dokids = 0;
                  $search = $result;
              }
              elsif ($result == DO_NOT_PROCESS) {
                  # don't process anything.
                  return;
              }
              elsif ($result == DO_SELF_ONLY) {
                  # -1 means don't do children.
                  $dokids = 0;
              }
              elsif ($result == DO_SELF_AND_KIDS) {
                  # do kids
              }
  #            warn "Here with dokids => $dokids, search => $search\n";
          }
          
          local $translations->{$node_name};
          # copy old values in
          %{$translations->{$node_name}} = %$trans;
          
          if (%$t) {
              foreach my $key (keys %$t) {
                  $translations->{$node_name}{$key} = $t->{$key};
              }
              $trans = $translations->{$node_name};
          }
          
          # default: process children too.
          my $pre = interpolate($node, $trans->{pre}, $testcode_output) . 
                  ($trans->{showtag} ? start_tag($node) : '') .
                  interpolate($node, $trans->{prechildren}, $testcode_output);
          
          my $post = interpolate($node, $trans->{postchildren}, $testcode_output) .
                  ($trans->{showtag} ? end_tag($node) : '') .
                  interpolate($node, $trans->{post}, $testcode_output);
          
          if ($dokids) {
              my $middle = '';
              for my $kid ($node->getChildNodes()) {
                  if ($kid->isElementNode) {
                      $middle .= interpolate($node, $trans->{prechild}) .
                              _apply_templates($kid) .
                              interpolate($node, $trans->{postchild});
                  }
                  else {
                      $middle .= _apply_templates($kid);
                  }
              }
              return $pre . $middle . $post;
          }
          elsif ($search) {
              my $middle = '';
              for my $kid (findnodes($search, $node)) {
                  if ($kid->isElementNode) {
                      $middle .= interpolate($node, $trans->{prechild}) .
                              _apply_templates($kid) .
                              interpolate($node, $trans->{postchild});
                  }
                  else {
                      $middle .= _apply_templates($kid);
                  }
              }
              return $pre . $middle . $post;
          }
          else {
              return $pre . $post;
          }
      }
      
      sub start_tag {
          my ($node) = @_;
          
          my $name = $node->getName;
          return '' unless $name;
          
          my $string = "<" . $name;
          
          foreach my $ns ($node->getNamespaceNodes) {
              $string .= $ns->toString;
          }
          
          foreach my $attr ($node->getAttributeNodes) {
              $string .= $attr->toString;
          }
  
          $string .= ">";
          
          return $string;
      }
      
      sub end_tag {
          my ($node) = @_;
          
          if (my $name = $node->getName) {
              return "</" . $name . ">";
          }
          else {
              return '';
          }
      }
      
      sub empty_tag {
          my ($node) = @_;
          
          my $name = $node->getName;
          return '' unless $name;
          
          my $string = "<" . $name;
          
          foreach my $ns ($node->getNamespaceNodes) {
              $string .= $ns->toString;
          }
          
          foreach my $attr ($node->getAttributeNodes) {
              $string .= $attr->toString;
          }
  
          $string .= " />";
          
          return $string;
      }        
      
      sub interpolate {
          my ($node, $string, $ignore) = @_;
          return $string if $XPathScript::DoNotInterpolate || $ignore;
          return $string unless AxKit::Apache->request->dir_config('AxXPSInterpolate');
          my $new = '';
          while ($string =~ m/\G(.*?)\{(.+?)\}/gcs) {
              my ($pre, $path) = ($1, $2);
              $new .= $pre;
              $new .= $node->findvalue($path);
          }
          $string =~ /\G(.*)/gcs;
          $new .= $1 if defined $1;
          return $new;
      }
  
      # make a clone, but copy subs.
      sub clone {
          my ($a) = @_;
          my ($b);
          if (ref($a) eq "HASH") {
              $b = {};
              foreach my $key (keys(%$a)) {        
                  my ($copy) = clone($a->{$key});
                  $b->{$key} = $copy;
              }
          }
          else {
              # copy as is
              $b = $a;
          }
          return $b;
      }
  
      1;
  }
  
  1;
  __END__
  
  =head1 NAME
  
  Apache::AxKit::Language::XPathScript - An XML Stylesheet Language
  
  =head1 SYNOPSIS
  
    AxAddStyleMap "application/x-xpathscript => \
          Apache::AxKit::Language::XPathScript"
  
  =head1 DESCRIPTION
  
  This documentation has been removed. The definitive reference for 
  XPathScript is now at http://axkit.org/docs/xpathscript/guide.dkb
  in DocBook format.
  
  =cut
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/Language/XSLT.pm
  
  Index: XSLT.pm
  ===================================================================
  # $Id: XSLT.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
  
  package Apache::AxKit::Language::XSLT;
  
  use strict;
  use XML::XSLT;
  use Apache::AxKit::Language;
  
  use vars qw/@ISA/;
  
  @ISA = 'Apache::AxKit::Language';
  
  sub handler {
      my $class = shift;
      my ($r, $xml, $style) = @_;
  
  #    warn "Parsing stylefile '$stylefile'\n";
      my $parser = XML::XSLT->new($style->get_strref(), "STRING");
  
      if (my $dom_tree = $r->pnotes('dom_tree')) {
  #        warn "Parsing dom_tree: ", $dom_tree->toString, "\n";
          my $xml_string = $dom_tree->toString;
          delete $r->pnotes()->{'dom_tree'};
          $parser->transform_document($xml_string, "STRING");
      }
      elsif (my $xmlstr = $r->pnotes('xml_string')) {
  #        warn "Parsing string:\n$xml\n";
          $parser->transform_document($xmlstr, "STRING");
      }
      else {
  #        warn "Parsing file '$xmlfile'\n";
          $parser->transform_document($xml->get_strref(), "STRING");
      }
  
      $r->pnotes('xml_string', $parser->result_tree()->toString);
      
      $parser->dispose();
  }
  
  1;
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/Language/XSP.pm
  
  Index: XSP.pm
  ===================================================================
  # $Id: XSP.pm,v 1.1 2002/01/13 20:45:11 matts Exp $
  
  package Apache::AxKit::Language::XSP;
  
  use strict;
  use Apache::AxKit::Language;
  use Apache::Request;
  use Apache::AxKit::Exception;
  use Apache::AxKit::Cache;
  
  use vars qw/@ISA/;
  
  @ISA = ('Apache::AxKit::Language');
  
  sub stylesheet_exists () { 0; }
  
  sub get_mtime {
      return 30; # 30 days in the cache?
  }
  
  my $cache;
  
  # useful for debugging - not actually used by AxKit:
  # sub get_code {
  #     my $filename = shift;
  #  
  # # cannot register - no $AxKit::Cfg...
  # #    _register_me_and_others();
  #     __PACKAGE__->register();
  #     
  #     my $package = get_package_name($filename);
  #     my $parser = get_parser($package, $filename);
  #     return $parser->parsefile($filename);
  # }
  
  sub handler {
      my $class = shift;
      my ($r, $xml, undef, $last_in_chain) = @_;
      
      _register_me_and_others();
      
  #    warn "XSP Parse: $xmlfile\n";
      
      my $key = $xml->key();
      
      my $package = get_package_name($key);
      
      my $handler = AxKit::XSP::SAXHandler->new_handler(
              XSP_Package => $package,
              XSP_Line => $key,
              );
      my $parser = AxKit::XSP::SAXParser->new(
              provider => $xml,
              Handler => $handler,
              );
      
      local $Apache::AxKit::Language::XSP::ResNamespaces = $r->dir_config('XSPResNamespaces');
      
      my $to_eval;
      
      eval {
          if (my $dom_tree = $r->pnotes('dom_tree')) {
              AxKit::Debug(5, 'XSP: parsing dom_tree');
              $to_eval = $parser->parse($dom_tree->toString);
              delete $r->pnotes()->{'dom_tree'};
          }
          elsif (my $xmlstr = $r->pnotes('xml_string')) {
              if ($r->no_cache()
                      || !defined &{"${package}::handler"}) {
                  AxKit::Debug(5, 'XSP: parsing xml_string');
                  $to_eval = $parser->parse($xmlstr);
              }
              else {
                  AxKit::Debug(5, 'XSP: not reparsing xml_string (cached)');
              }
          }
          else {
              # check mtime.
              my $mtime = $xml->mtime();
              no strict 'refs';
              if (exists($cache->{$key})
                      && !$xml->has_changed($cache->{$key}{mtime})
                      && defined &{"${package}::handler"}
                      )
              {
                  # cached
                  AxKit::Debug(5, 'XSP: xsp script cached');
              }
              else {
                  AxKit::Debug(5, 'XSP: parsing fh');
                  $to_eval = eval {
                      $parser->parse($xml->get_fh());
                  } || $parser->parse(${ $xml->get_strref() });
                  
                  $cache->{$key}{mtime} = $mtime;
              }
          }
      };
      if ($@) {
          throw Apache::AxKit::Exception::Error(
                  -text => "Parse of '$key' failed: $@"
                  );
      }
      
      if ($to_eval) {
          undef &{"${package}::handler"};
          AxKit::Debug(5, 'Recompiling XSP script');
          AxKit::Debug(10, $to_eval);
          eval $to_eval;
          AxKit::Debug(5, 'XSP Compilation finished');
          if ($@) {
              my $line = 1;
              $to_eval =~ s/\n/"\n".++$line." "/eg;
              warn("Script:\n1 $to_eval\n");
              die "Failed to parse: $@";
          }
      }
      
      no strict 'refs';
      my $cv = \&{"$package\::handler"};
      
      my $cgi = Apache::Request->instance($r);
      
      $r->no_cache(1);
      
      my $xsp_cache = Apache::AxKit::Cache->new($r, $package, $package->cache_params($r, $cgi));
      
      if (!$package->has_changed($xsp_cache->mtime()) && 
                  !$xml->has_changed($xsp_cache->mtime())) {
          AxKit::Debug(3, "XSP results cached");
          $r->print($xsp_cache->read);
          return;
      }
      
      my $dom = $cv->($r, $cgi);
      $r->pnotes('dom_tree', $dom);
      $r->print($dom->toString) if $last_in_chain;
  
      $xsp_cache->write( $dom->toString );
  }
  
  sub register {
      my $class = shift;
      no strict 'refs';
      $class->register_taglib(${"${class}::NS"});
  }
  
  sub _register_me_and_others {
  #    warn "Loading taglibs\n";
      foreach my $package ($AxKit::Cfg->XSPTaglibs()) {
  #        warn "Registering taglib: $package\n";
          AxKit::load_module($package);
          $package->register();
      }
  }
  
  sub register_taglib {
      my $class = shift;
      my $namespace = shift;
      
  #    warn "Register taglib: $namespace => $class\n";
      
      $Apache::AxKit::Language::XSP::tag_lib{$namespace} = $class;
  }
  
  sub is_xsp_namespace {
      my ($ns) = @_;
      
      # a uri of the form "res:perl/<spec>" turns into an implicit loading of
      # the module indicated by <spec> (after slashes are turned into
      # double-colons). an example uri is "res:perl/My/Cool/Module".
      if ($Apache::AxKit::Language::XSP::ResNamespaces && $ns =~ m/^res:perl\/(.*)$/) {
         my $package = $1;
         $package =~ s/\//::/g;
         AxKit::load_module($package);
         $package->register();
      }
      
      return 1 if $Apache::AxKit::Language::XSP::tag_lib{$ns};
  }
  
  sub get_package_name {
      my $filename = shift;
      # Escape everything into valid perl identifiers
      $filename =~ s/([^A-Za-z0-9_\/])/sprintf("_%2x",unpack("C",$1))/eg;
  
      # second pass cares for slashes and words starting with a digit
      $filename =~ s{
                    (/+)       # directory
                    (\d?)      # package's first character
                   }[
                     "::" . (length $2 ? sprintf("_%2x",unpack("C",$2)) : "")
                    ]egx;
  
      return "Apache::AxKit::Language::XSP::ROOT$filename";
  }
  
  ############################################################
  # SAX Handler code
  ############################################################
  
  package AxKit::XSP::SAXHandler;
  
  sub new_handler {
      my ($type, %self) = @_; 
      return bless \%self, $type;
  }
  
  sub start_expr {
      my ($e) = @_;
      my $element = { Name => "expr",
                      NamespaceURI => $AxKit::XSP::Core::NS,
                      Attributes => [ ],
                      Parent => $e->{Current_Element}->{Parent},
  #                    OldParent => $e->{Current_Element},
              };
  #    warn "start_expr: $e->{Current_Element}->{Name}\n";
      $e->start_element($element);
  }
  
  sub end_expr {
      my ($e) = @_;
      my $parent = $e->{Current_Element}->{Parent};
      my $element = { Name => "expr",
                      NamespaceURI => $AxKit::XSP::Core::NS,
                      Attributes => [ ],
                      Parent => $parent,
              };
  #    warn "end_expr: $parent->{Name}\n";
      $e->end_element($element);
  }
  
  sub append_to_script {
      my ($e, $code) = @_;
      $e->{XSP_Script} .= $code;
  }
  
  sub manage_text {
      my ($e, $set, $go_back) = @_;
      
      $go_back ||= 0;
  
      my $depth = $e->depth();
      if (defined($set) && $set >= 0) {
          $e->{XSP_Manage_Text}[$depth - $go_back] = $set;
      }
      else {
          if (defined($set) && $set == -1) {
              # called from characters handler, rather than expr
              return $e->{XSP_Manage_Text}[$depth];
          }
          return $e->{XSP_Manage_Text}[$depth - 1];
      }
  }
  
  sub depth {
      my ($e) = @_;
      my $element = $e->{Current_Element};
      
      my $depth = 0;
      while ($element = $element->{Parent}) {
          $depth++;
      }
      
      return $depth;
  }
  
  sub current_element {
      my $e = shift;
      my $tag = $e->{Current_Element}{Name};
      $tag =~ s/^(.*:)//;
      return $tag;
  }
  
  sub start_document {
      my $e = shift;
      
      $e->{XSP_Script} = join("\n", 
                  "package $e->{XSP_Package}; \@$e->{XSP_Package}::ISA = ('Apache::AxKit::Language::XSP::Page');",
                  "#line 2 ".$e->{XSP_Line}."\n",
                  "use Apache;",
                  "use XML::LibXML;",
                  );
      
      foreach my $ns (keys %Apache::AxKit::Language::XSP::tag_lib) {
          my $pkg = $Apache::AxKit::Language::XSP::tag_lib{$ns};
          my $sub;
          if (($sub = $pkg->can("start_document")) && ($sub != \&start_document)) {
              $e->{XSP_Script} .= $sub->($e);
          }
          elsif ($sub = $pkg->can("parse_init")) {
              $e->{XSP_Script} .= $sub->($e);
          }
      }
  }
  
  sub end_document {
      my $e = shift;
      
      foreach my $ns (keys %Apache::AxKit::Language::XSP::tag_lib) {
          my $pkg = $Apache::AxKit::Language::XSP::tag_lib{$ns};
          my $sub;
          if (($sub = $pkg->can("end_document")) && ($sub != \&end_document)) {
              $e->{XSP_Script} .= $sub->($e);
          }
          elsif ($sub = $pkg->can("parse_final")) {
              $e->{XSP_Script} .= $sub->($e);
          }
      }
  
      $e->{XSP_Script} .= "return \$document\n}\n";
      
      return $e->{XSP_Script};
  }
  
  sub start_element {
      my $e = shift;
      my $element = shift;
      
      $element->{Parent} ||= $e->{Current_Element};
      
      $e->{Current_Element} = $element;
  
      my $ns = $element->{NamespaceURI};
      
  #    warn "START-NS: $ns : $element->{Name}\n";
      
      my @attribs;
      
      for my $attr (@{$element->{Attributes}}) {
          if ($attr->{Name} eq 'xmlns') {
              unless (Apache::AxKit::Language::XSP::is_xsp_namespace($attr->{Value})) {
                  $e->{Current_NS}{'#default'} = $attr->{Value};
              }
          }
          elsif ($attr->{Name} =~ /^xmlns:(.*)$/) {
              my $prefix = $1;
              unless (Apache::AxKit::Language::XSP::is_xsp_namespace($attr->{Value})) {
                  $e->{Current_NS}{$prefix} = $attr->{Value};
              }
          }
          else {
              push @attribs, $attr;
          }
      }
      
      $element->{Attributes} = \@attribs;
      
      if (!defined($ns) || 
          !exists($Apache::AxKit::Language::XSP::tag_lib{ $ns })) 
      {
          $e->manage_text(0); # set default for non-xsp tags
          $e->{XSP_Script} .= AxKit::XSP::DefaultHandler::start_element($e, $element);
      }
      else {
  #        local $^W;
          $element->{Name} =~ s/^(.*)://;
          my $prefix = $1;
          my $tag = $element->{Name};
          my %attribs;
          # this is probably a bad hack to turn xsp:name="value" into name="value"
          for my $attr (@{$element->{Attributes}}) {
              $attr->{Name} =~ s/^\Q$prefix\E://;
              $attribs{$attr->{Name}} = $attr->{Value};
          }
          $e->manage_text(1); # set default for xsp tags
          my $pkg = $Apache::AxKit::Language::XSP::tag_lib{ $ns };
          my $sub;
          if (($sub = $pkg->can("start_element")) && ($sub != \&start_element)) {
              $e->{XSP_Script} .= $sub->($e, $element);
          }
          elsif ($sub = $pkg->can("parse_start")) {
              $e->{XSP_Script} .= $sub->($e, $tag, %attribs);
          }
      }
  }
  
  sub end_element {
      my $e = shift;
      my $element = shift;
  
      my $ns = $element->{NamespaceURI};
      
  #    warn "END-NS: $ns : $_[0]\n";
      
      if (!defined($ns) || 
          !exists($Apache::AxKit::Language::XSP::tag_lib{ $ns })) 
      {
          $e->{XSP_Script} .= AxKit::XSP::DefaultHandler::end_element($e, $element);
      }
      else {
  #        local $^W;
          $element->{Name} =~ s/^(.*)://;
          my $tag = $element->{Name};
          my $pkg = $Apache::AxKit::Language::XSP::tag_lib{ $ns };
          my $sub;
          if (($sub = $pkg->can("end_element")) && ($sub != \&end_element)) {
              $e->{XSP_Script} .= $sub->($e, $element);
          }
          elsif ($sub = $pkg->can("parse_end")) {
              $e->{XSP_Script} .= $sub->($e, $tag);
          }
      }
      
      $e->{Current_Element} = $element->{Parent} || $e->{Current_Element}->{Parent};
  }
  
  sub characters {
      my $e = shift;
      my $text = shift;
      my $ns = $e->{Current_Element}->{NamespaceURI};
      
  #    warn "CHAR-NS: $ns\n";
      
      if (!defined($ns) || 
          !exists($Apache::AxKit::Language::XSP::tag_lib{ $ns }) ||
          !$e->manage_text(-1))
      {
          $e->{XSP_Script} .= AxKit::XSP::DefaultHandler::characters($e, $text);
      }
      else {
          my $pkg = $Apache::AxKit::Language::XSP::tag_lib{ $ns };
          my $sub;
          if (($sub = $pkg->can("characters")) && ($sub != \&characters)) {
              $e->{XSP_Script} .= $sub->($e, $text);
          }
          elsif ($sub = $pkg->can("parse_char")) {
              $e->{XSP_Script} .= $sub->($e, $text->{Data});
          }
      }
  }
  
  sub comment {
      my $e = shift;
      my $comment = shift;
  
      my $ns = $e->{Current_Element}->{NamespaceURI};
                  
      if (!defined($ns) || 
          !exists($Apache::AxKit::Language::XSP::tag_lib{ $ns })) 
      {
          $e->{XSP_Script} .= AxKit::XSP::DefaultHandler::comment($e, $comment);
      }
      else {
  #        local $^W;
          my $pkg = $Apache::AxKit::Language::XSP::tag_lib{ $ns };
          my $sub;
          if (($sub = $pkg->can("comment")) && ($sub != \&comment)) {
              $e->{XSP_Script} .= $sub->($e, $comment);
          }
          elsif ($sub = $pkg->can("parse_comment")) {
              $e->{XSP_Script} .= $sub->($e, $comment->{Data});
          }
      }
  }
  
  sub processing_instruction {
      my $e = shift;
      my $pi = shift;
  
      my $ns = $e->{Current_Element}->{NamespaceURI};
      
      if (!defined($ns) || 
          !exists($Apache::AxKit::Language::XSP::tag_lib{ $ns })) 
      {
          $e->{XSP_Script} .= AxKit::XSP::DefaultHandler::processing_instruction($e, $pi);
      }
      else {
  #        local $^W;
          my $pkg = $Apache::AxKit::Language::XSP::tag_lib{ $ns };
          my $sub;
          if (($sub = $pkg->can("processing_instruction")) && ($sub != \&processing_instruction)) {
              $e->{XSP_Script} .= $sub->($e, $pi);
          }
          elsif ($sub = $pkg->can("parse_pi")) {
              $e->{XSP_Script} .= $sub->($e, $pi->{Target}, $pi->{Data});
          }
      }
  }
  
  ############################################################
  # Functions implementing xsp:* processing
  ############################################################
  
  package AxKit::XSP::Core;
  
  use vars qw/@ISA $NS/;
  
  @ISA = ('Apache::AxKit::Language::XSP');
  
  $NS = 'http://apache.org/xsp/core/v1';
  
  __PACKAGE__->register();
  
  # hack for backwards compatibility:
  __PACKAGE__->register_taglib("http://www.apache.org/1999/XSP/Core");
  
  
  sub start_document {
      return "#initialize xsp namespace\n";
  }
  
  sub end_document {
      return '';
  }
  
  sub comment {
      return '';
  }
  
  sub processing_instruction {
      return '';
  }
  
  sub characters {
      my ($e, $node) = @_;
  
      local $^W;
      
      my $text = $node->{Data};
      
  #     Ricardo writes: "<xsp:expr> produces either an [object]
  # _expression_ (not necessarily a String) or a character event depending
  # on context. When  <xsp:expr> is enclosed in another XSP tag (except
  # <xsp:content>), it's replaced by the code it contains. Otherwise it
  # should be treated as a text node and, therefore, coerced to String to be
  # output through a characters SAX event."
  
      if ($e->current_element() =~ /^(content)$/) {
          $text =~ s/\|/\\\|/g;
  
          return <<"EOT";
  {
      my \$text = \$document->createTextNode(q|$text|);
      \$parent->appendChild(\$text); 
  }
  EOT
      }
      elsif ($e->current_element() =~ /^(attribute|comment|name)$/) {
          return '' if ($e->current_element() eq 'attribute' && !$e->{attrib_seen_name});
          $text =~ s/^\s*//; $text =~ s/\s*$//;
          $text =~ s/\|/\\\|/g;    
          return ". q|$text|";
      }
      
  #    return '' unless $e->{XSP_User_Root};
      
      return $text;
  }
  
  sub start_element {
      my ($e, $node) = @_;
      
      my ($tag, %attribs);
      
      $tag = $node->{Name};
      
      foreach my $attrib (@{$node->{Attributes}}) {
          $attribs{$attrib->{Name}} = $attrib->{Value};
      }
      
      if ($tag eq 'page') {
          if ($attribs{language} && lc($attribs{language}) ne 'perl') {
              die "Only Perl XSP pages supported at this time!";
          }
          local $^W;
          if ($attribs{'indent-result'} eq 'yes') {
              $e->{XSP_Indent} = 1;
          }
      }
      elsif ($tag eq 'structure') {
      }
      elsif ($tag eq 'dtd') {
      }
      elsif ($tag eq 'include') {
          return "warn \"xsp:include is deprecated\"; use ";
      }
      elsif ($tag eq 'content') {
      }
      elsif ($tag eq 'logic') {
      }
      elsif ($tag eq 'import') {
          return "use ";
      }
      elsif ($tag eq 'element') {
          if (my $name = $attribs{name}) {
              $e->manage_text(0);
              return '{ my $elem = $document->createElement(q(' . $name . '));' .
                      '$parent->appendChild($elem); $parent = $elem; }' . "\n";
          }
      }
      elsif ($tag eq 'attribute') {
          if (my $name = $attribs{name}) {
              $e->{attrib_seen_name} = 1;
              return '$parent->setAttribute(q|' . $name . '|, ""';
          }
          $e->{attrib_seen_name} = 0;
      }
      elsif ($tag eq 'name') {
          return '{ my $name = ""';
      }
      elsif ($tag eq 'pi') {
      }
      elsif ($tag eq 'comment') {
          return '{ my $comment = $document->createComment(""';
      }
      elsif ($tag eq 'text') {
          return '{ my $text = $document->createTextNode(""';
      }
      elsif ($tag eq 'expr') {
  #        warn "expr: -2 = {", $node->{Parent}->{NamespaceURI}, "}", $node->{Parent}->{Name}, "\n";
          if (Apache::AxKit::Language::XSP::is_xsp_namespace($node->{Parent}->{NamespaceURI})) {
              if (!$e->manage_text() || $node->{Parent}->{Name} =~ /^(.*:)?content$/) {
                  return <<'EOT';
  {
      my $text = $document->createTextNode("".do {
  EOT
              }
              elsif ($node->{Parent}->{Name} =~ /^(.*:)?(logic|expr)$/) {
                  return 'do {';
              }
          }
          else {
              return <<'EOT';
  {
      my $text = $document->createTextNode("".do {
  EOT
          }
          
          return '. do {';
  #        warn "start Expr: CurrentEl: ", $e->current_element, "\n";
      }
      
      return '';
  }
  
  sub end_element {
      my ($e, $node) = @_;
      
      my $tag = $node->{Name};
      
      if ($tag eq 'page') {
      }
      elsif ($tag eq 'structure') {
      }
      elsif ($tag eq 'dtd') {
      }
      elsif ($tag eq 'include') {
          return ";\n";
      }
      elsif ($tag eq 'import') {
          return ";\n";
      }
      elsif ($tag eq 'content') {
      }
      elsif ($tag eq 'logic') {
      }
      elsif ($tag eq 'element') {
          return '$parent = $parent->getParentNode;' . "\n";
      }
      elsif ($tag eq 'attribute') {
          return ');' . "\n";
      }
      elsif ($tag eq 'name') {
          if ($node->{Parent}->{Name} =~ /^(.*:)?element$/) {
              $e->manage_text(0, 1);
              return '; my $elem = $document->createElement($name);' .
                      '$parent->appendChild($elem); $parent = $elem; }' . "\n";
          }
          elsif ($node->{Parent}->{Name} =~ /^(.*:)?attribute$/) {
              $e->{attrib_seen_name} = 1;
              return '; my $attr = $document->createAttribute($name, ""';
          }
          else {
              die "xsp:name parent node: $node->{Parent}->{Name} not valid";
          }
      }
      elsif ($tag eq 'pi') {
      }
      elsif ($tag eq 'comment') {
          return '); $parent->appendChild($comment); }' . "\n";
      }
      elsif ($tag eq 'text') {
          return '); $parent->appendChild($text); }' . "\n";
      }
      elsif ($tag eq 'expr') {
  #        warn "expr: -2 = {", $node->{Parent}->{NamespaceURI}, "}", $node->{Parent}->{Name}, "\n";
          if (Apache::AxKit::Language::XSP::is_xsp_namespace($node->{Parent}->{NamespaceURI})) {
              if (!$e->manage_text() || $node->{Parent}->{Name} =~ /^(.*:)?content$/) {
                  return <<'EOT';
  }); # xsp tag
      $parent->appendChild($text); 
  }
  EOT
              }
              elsif ($node->{Parent}->{Name} =~ /^(.*:)?(logic|expr)$/) {
                  return '}';
              }
          }
          else {
              return <<'EOT';
  }); # non xsp tag
      $parent->appendChild($text); 
  }
  EOT
          }
          return '}';
      }
      
      return '';
  }
  
  1;
  
  ############################################################
  ## Default (non-xsp-namespace) handlers
  ############################################################
  
  package AxKit::XSP::DefaultHandler;
  
  sub start_element {
      my ($e, $node) = @_;
      
      my $code;
      if (!$e->{XSP_User_Root}) {
          $e->{XSP_Script} .= join("\n",
                  'sub handler {',
                  'my ($r, $cgi) = @_;',
                  'my $document = XML::LibXML::Document->createDocument("1.0", "UTF-8");',
                  'my ($parent);',
                  "\n",
                  );
          $e->{XSP_User_Root} = 1;
          $code = '{ my $elem = $document->createElement(q(' . $node->{Name} . '));' .
  #        $code = '{ my $elem = $document->createElementNS(q(' . ($node->{NamespaceURI} || "") . '), q(' . $node->{Name} . '));' .
                  '$document->setDocumentElement($elem); $parent = $elem; }' . "\n";
      }
      else {
          $code = '{ my $elem = $document->createElement(q(' . $node->{Name} . '));' .
                  '$parent->appendChild($elem); $parent = $elem; }' . "\n";
      }
      
      for my $attr (@{$node->{Attributes}}) {
          $code .= '$parent->setAttribute(q(' . $attr->{Name} . 
                  '), q(' . $attr->{Value} . 
                  '));' . "\n";
      }
  
      for my $ns (keys %{$e->{Current_NS}}) {
          $code .= '$parent->setAttribute("xmlns:" . q(' . $ns .'), q(' .
                  $e->{Current_NS}{$ns} . '));';
      }
      
      push @{ $e->{NS_Stack} },
              { %{ $e->{Current_NS} || {} } };
      
      $e->{Current_NS} = {};
      
      return $code;
  }
  
  sub end_element {
      my ($e, $element) = @_;
      
      $e->{Current_NS} = pop @{ $e->{NS_Stack} };
      
      return '$parent = $parent->getParentNode;' . "\n";
  }
  
  sub characters {
      my ($e, $node) = @_;
      
      my $text = $node->{Data};
      
      return '' unless $e->{XSP_User_Root}; # should not happen!
      
      if (!$e->{XSP_Indent}) {
          return '' unless $text =~ /\S/;
      }
      
      $text =~ s/\|/\\\|/g;
      
      return '{ my $text = $document->createTextNode(q|' . $text . '|);' .
              '$parent->appendChild($text); }' . "\n";
  }
  
  sub comment {
      return '';
  }
  
  sub processing_instruction {
      return '';
  }
  
  1;
  
  ######################################################
  ## SAXParser
  ######################################################
  
  package AxKit::XSP::SAXParser;
  
  use XML::LibXML 1.30;
  
  sub new {
      my ($type, %self) = @_; 
      return bless \%self, $type;
  }
  
  sub parse {
      my ($self, $thing) = @_;
  
      my $doc;
  
      my $parser = XML::LibXML->new();
      $parser->expand_entities(1);
      
      if (ref($thing)) {
          $doc = $parser->parse_fh($thing);
      }
      else {
          $doc = $parser->parse_string($thing);
      }
      $doc->process_xinclude;
      
      my $document = { Parent => undef };
      $self->{Handler}->start_document($document);
      
      my $root = $doc->getDocumentElement;
      if ($root) {
          process_node($self->{Handler}, $root);
      }
      
      $self->{Handler}->end_document($document);
  }
  
  sub process_node {
      my ($handler, $node) = @_;
      
      my $node_type = $node->getType();
      if ($node_type == XML_COMMENT_NODE) {
          $handler->comment( { Data => $node->getData } );
      }
      elsif ($node_type == XML_TEXT_NODE || $node_type == XML_CDATA_SECTION_NODE) {
          # warn($node->getData . "\n");
          $handler->characters( { Data => $node->getData } );
      }
      elsif ($node_type == XML_ELEMENT_NODE) {
          # warn("<" . $node->getName . ">\n");
          process_element($handler, $node);
          # warn("</" . $node->getName . ">\n");
      }
      elsif ($node_type == XML_ENTITY_REF_NODE) {
          foreach my $kid ($node->getChildnodes) {
              # warn("child of entity ref: " . $kid->getType() . " called: " . $kid->getName . "\n");
              process_node($handler, $kid);
          }
      }
      elsif ($node_type == XML_DOCUMENT_NODE) {
          # just get root element. Ignore other cruft.
          foreach my $kid ($node->getChildnodes) {
              if ($kid->getType() == XML_ELEMENT_NODE) {
                  process_element($handler, $kid);
                  last;
              }
          }
      }
      else {
          warn("unknown node type: $node_type");
      }
  }
  
  sub process_element {
      my ($handler, $element) = @_;
      
      my @attr;
      
      foreach my $attr ($element->getAttributes) {
          push @attr, {
              Name => $attr->getName,
              Value => $attr->getData,
              NamespaceURI => $attr->getNamespaceURI,
              Prefix => $attr->getPrefix,
              LocalName => $attr->getLocalName,
          };
      }
      
      my $node = {
          Name => $element->getName,
          Attributes => \@attr,
          NamespaceURI => $element->getNamespaceURI,
          Prefix => $element->getPrefix,
          LocalName => $element->getLocalName,
      };
      
      $handler->start_element($node);
      
      foreach my $child ($element->getChildnodes) {
          process_node($handler, $child);
      }
      
      $handler->end_element($node);
  }
  
  ############################################################
  # Base page class
  ############################################################
  
  package Apache::AxKit::Language::XSP::Page;
  
  sub has_changed {
      my $class = shift;
      my $mtime = shift;
      return 1;
  }
  
  sub cache_params {
      my $class = shift;
      my ($r, $cgi) = @_;
      return '';
  }
  
  1;
  
  __END__
  =pod
  
  =head1 NAME
  
  Apache::AxKit::Language::XSP - eXtensible Server Pages
  
  =head1 SYNOPSIS
  
    <xsp:page
      xmlns:xsp="http://apache.org/xsp/core/v1">
  
      <xsp:structure>
          <xsp:import>Time::Object</xsp:import>
      </xsp:structure>
  
      <page>
          <title>XSP Test</title>
          <para>
          Hello World!
          </para>
          <para>
          Good 
          <xsp:logic>
          if (localtime->hour >= 12) {
              <xsp:content>Afternoon</xsp:content>
          }
          else {
              <xsp:content>Morning</xsp:content>
          }
          </xsp:logic>
          </para>
      </page>
      
    </xsp:page>
  
  =head1 DESCRIPTION
  
  XSP implements a tag-based dynamic language that allows you to develop
  your own tags, examples include sendmail and sql taglibs. It is AxKit's
  way of providing an environment for dynamic pages. XSP is originally part
  of the Apache Cocoon project, and so you will see some Apache namespaces
  used in XSP.
  
  =head1 Tag Reference
  
  =head2 C<<xsp:page>>
  
  This is the top level element, although it does not have to be. AxKit's
  XSP implementation can process XSP pages even if the top level element
  is not there, provided you use one of the standard AxKit ways to turn
  on XSP processing for that page. See L<AxKit>.
  
  The attribute C<language="Perl"> can be present, to mandate the language.
  This is useful if you expect people might mistakenly try and use this
  page on a Cocoon system. The default value of this attribute is "Perl".
  
  XSP normally swallows all whitespace in your output. If you don't like
  this feature, or it creates invalid output, then you can add the
  attribute: C<indent-result="yes">
  
  =head2 C<<xsp:structure>>
  
    parent: <xsp:page>
  
  This element appears at the root level of your page before any non-XSP
  tags. It defines page-global "things" in the C<<xsp:logic>> and
  C<<xsp:import>> tags.
  
  =head2 C<<xsp:import>>
  
    parent: <xsp:structure>
  
  Use this tag for including modules into your code, for example:
  
    <xsp:structure>
      <xsp:import>DBI</xsp:import>
    </xsp:structure>
  
  =head2 C<<xsp:logic>>
  
    parent: <xsp:structure>, any
  
  The C<<xsp:logic>> tag introduces some Perl code into your page.
  
  As a child of C<<xsp:structure>>, this element allows you to define
  page global variables, or functions that get used in the page. Placing
  functions in here allows you to get around the Apache::Registry
  closures problem (see the mod_perl guide at http://perl.apache.org/guide
  for details).
  
  Elsewhere the perl code contained within the tags is executed on every
  view of the XSP page.
  
  B<Warning:> Be careful - the Perl code contained within this tag is still
  subject to XML's validity constraints. Most notably to Perl code is that
  the & and < characters must be escaped into &amp; and &lt; respectively.
  You can get around this to some extent by using CDATA sections. This is
  especially relevant if you happen to think something like this will work:
  
    <xsp:logic>
      if ($some_condition) {
        print "<para>Condition True!</para>";
      }
      else {
        print "<para>Condition False!</para>";
      }
    </xsp:logic>
  
  The correct way to write that is simply:
  
    <xsp:logic>
      if ($some_condition) {
        <para>Condition True!</para>
      }
      else {
        <para>Condition False!</para>
      }
    </xsp:logic>
  
  The reason is that XSP intrinsically knows about XML!
  
  =head2 C<<xsp:content>>
  
    parent: <xsp:logic>
  
  This tag allows you to temporarily "break out" of logic sections to generate
  some XML text to go in the output. Using something similar to the above
  example, but without the surrounding C<<para>> tag, we have:
  
    <xsp:logic>
      if ($some_condition) {
        <xsp:content>Condition True!</xsp:content>
      }
      else {
        <xsp:content>Condition False!</xsp:content>
      }
    </xsp:logic>
  
  =head2 C<<xsp:element>>
  
  This tag generates an element of name equal to the value in the attribute
  C<name>. Alternatively you can use a child element C<<xsp:name>> to specify
  the name of the element. Text contents of the C<<xsp:element>> are created
  as text node children of the new element.
  
  =head2 C<<xsp:attribute>>
  
  Generates an attribute. The name of the attribute can either be specified
  in the C<name="..."> attribute, or via a child element C<<xsp:name>>. The
  value of the attribute is the text contents of the tag.
  
  =head2 C<<xsp:comment>>
  
  Normally XML comments are stripped from the output. So to add one back in
  you can use the C<<xsp:comment>> tag. The contents of the tag are the
  value of the comment.
  
  =head2 C<<xsp:text>>
  
  Create a plain text node. The contents of the tag are the text node to be
  generated. This is useful when you wish to just generate a text node while
  in an C<<xsp:logic>> section.
  
  =head2 C<<xsp:expr>>
  
  This is probably the most useful, and most important (and also the most
  complex) tag. An expression is some perl code that executes, and the results
  of which are added to the output. Exactly how the results are added to the
  output depends very much on context.
  
  The default method for output for an expression is as a text node. So for
  example:
  
    <p>
    It is now: <xsp:expr>localtime</xsp:expr>
    </p>
  
  Will generate a text node containing the time.
  
  If the expression is contained within an XSP namespaces, that is either a
  tag in the xsp:* namespace, or a tag implementing a tag library, then an
  expression generally does not create a text node, but instead is simply
  wrapped in a Perl C<do {}> block, and added to the perl script. However,
  there are anti-cases to this. For example if the expression is within
  a C<<xsp:content>> tag, then a text node is created.
  
  Needless to say, in every case, C<<xsp:expr>> should just "do the right
  thing". If it doesn't, then something (either a taglib or XSP.pm itself)
  is broken and you should report a bug.
  
  =head1 DESIGN PATTERNS
  
  Writing your own taglibs can be tricky, because you're using an event
  based API to write out Perl code. You may want to take a look at the
  Apache::AxKit::Language::XSP::TaglibHelper module, which comes with
  AxKit and allows you to easily publish a taglib without writing
  XML event code.
  
  These patterns represent the things you may want to achieve when 
  authoring a tag library "from scratch".
  
  B<1. Your tag is a wrapper around other things.>
  
  Example:
  
    <mail:sendmail>...</mail:sendmail>
  
  Solution:
  
  Start a new block, so that you can store lexical variables, and declare
  any variables relevant to your tag:
  
  in parse_start:
  
    if ($tag eq 'sendmail') {
      return '{ my ($to, $from, $sender);';
    }
  
  Often it will also be relevant to execute that code when you see the end
  tag:
  
  in parse_end:
  
    if ($tag eq 'sendmail') {
      return 'Mail::Sendmail::sendmail( 
              to => $to, 
              from => $from, 
              sender => $sender 
              ); }';
    }
  
  Note there the closing of that original opening block.
  
  B<2. Your tag indicates a parameter for a surrounding taglib.>
  
  Example:
  
    <mail:to>...</mail:to>
  
  Solution:
  
  Having declared the variable as above, you simply set it to the empty
  string, with no semi-colon:
  
  in parse_start:
  
    if ($tag eq 'to') {
      return '$to = ""';
    }
  
  Then in parse_char:
  
  sub parse_char {
    my ($e, $text) = @_;
    $text =~ s/^\s*//;
    $text =~ s/\s*$//;
  
    return '' unless $text;
  
    $text =~ s/\|/\\\|/g;
    return ". q|$text|";
  }
  
  Note there's no semi-colon at the end of all this, so we add that:
  
  in parse_end:
  
    if ($tag eq 'to') {
      return ';';
    }
  
  All of this black magic allows other taglibs to set the thing in that
  variable using expressions.
  
  B<3. You want your tag to return a scalar (string) that does the right thing
  depending on context. For example, generates a Text node in one place or
  generates a scalar in another context.>
  
  Solution:
  
  use start_expr(), append_to_script(), end_expr().
  
  Example:
  
    <example:get-datetime format="%Y-%m-%d %H:%M:%S"/>
  
  in parse_start:
  
    if ($tag eq 'get-datetime') {
      start_expr($e, $tag); # creates a new { ... } block
      my $local_format = lc($attribs{format}) || '%a, %d %b %Y %H:%M:%S %z';
      return 'my ($format); $format = q|' . $local_format . '|;';
    }
  
  in parse_end:
  
    if ($tag eq 'get-datetime') {
      append_to_script($e, 'use Time::Object; localtime->strftime($format);');
      end_expr($e);
      return '';
    }
  
  Explanation:
  
  This is more complex than the first 2 examples, so it warrants some 
  explanation. I'll go through it step by step.
  
    start_expr(...)
  
  This tells XSP that this really generates a <xsp:expr> tag. Now we don't
  really generate that tag, we just execute the handler for it. So what
  happens is the <xsp:expr> handler gets called, and it looks to see what
  the current calling context is. If its supposed to generate a text node,
  it generates some code to do that. If its supposed to generate a scalar, it
  does that too. Ultimately both generate a do {} block, so we'll summarise 
  that by saying the code now becomes:
  
    do {
  
  (the end of the block is generated by end_expr()).
  
  Now the next step (ignoring the simple gathering of the format variable), is
  a return, which appends more code onto the generated perl script, so we
  get:
  
    do {
      my ($format); $format = q|%a, %d %b %Y %H:%M:%S %z|;
  
  Now we immediately receive an end_expr, because this is an empty element
  (we'll see why we formatted it this way in #5 below). The first thing we
  get is:
  
    append_to_script($e, 'use Time::Object; localtime->strftime($format);');
  
  This does exactly what it says, and the script becomes:
  
    do {
      my ($format); $format = q|%a, %d %b %Y %H:%M:%S %z|;
      use Time::Object; localtime->strftime($format);
  
  Finally, we call:
  
    end_expr($e);
  
  which closes the do {} block, leaving us with:
  
    do {
      my ($format); $format = q|%a, %d %b %Y %H:%M:%S %z|;
      use Time::Object; localtime->strftime($format);
    }
  
  Now if you execute that in Perl, you'll see the do {} returns the last
  statement executed, which is the C<localtime->strftime()> bit there,
  thus doing exactly what we wanted.
  
  Note that start_expr, end_expr and append_to_script aren't exported
  by default, so you need to do:
  
    use Apache::AxKit::Language::XSP 
          qw(start_expr end_expr append_to_script);
  
  B<4. Your tag can take as an option either an attribute, or a child tag.>
  
  Example:
  
    <util:include-uri uri="http://server/foo"/>
  
  or
  
    <util:include-uri>
      <util:uri><xsp:expr>$some_uri</xsp:expr></util:uri>
    </util:include-uri>
  
  Solution:
  
  There are several parts to this. The simplest is to ensure that whitespace
  is ignored. We have that dealt with in the example parse_char above. Next
  we need to handle that variable. Do this by starting a new block with the
  tag, and setting up the variable:
  
  in parse_start:
  
    if ($tag eq 'include-uri') {
      my $code = '{ my ($uri);';
      if ($attribs{uri}) {
        $code .= '$uri = q|' . $attribs{uri} . '|;';
      }
      return $code;
    }
  
  Now if we don't have the attribute, we can expect it to come in the 
  C<<util:uri>> tag:
  
  in parse_start:
  
    if ($tag eq 'uri') {
      return '$uri = ""'; # note the empty string!
    }
  
  Now you can see that we're not explicitly setting C<$uri>, that's because the
  parse_char we wrote above handles it by returning '. q|$text|'. And if we
  have a C<<xsp:expr>> in there, that's handled automagically too.
  
  Now we just need to wrap things up in the end handlers:
  
  in parse_end:
  
    if ($tag eq 'uri') {
      return ';';
    }
    if ($tag eq 'include-uri') {
      return 'Taglib::include_uri($uri); # execute the code
              } # close the block
      ';
    }
  
  B<5. You want to return a scalar that does the right thing in context, but
  also can take a parameter as an attribute I<or> a child tag.>
  
  Example:
  
    <esql:get-column column="user_id"/>
  
  vs
  
    <esql:get-column>
      <esql:column><xsp:expr>$some_column</xsp:expr></esql:column>
    </esql:get-column>
  
  Solution:
  
  This is a combination of patterns 3 and 4. What we need to do is change
  #3 to simply allow our variable to be added as in #4 above:
  
  in parse_start:
  
    if ($tag eq 'get-column') {
      start_expr($e, $tag);
      my $code = 'my ($col);'
      if ($attribs{col}) {
        $code .= '$col = q|' . $attribs{col} . '|;';
      }
      return $code;
    }
    if ($tag eq 'column') {
      return '$col = ""';
    }
  
  in parse_end:
  
    if ($tag eq 'column') {
      return ';';
    }
    if ($tag eq 'get-column') {
      append_to_script($e, 'Full::Package::get_column($col)');
      end_expr($e);
      return '';
    }
  
  B<6. You have a conditional tag>
  
  Example:
  
    <esql:no-results>
      No results!
    </esql:no-results>
  
  Solution:
  
  The problem here is that taglibs normally recieve character/text events
  so that they can manage variables. With a conditional tag, you want
  character events to be handled by the core XSP and generate text events.
  So we have a switch for that:
  
    if ($tag eq 'no-results') {
      $e->manage_text(0);
      return 'if (AxKit::XSP::ESQL::get_count() == 0) {';
    }
  
  Turning off manage_text with a zero simply ensures that immediate children
  text nodes of this tag don't fire text events to the tag library, but
  instead get handled by XSP core, thus creating text nodes (and doing
  the right thing, generally).
  
  =head1 <xsp:expr> (and start_expr, end_expr) Notes
  
  B<Do not> consider adding in the 'do {' ... '}' bits yourself. Always
  leave this to the start_expr, and end_expr functions. This is because the
  implementation could change, and you really don't know better than
  the underlying XSP implementation. You have been warned.
  
  =cut
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/Language/XSP/TaglibHelper.pm
  
  Index: TaglibHelper.pm
  ===================================================================
  # $Id: TaglibHelper.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
  
  package Apache::AxKit::Language::XSP::TaglibHelper;
  @ISA = qw(Apache::AxKit::Language::XSP);
  use XML::LibXML;
  
  use strict;
  
  sub parse_char {
      my ($e, $text) = @_;
      $text =~ s/^\s*//;
      $text =~ s/\s*$//;
  
      return '' unless $text;
  
      $text =~ s/\|/\\\|/g;
      return ". q|$text|";
  }
  
  # Try to find the given function name and see if it's in the "use"ing
  # module's list of exported functions. Retuns the function spec if
  # if it found it, or undef if it didn't.
  sub is_function ($$) {
      my ($pkg, $fname) = @_;
  
      no strict;
      my @exports = @{"$pkg\::EXPORT_TAGLIB"};
      use strict;
  
      foreach my $funspec(@exports) {
          return $funspec if $funspec =~ /^$fname *\(/;
      }
  
      return undef;
  }
  
  sub func_name ($) {
      my ($funspec) = @_;
  
      my %opts = func_options($funspec);
      return $opts{isreally} if $opts{isreally};
      my ($argspec) = ($funspec =~ /^ *(.*?)\(/);
      return $argspec;
  }
  
  sub func_options ($) {
      my ($funspec) = @_;
  
      my @args = split (/:\s*/, $funspec);
      shift @args;
      my %retval = ();
      foreach (@args) {
          #                            |     |  Bra!
          my ($key, $value) = ($_ =~ /(.*)=(.*)/);
          $retval{$key} = $value;
      }
      return %retval;
  }
  
  sub required_args ($) {
      my ($funspec) = @_;
  
      my ($argspec) = ($funspec =~ /\(\s*([^\);]*)/);
      my @retval;
      foreach my $arg(split (/,/, $argspec)) {
          $arg =~ s/^\s*//g;
          $arg =~ s/\s*$//g;
          push (@retval, $arg);
      }
      return @retval;
  }
  
  sub optional_args ($) {
      my ($funspec) = @_;
  
      my ($argspec) = ($funspec =~ /; *([^\)]*)/);
      my @retval;
      foreach my $arg(split (/,/, $argspec)) {
          $arg =~ s/^\s*//g;
          $arg =~ s/\s*$//g;
          push (@retval, $arg);
      }
      return @retval;
  }
  
  sub find_arg ($@) {
      my $argname = shift;
  
      foreach (@_) { return $_ if /^.$argname/ }
      return "";
  }
  
  # quieten warnings when compiling
  sub handle_result ($$$$$;@);
  
  # The input to this function is the *result* from a taglib function, and
  # therefore can be anything. We need to be able to turn it into a set
  # of XML tags.
  sub handle_result ($$$$$;@) {
      my $funspec     = shift;
      my $lastkey	  = shift; # If parent was a hash, else this is undef.
      my $indentlevel = shift;
      my $document    = shift;
      my $parent      = shift;
  
      my $indent = '  ' x $indentlevel;
  
      my %options = func_options($funspec);
  
      if ($options{as_xml}) {
          $parent->appendWellBalancedChunk(shift);
          return;
      }
  
      # if we got more than one result (we assume it's an array),
      # we'll act as if we got a single arrayref
      # forcearray makes it always think there's an array; useful
      # for functions that are returning an array of one value
      if ($indentlevel == 0 and ($options{forcearray} or scalar @_ > 1)) {
  	# if thre's one item, and it's undef, return an arrayref to an empty array
  	if (scalar @_ == 1 and !defined($_[0])) {
  		@_ = ([]);
  	} else {
  	        @_ = ([@_]);
  	}
      }
      
      # break down each arg in the results, possibly call self recursively
      foreach my $ref(@_) {
          if ( (ref($ref)) =~ /ARRAY/) {
  
              # arrays are hard because they're not keyed except by numbers
              # the array list itself will have a wrapper tag of funcname+"-list" if
              # we're at indent level 0, or no wrapper if it's below level 0
              if ($indentlevel == 0) {
                  $parent->appendChild($document->createTextNode("\n" . $indent));
                  my $el;
                  if ($options{listtag}) {
                      $el = $document->createElement($options{listtag});
                  }
                  else {
                      my $funcname = $options{'array_uses_hash'} ? $lastkey : 
                      	func_name($funspec) || func_name($funspec);
                      $funcname =~ s/_|\s/-/g;    # convert back to XML-style tagnames
                      $el = $document->createElement("${funcname}-list");
                  }
                  $parent->appendChild($el);
                  $parent = $el;
              }
  
              my $id = 1;
              foreach my $value(@$ref) {
          # each item within an array should have a wrapper "-item" tag
          my $item;
          if ($options{itemtag}) {
              $item = $document->createElement($options{itemtag});
          }
          else {
              my $funcname = $options{'array_uses_hash'} ? $lastkey : 
                      func_name($funspec) || func_name($funspec);
              $funcname =~ s/_|\s/-/g;    # convert back to XML-style tagnames
              $item = $document->createElement("${funcname}-item");
          }
                  $item->setAttribute("id", $id++);
                  $parent->appendChild($item);
                  handle_result($funspec, $lastkey, $indentlevel + 1, $document, $item, $value);
              }
          }
          elsif ( (ref($ref)) =~ /HASH/) {
              # hashes are relatively easy because they're keyed
              $parent->appendChild($document->createTextNode("\n" . $indent));
              while (my ($key, $value) = each %$ref) {
                  my $el = $document->createElement($key);
                  $parent->appendChild($el);
                  handle_result($funspec, $key, $indentlevel + 1, $document, $el, $value);
              }
          }
          else {
  
              # not arrayref or not hashref: it's either a scalar or an unsupported
              # type, so we'll just dump it as text
              # special case: at the highest level of hierarchy, we can just return a
              # string because it will be turned automatically into a text node by
              # AxKit
              if ($indentlevel == 0) {
                  return $_[0];
              }
              elsif (defined $_[0]) {
                  $parent->appendChild($document->createTextNode($_[0]));
              }
          }
  
      }
  }
  
  $main::indent = 0;
  
  # quieten warnings when compiling
  sub convert_from_dom ($$);
  # This function converts from a DOM tree into a collection of hashes.
  # It's used for "*" taglib arguments.
  sub convert_from_dom ($$) {
      my ($funspec, $node) = @_;
  local $main::indent = $main::indent;
  $main::indent++;
  require Data::Dumper;
      # if we're at the first level, we'll ignore our top node, because we know it's just
      # a dummy root node
      if (UNIVERSAL::isa($node, "XML::LibXML::Element")) {
          my @children = ($node->getChildnodes,$node->getAttributes);
          my $multiple = 0;
          my %mdetect = ();
          # look for multiple children with the same name, which
          # means we should treat it as an array
          foreach (@children) {
              # sometimes we get blank text nodes; we don't want 'em
              next if UNIVERSAL::isa($_, "XML::LibXML::Text") and $_->getData eq '';
    
              $multiple = 1 if $mdetect{$_->getName};
              $mdetect{$_->getName} = 1;
          }
                  # special option might force us to treat it as an array regardless
                  if (!$multiple && @children == 1) {
                          my %opts = func_options($funspec);
                          if (my $taglist = $opts{tagisarray}) {
                                  $multiple = 1
                                          if grep 
                                                  { $_ eq $node->getName } 
                                                  split(/,/, $opts{tagisarray}); 
                          }
  
                  }
  
          if ($multiple) {
              my $retval = [];
              foreach (@children) {
              # sometimes we get blank text nodes; we don't want 'em
              next if UNIVERSAL::isa($_, "XML::LibXML::Text") and $_->getData eq '';
                  push(@$retval, convert_from_dom($funspec,$_));
              }
              return $retval;
          }
          else {
                          # <list>text</list>  converts to { list => 'text' },
                          # <list><item>text</item></list> converts to { list => { item => 'text' } };
                          # in other words, if there's only one child, we need to figure out
                          # if it's a text node or a regular tag
                          if ((@children > 1) or (@children == 1 and not UNIVERSAL::isa($children[0], "XML::LibXML::Text"))) {
                  my $retval   = {};
                  foreach (@children) {
  					my $name = $_->getName;
              # sometimes we get blank text nodes; we don't want 'em
              next if UNIVERSAL::isa($_, "XML::LibXML::Text") and $_->getData eq '';
  					$name = 'TEXT' if UNIVERSAL::isa($_, "XML::LibXML::Text");
                      $retval->{$name} = convert_from_dom($funspec,$_);
                 }
                  return $retval;
              }
              elsif (@children == 1) {
                  my $retval = convert_from_dom($funspec,$children[0]);
                  return $retval;
              }
          }
          return "";
      }
      else {
  
          # we'll just assume it's text for now
          return $node->getData;
      }
  }
  
  @Apache::AxKit::Language::XSP::TaglibHelper::function_stack = ();
  
  sub parse_start {
      my ($e, $tag, %attribs) = @_;
      # Dashes are more "XML-like" than underscores, but we can't use
      # dashes in function or argument names. So we'll just convert them
      # arbitrarily here.
      $tag =~ s/-/_/g;
  
      my $pkg = caller;
  
      # horrible hack: if the caller is the SAX library directly,
      # then we'll just have to assume that we're testing TaglibHelper
      $pkg = "Apache::AxKit::Language::XSP::TaglibHelper" if $pkg eq "AxKit::XSP::SAXHandler";
      my $funspec = is_function($pkg, $tag);
  
      my $code = "";
      if ($funspec) {
          my %options = func_options($funspec);
          push (@Apache::AxKit::Language::XSP::TaglibHelper::function_stack, $funspec);
          $code = "{ my \%_args = ();";
          while (my ($key, $value) = each %attribs) {
              my $paramspec = find_arg($key, required_args($funspec), optional_args($funspec));
              if ($paramspec =~ /^\*/) {
                  $code .= " die 'Argument $key to function $tag is tree type, and cannot be set in an attribute.';\n";
              }
              elsif ($paramspec =~ /^\@/) {
                  $key   =~ s/-/_/g;
                  $value =~ s/\|/\\\|/g;
                  $code .= " \$_args{$key} ||= []; push \@{\$_args{$key}}, q|$value|;\n";
              }
              else {
                  $key   =~ s/-/_/g;
                  $value =~ s/\|/\\\|/g;
                  $code .= " \$_args{$key} = q|$value|;\n";
              }
          }
          # if it's a "conditional" function (i.e. it wraps around conditional tags)
          # we need to pick up the arguments in the attributes only, and execute
          # the function here
          if ($options{conditional}) {
              foreach my $arg(required_args($funspec)) {
                  $arg =~ s/^.//g;    # ignore type specs for now
                  $code .=
              " die 'Required arg \"$arg\" for tag $tag is missing' if not defined \$_args{$arg};\n";
              }
              $code .= " if ($pkg\::" . func_name($funspec) . "(";
  
              foreach my $arg(required_args($funspec), optional_args($funspec)) {
                  $arg =~ s/^.//g;    # remove type specs from hash references
                  $code .= "\$_args{$arg},";
              }
              $code .= ")) {\n";
              $e->manage_text(0);
          }
          else {
              $e->start_expr($tag);
          }
      }
      else {
          my $funspec =
            $Apache::AxKit::Language::XSP::TaglibHelper::function_stack
            [$#Apache::AxKit::Language::XSP::TaglibHelper::function_stack];
          my $paramspec = find_arg($tag, required_args($funspec), optional_args($funspec));
  
          # if the param is of type '*', then we have to prepare a new DOM tree
          # but we default to assuming it's a scalar argument
          if ($paramspec =~ /^\*/) {
              $code =
  " { my \$theparent = \$parent ; \$parent = \$document->createElement('ROOT-$tag'); \$_args{$tag} = \$parent;\n";
              $e->manage_text(0);
          }
          elsif ($paramspec =~ /^\@/) {
  	    if (keys %attribs){
  		my $attrib_string = '';
  		while (my ($key, $value) = each %attribs) {
  			$key =~ s/\'/\\\'/g;
  			$value =~ s/\'/\\\'/g;			
  			$attrib_string .= "'$key','$value',";
  		}
  		$attrib_string =~ s/,$//;
  		$code = " \$_args{$tag} ||= [];
  			  my \$href = {};
  			  my %h = ($attrib_string);
  			  while (my (\$key,\$value) = each %h){
  				\$href->{\$key} = \$value;
  			  }
  			  push \@{\$_args{$tag}}, \$href;\n";
  	    }
  	    else{
              $code = " \$_args{$tag} ||= []; push \@{\$_args{$tag}}, \"\"\n";
          }
          }
          else {
  	    if (keys %attribs){
  		my $attrib_string = '';
  		while (my ($key, $value) = each %attribs) {
  			$key =~ s/\'/\\\'/g;
  			$value =~ s/\'/\\\'/g;			
  			$attrib_string .= "'$key','$value',";
  		}
  		$attrib_string =~ s/,$//;
  		$code = " \$_args{$tag} ||= [];
  			  my \$href = {};
  			  my %h = ($attrib_string);
  			  while (my (\$key,\$value) = each %h){
  			  	last if \$href->{\$key};
  				\$href->{\$key} = \$value;
  			  }
  			  \$_args{$tag} = \$href;\n";
  	    	
  	    }
  	    else{
              $code = " \$_args{$tag} = \"\"\n";
          }
      }
      }
      
      return $code;
  }
  
  sub parse_end {
      my ($e, $tag) = @_;
      my $origtag = $tag;
      $tag =~ s/-/_/g;
  
      my $pkg = caller;
      $pkg = "Apache::AxKit::Language::XSP::TaglibHelper" if $pkg eq "AxKit::XSP::SAXHandler";
      my $funspec = is_function($pkg, $tag);
  
      my $code = "";
      if ($funspec) {
          pop (@Apache::AxKit::Language::XSP::TaglibHelper::function_stack);
          my %options = func_options($funspec);
          if ($options{conditional}) {
              $e->manage_text(1);
              return "}}\n";
          }
          else {
              $code = ";";
              foreach my $arg(required_args($funspec)) {
              $arg =~ s/^.//g;    # ignore type specs for now
              $code .=
          " die 'Required arg \"$arg\" for tag $origtag is missing' if not defined \$_args{$arg};\n";
              }
              $code .=
          " Apache::AxKit::Language::XSP::TaglibHelper::handle_result('$funspec', undef(), 0, \$document, \$parent, $pkg\::"
                . func_name($funspec) . "(";
  
              foreach my $arg(required_args($funspec), optional_args($funspec)) {
              $arg =~ s/^.//g;    # remove type specs from hash references
              $code .= "\$_args{$arg},";
              }
              $code .= "));}\n";
              $e->append_to_script($code);
              $e->end_expr();
              return '';
          }
      }
      else {
  
          # what function are we in?
          my $funspec =
            $Apache::AxKit::Language::XSP::TaglibHelper::function_stack
            [$#Apache::AxKit::Language::XSP::TaglibHelper::function_stack];
          my $paramspec = find_arg($tag, required_args($funspec), optional_args($funspec));
  
          # if the param is of type '*', then we restore the old DOM tree
          if ($paramspec =~ /^\*/) {
              $e->manage_text(1);
              $code =
  " \$parent = \$theparent; \$_args{$tag} = Apache::AxKit::Language::XSP::TaglibHelper::convert_from_dom('$funspec',\$_args{$tag}); }";
          }
          return "$code;\n";
      }
  }
  
  ##############################################################################
  # a built-in taglib, so we can test the functionality of TaglibHelper
  no strict;
  
  $NS = 'http://apache.org/xsp/testtaglibhelper/v1';
  @EXPORT_TAGLIB = (
    'test_hello($name)', 
    'test_echo(*whatever)', 
    'test_echo_array(@array)', 
    'test_get_person($name)', 
    'test_get_people($name)',
    'test_get_people2($name):listtag=people:itemtag=person',
  );
  
  use strict;
  
  # now you declare your functions
  sub test_hello ($) {
      my ($name) = @_;
      return "Hello, $name!";
  }
  
  sub test_echo ($) {
      my ($whatever) = @_;
      return $whatever;
  }
  
  sub test_echo_array ($) {
      my ($whatever) = @_;
      return $whatever;
  }
  
  sub test_get_person ($) {
      my ($name) = @_;
      srand(time + $$) if not $Apache::AxKit::Language::XSP::TaglibHelper::didsrand;
      $Apache::AxKit::Language::XSP::TaglibHelper::didsrand = 1;
      return {
          person => {
              name  => $name,
                age => int(rand(99)),
          }
      };
  }
  
  sub test_get_people ($) {
      my ($name) = @_;
      return [
          test_get_person($name),       test_get_person($name . "2"),
          test_get_person($name . "3"), test_get_person($name . "4"),
      ];
  }
  
  sub test_get_people2 ($) {
      my ($name) = @_;
      return (test_get_person($name)->{person}, test_get_person($name . "2")->{person},
        test_get_person($name . "3")->{person}, test_get_person($name . "4")->{person},);
  }
  
  1;
  
  __END__
  
  =head1 NAME
  
  TaglibHelper - module to make it easier to write a taglib
  
  =head1 SYNOPSIS
  
  Put this code at the top of your taglib module:
  
      # this stuff, you change for each taglib
      $NS = 'http://apache.org/xsp/testtaglib/v1';
      @EXPORT_TAGLIB = (
      'func1($arg1)',
      'func2($arg1,$arg2)',
      'func3($arg1,$arg2;$optarg)',
      'func4($arg1,*treearg)',
      'func4($arg1,*treearg):listtag=mylist:itemtag=item',
      );
  
      # this stuff you don't
      use Apache::AxKit::Language::XSP::TaglibHelper;
      sub parse_char { Apache::AxKit::Language::XSP::TaglibHelper::parse_char(@_); }
      sub parse_start { Apache::AxKit::Language::XSP::TaglibHelper::parse_start(@_); }
      sub parse_end { Apache::AxKit::Language::XSP::TaglibHelper::parse_end(@_); }
      use strict;
  
  ...and then edit the $NS and @EXPORT_TAGLIB to reflect
  your taglib's namespace and list of functions.
  
  =head1 DESCRIPTION
  
  The TaglibHelper module is intended to make it much easier to build
  a taglib module than had previously existed. When you create a library
  that uses TaglibHelper, you need only to write "regular" functions that
  take string arguments (optional arguments are supported) and return
  standard Perl data structures like strings and hashrefs.
  
  =head1 FUNCTION SPECIFICATIONS
  
  The @EXPORT_TAGLIB global variable is where you list your exported
  functions. It is of the format:
  
    funcname(arguments)[:options]
  
  The C<<arguments>> section contains arguments of the form:
  
  =over 4
  
  =item $argument
  
  An argument that is expected to be a plain string
  
  =item *argument
  
  An argument that can take a XML tree in hashref form
  
  =item @argument
  
  An argument that is expected to be an array of plain strings or an array
  of hashrefs if the subtag has attributes
  
  =back
  
  These arguments are separated by commas, and optional args are
  separated from required ones by a semicolon. For example,
  C<$field1,$field2;$field3,$field4> has required parameters C<field1>
  and C<field2>, and optional parameters C<field3> and C<field4>.
  
  The options are colon-separated and give extra hints to TaglibHelper in
  places where the default behavior isn't quite what you want. All
  options are key/value pairs, formatted as B<key1=value1:key2=value2>,
  etc. Currently recognized options are:
  
  =over 4
  
  =item listtag
  
  For functions that return arrays, use the indicated wrapper tag for the
  list instead of <funcname>-list
  
  =item itemtag
  
  For functions that return arrays of strings, use the indicated wrapper
  tag for the list items instead of  <funcname>-item
  
  =item forcearray
  
  For functions that always return an array, you should generally set
  this option to "1". the reason is that if your array-returning function
  only returns one value in its array, the result won't be treated as an
  array otherwise.
  
  =item conditional
  
  The function's return value will not be printed, and instead will be
  used to conditionally execute child tags. NOTE that arguments to the
  function cannot be brought in via child tags, but instead must come in
  via attributes.
  
  =item isreally
  
  This function specification is actually an alias for a perl function of
  a different name. For example, a specification of
  C<"person($name):isreally=get_person"> allows you to have a tag <ns:person
  name="Joe"/> that will resolve to Perl code "get_person('Joe')".
  
  =item as_xml
  
  Set this to true and return a well-balanced chunk of XML, and it will be 
  parsed and added to the output.
  
  =item array_uses_hash
  
  Set this to true to use the preceding hash key as the prefix to 
  array tag names. In the situation where complex data structures of 
  hashes pointing to arrays are returned, then this makes the xml output 
  more meaningful. Otherwise the default of the itemtag or <funcname>-item 
  is used.
  
  =back
  
  =head1 EXAMPLE
  
  if you had these two functions:
  
  
    sub hello ($) {
      my ($name) = @_;
      return "Hello, $name!";
    }
  
    sub get_person ($) {
      my ($name) = @_;
      return { 
          person => { 
          name => $name,
          age => 25,
          height => 200,
          }
      }
    }
  
  ...and you called them with this xsp fragment:
  
    <test:hello>
      <test:name>Joe</test:name>
    </test:hello>
  
    <test:get-person name="Bob"/>
  
  ...you would get this XML result:
  
    Hello, Joe!
    <person>
      <height>200</height>
      <age>25</age>
    <name>Bob</name></person>
  
  If your function returned deeper result trees, with hashes containing
  hashrefs or something similar, that would be handled fine. There are some
  limitations with arrays, however, described in the BUGS AND LIMITATIONS
  section.
  
  =head1 STRUCTURED INPUT EXAMPLE
  
  If you wish to send structured data (i.e. not just a scalar) to a taglib
  function, use "*" instead of "$" for a variable. The input to a taglib
  function specified as "insert_person($pid,*extra)" might be:
  
    <test:insert-person pid="123">
    <test:extra>
        <weight>123</weight>
        <friends>    
         <pid>3</pid>
         <pid>5</pid>
         <pid>13</pid>
        </friends>
    </test:extra>
    </test:insert-person>
  
  The function call would be the same as:
  
    insert_function("123", { 
      weight => 123, 
      friends => [ 3, 5, 13 ]
      }
    );
  
  The <friends> container holds repeating tags, notice, and TaglibHelper
  figured out automatically that it needs to use an arrayref instead of
  hashref for the values. But you'll get unexpected results if you mix
  repeating tags and nonrepeating ones:
  
    <test:extra>
      <weight>123</weight>
      <friend>3</friend>
      <friend>5</friend>
      <friend>13</friend>
    </test:extra>
  
  Just wrap your singular repeated tags with a plural-form tag, in this
  case <friends>.
  
  =head1 ARRAY INPUT EXAMPLE
  
  If you wish to send an arbitrary number of values to a taglib function's
  parameter, use "@" instead of "$" for the variable in the EXPORT_TAGLIB
  header array (but still declare it with "$" in the function declaration).
  The parameter will end up turning into an arrayref. For example, you might
  have a TaglibHelper header:
  
    listbox($name;$pretty_name,@option,$default,$multiple,$size,$required)
  
  and a Perl declaration:
  
    sub listbox ($$$$$$$) {
      my ($name, $pretty_name, $options, $default, $multiple, $size, $required) = @_;
  	...
    }
  
  and an XSP file that calls it:
  
    <test:listbox name="country" pretty_name="Pick a Country" default="" required="1">
      <test:option name="Please choose a country" value=""/>
      <test:option name="United States" value="US"/>
      <test:option name="Canada" value="CA"/>
    </test:listbox>
  
  It would turn into this function call:
  
    listbox("country", "Pick a Country", [
      { name => "Please choose a country", value => "" },
  	{ name => "United States", value => "" },
  	{ name => "Canada", value => "CA" },
    ],  "", undef, undef, 1);
  
  Hopefully the example is clear enough.
  
  =head1 BUGS AND LIMITATIONS
  
  Arrays and arrayrefs are generally difficult to work with because the
  items within the array have no keys other than the index value. As a
  result, if you want items within an array to be identified correctly,
  you must currently make all array items point to a hashref that contains
  the item's key or you must use the optional arguments to give TaglibHelper
  enough "hints" to be able to represent the XML tree the way you want.
  
  =head1 AUTHOR
  
  Steve Willer, steve@willer.cc
  
  =head1 SEE ALSO
  
  AxKit.
  
  =cut
  
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/MediaChooser/WAPCheck.pm
  
  Index: WAPCheck.pm
  ===================================================================
  # $Id: WAPCheck.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
  
  package Apache::AxKit::MediaChooser::WAPCheck;
  
  use strict;
  use Apache::Constants;
  
  sub handler {
  	my $r = shift;
  	my $type;
  	
  #	warn "WAP Check on $ENV{HTTP_ACCEPT}\n";
  #	warn " and $ENV{HTTP_USER_AGENT}\n";
  	
  	local $^W;
  	
  	if ($ENV{HTTP_ACCEPT} =~ /vnd.wap.wml/i) {
  		$r->notes('preferred_media', 'handheld');
  	}
  	elsif (substr($ENV{HTTP_USER_AGENT},0,4) =~ 
  			/(
  			Noki
  			| Eric
  			| WapI
  			| MC21 # cough spit hack ;-) (I used to work at Ericsson)
  			| AUR\s
  			| R380
  			| UP.B
  			| WinW
  			| UPG1
  			| upsi
  			| QWAP
  			| Jigs
  			| Java
  			| Alca
  			| MITS
  			| MOT-
  			| My\sS
  			| WAPJ
  			| fetc
  			| ALAV
  			| Wapa
  			)/x) {
  #		warn "set media to handheld!\n";
  		$r->notes('preferred_media', 'handheld');
  	}
  	
  	return OK;
  }
  
  1;
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/Plugin/Fragment.pm
  
  Index: Fragment.pm
  ===================================================================
  # $Id: Fragment.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
  
  package Apache::AxKit::Plugin::Fragment;
  
  use strict;
  
  use Apache::Constants;
  use Apache::AxKit::Language::XPathScript;
  use XML::XPath;
  use XML::XPath::Parser;
  use XML::XPath::XMLParser;
  
  sub handler {
  	my $r = shift;
  	
  #	warn "Fragment handler\n";
  	
  	my $mtime = -M $r->finfo;
  	my $xmlfile = $r->filename;
  	
  	my $qs = $ENV{QUERY_STRING};
  	return DECLINED unless $qs;
  	return DECLINED if ($qs =~ /^\w+=/);
  	
  #	warn "qs: $qs\n";
  	
  	my $source_tree;
  	my $parser = XML::XPath::XMLParser->new();
  	my $xp = XML::XPath->new();
  
  	if (exists($Apache::AxKit::Language::XPathScript::cache->{$xmlfile})
  			&& $Apache::AxKit::Language::XPathScript::cache->{$xmlfile}{mtime} <= $mtime) {
  		$source_tree = $Apache::AxKit::Language::XPathScript::cache->{$xmlfile}{tree};
  	}
  	
  	if (!$source_tree) {
  		eval {
  			$source_tree = $parser->parsefile($xmlfile);
  		};
  		if ($@) {
  			warn "Parse of '$xmlfile' failed: $@";
  			return DECLINED;
  		}
  		
  		$Apache::AxKit::Language::XPathScript::cache->{$xmlfile} =
  			{
  				mtime => $mtime,
  				tree => $source_tree
  			};
  	}
  	
  	my $query = eval {XML::XPath::Parser->new()->parse($qs) };
  	if ($@) {
  		warn "Invalid query '$qs': $@\n";
  		return DECLINED;
  	}
  	
  	my $results = $query->evaluate($source_tree);
  	
  	$r->no_cache(1);
  	$r->notes('nocache', 1);
  	
  	my $toptag = $r->dir_config('XPathFragmentElement') || 'resultset';
  	
  	if ($results->isa('XML::XPath::NodeSet')) {
  #		warn "setting xml_string to a nodeset size: ", $results->size, "\n";
  		$r->pnotes('xml_string', "<$toptag>" . join('', map { $_->toString } $results->get_nodelist) . "</$toptag>");
  	}
  	else {
  #		warn "setting xml_string to a value\n";
  		$r->pnotes('xml_string', "<$toptag>" . $results->value . "</$toptag>");
  	}
  	
  	return OK;
  }
  
  1;
  __END__
  
  =head1 NAME
  
  Apache::AxKit::Plugin::Fragment - Fragment plugin
  
  =head1 SYNOPSIS
  
    AxAddPlugin Apache::AxKit::Plugin::Fragment
  
  =head1 DESCRIPTION
  
  This module provides direct web access to XML fragments, using an
  XPath syntax. By simply providing a querystring containing an
  XPath query, this module will set the XML to be parsed to be
  the XML nodes returned by the query. The nodes will be wrapped in
  either <resultset>...</resultset> or you can specify the outer tag
  using:
  
  	PerlSetVar XPathFragmentElement foo
  
  to wrap it in <foo>...</foo>.
  
  =head1 USAGE
  
  Simply add this module to the plugin list before StyleFinder:
  
  	PerlHandler Apache::AxKit::Plugins::Fragment \
  			AxKit
  
  Then request a URL as follows:
  
  	http://server/myfile.xml?/some/xpath/query
  
  Queries that match the regular expression: ^\w+= are ignored, as are
  any invalid XPath queries.
  
  Note that it's important to write your stylesheet to make use of this
  capability! If you intend to use this Fragment plugin, you can't assume
  that your stylesheet will just magically work. It will have to not make
  assumptions about the XML being passed into it. The apply_templates()
  method of XPathScript is extremely useful here, as is the xpath query
  'name(/child::node())' which identifies the top level element's name.
  Here's how I got around this with my first experiments with this:
  
  	<!-- Main document body -->
  	<% if (findvalue('name(/child::node())') eq 'page') { %>
  		<%= apply_templates('/page/body/section') %>
  	<% } else { %>
  		<%= apply_templates('/') %>
  	<% } %>
  
  Which checks that the top level element is called 'page', otherwise it
  simply does apply_templates() on all the nodes.
  
  =cut
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/Plugin/Passthru.pm
  
  Index: Passthru.pm
  ===================================================================
  # $Id: Passthru.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
  
  package Apache::AxKit::Plugin::Passthru;
  
  use strict;
  use Apache::Constants;
  
  sub handler {
      my $r = shift;
  
      my %in = $r->args();
      if ($in{passthru}) {
          $r->notes('axkit_passthru', 1);
      }
      if ($in{passthru_type}) {
          $r->notes('axkit_passthru_type', 1);
      }
      return DECLINED;
  }
  
  1;
  __END__
  
  =head1 NAME
  
  Apache::AxKit::Plugin::Passthru - allow passthru=1 in querystring
  
  =head1 SYNOPSIS
  
    AxAddPlugin Apache::AxKit::Plugin::Passthru
  
  =head1 DESCRIPTION
  
  This module allows AxKit to pass through raw XML without processing
  if the request contained the option C<passthru=1> in the querystring.
  
  Simply by referencing your xml files as follows:
  
  	http://xml.server.com/myfile.xml?passthru=1
  
  You will recieve the raw XML in myfile.xml, rather than it being
  pre-processed by AxKit.
  
  This module is also an example of how this can be done, should you
  wish to build your own passthru type module that makes the decision
  to pass through based on some other parameter, such as the user agent
  in use.
  
  A second function of this module is to allow the content-type of
  the requested file to be passed through unchanged. AxKit's default
  output content-type is "text/html; charset=utf-8". By enabling
  this plugin and requesting a file as:
  
      http://xml.server.com/myfile.xml?passthru_type=1
  
  Then the file's content type (as set by the Apache AddType option),
  will be used rather than any values set during the processing of the
  file.
  
  =cut
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/Plugin/QueryStringCache.pm
  
  Index: QueryStringCache.pm
  ===================================================================
  # $Id: QueryStringCache.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
  
  package Apache::AxKit::Plugin::QueryStringCache;
  use strict;
  
  use Apache::Constants qw(OK);
  
  sub handler {
      my $r = shift;
      $r->notes('axkit_cache_extra', $r->notes('axkit_cache_extra') . $r->args);
      return OK;
  }
  
  1;
  __END__
  
  =head1 NAME
  
  Apache::AxKit::Plugin::QueryStringCache - Cache based on QS
  
  =head1 SYNOPSIS
  
    SetHandler axkit
    AxAddPlugin Apache::AxKit::Plugin::QueryStringCache
  
  =head1 DESCRIPTION
  
  By default AxKit does not change it's cache file with a change
  in querystring, which can lead to some pretty unexpected behaviour
  (and also a number of frequently asked questions). In order
  to get around this, use this plugin.
  
  =cut
  
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/Provider/File.pm
  
  Index: File.pm
  ===================================================================
  # $Id: File.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
  
  package Apache::AxKit::Provider::File;
  use strict;
  use vars qw/@ISA/;
  @ISA = ('Apache::AxKit::Provider');
  
  use Apache;
  use Apache::Log;
  use Apache::Constants qw(HTTP_OK);
  use Apache::AxKit::Exception;
  use Apache::AxKit::Provider;
  use Apache::MimeXML;
  use AxKit;
  use File::Basename;
  use Fcntl qw(O_RDONLY LOCK_SH);
  
  sub init {
      my $self = shift;
      my (%p) = @_;
      
      my $stats_done;
      if ($p{key}) {
          $self->{file} = $p{key};
      }
      else {
          
          if ($p{uri} and $p{uri} =~ s|^file:(//)?||) {
              $p{file} = delete $p{uri};
          }
          
          if ($p{uri}) {
              my $r = $p{rel} ? $p{rel}->apache_request() : $self->apache_request();
              
              AxKit::Debug(8, "[uri] File Provider looking up" . ($p{rel} ? " relative" : "") . " uri $p{uri}");
      
              $self->{apache} = $r->lookup_uri($p{uri});
              my $status = $self->{apache}->status();
              if ($status != HTTP_OK) {
                  throw Apache::AxKit::Exception::Error(-text => "Subrequest failed with status: " . $status);
              }
              $self->{file} = $self->{apache}->filename();
              
              AxKit::Debug(8, "[uri] File Provider set filename to $self->{file}");
          }
          elsif ($p{file}) {
              if ($p{rel} && $p{file} !~ /^\//) {
                  my $file = $p{rel}->apache_request->filename();
                  my $dir = File::Basename::dirname($file);
                  require File::Spec;
                  $self->{file} = File::Spec->rel2abs($p{file}, $dir);
                  AxKit::Debug(8, "[file] File Provider set filename to $self->{file}");
              }
              else {
                  $self->{file} = $p{file};
              }
          }
          else {
              $self->{file} = $self->{apache}->filename();
              my @stats = stat($self->{apache}->finfo());
              $self->{mtime} = $stats[9];
              if (-e _) {
                  if (-r _ ) {
                      $self->{file_exists} = 1;
                  }
  
                  if (-d _) {
                      $self->{is_dir} = 1;
                  }
                  else {
                      $self->{is_dir} = 0;
                  }
              }
              $stats_done++;
          }
      }
      
      if (!$stats_done) {
          my @stats = stat($self->{file});
          $self->{mtime} = $stats[9];
          if (-e _) {
              if (-r _ ) {
                  $self->{file_exists} = 1;
              }
  
              if (-d _) {
                  $self->{is_dir} = 1;
              }
              else {
                  $self->{is_dir} = 0;
              }
          }
      }
  }
  
  sub _is_dir {
      my $self = shift;
      return $self->{is_dir} if exists $self->{is_dir};
      return -d $self->{file};
  }
  
  sub key {
      my $self = shift;
      return $self->{file};
  }
  
  sub exists {
      my $self = shift;
      return $self->{file_exists} if exists $self->{file_exists};
      if (-e $self->{file}) {
          if (-r _ ) {
              $self->{file_exists} = 1;
              return 1;
          }
          else {
              AxKit::Debug(2, "'$self->{file}' not readable");
              return;
          }
      }
      return;
  }
  
  sub process {
      my $self = shift;
      
      my $xmlfile = $self->{file};
      
      unless ($self->exists()) {
          AxKit::Debug(5, "file '$xmlfile' does not exist or is not readable");
          return 0;
      }
      
      if ($self->_is_dir) {
          AxKit::Debug(5, "'$xmlfile' is a directory");
          return 0;
      }
      
      local $^W;
      if (($xmlfile =~ /\.xml$/i) ||
          ($self->{apache}->content_type() =~ /^(text|application)\/xml/) ||
          $self->{apache}->pnotes('xml_string') ||
          Apache::MimeXML::check_for_xml(eval {$self->get_fh} || ${ $self->get_strref } )
          ) {
              # chdir(dirname($xmlfile));
              return 1;
      }
      
      AxKit::Debug(5, "'$xmlfile' not recognised as XML");
      return 0;
  }
  
  sub mtime {
      my $self = shift;
      return $self->{mtime} if exists $self->{mtime};
      return ($self->{mtime} = (stat($self->{file}))[9]);
  }
  
  sub get_fh {
      my $self = shift;
      if (!$self->exists()) {
          throw Apache::AxKit::Exception::IO(-text => "File '$self->{file}' does not exist or is not readable");
      }
      my $filename = $self->{file};
      # chdir(dirname($filename));
      my $fh = Apache->gensym();
      if (sysopen($fh, $filename, O_RDONLY)) {
          # seek($fh, 0, 0);
          return $fh;
      }
      throw Apache::AxKit::Exception::IO( -text => "Can't open '$self->{file}': $!" );
  }
  
  sub get_strref {
      my $self = shift;
      my $fh = $self->get_fh();
      local $/;
      my $contents = <$fh>;
      return \$contents
  }
  
  1;
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/Provider/Filter.pm
  
  Index: Filter.pm
  ===================================================================
  # $Id: Filter.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
  
  package Apache::AxKit::Provider::Filter;
  use strict;
  use vars qw/@ISA/;
  @ISA = ('Apache::AxKit::Provider::File');
  
  # Provider for Apache::Filter
  use Apache;
  use Apache::Log;
  use Apache::AxKit::Exception;
  use Apache::AxKit::Provider;
  use Apache::AxKit::Provider::File;
  use AxKit;
  use Apache::Constants;
  use File::Basename;
  
  # copied mostly from File provider...
  sub init {
      my $self = shift;
      my (%p) = @_;
      
      if ($p{key} || $p{uri} || $p{file}) {
          return $self->SUPER::init(%p);
      }
      else {
          $self->{file} = $self->{apache}->filename();
          my ($fh, $status) = Apache->filter_register->filter_input();
          throw Apache::AxKit::Exception::Error(
                  -text => "Bad filter_input status"
                  ) unless $status == OK;
          $self->{filter_data} = join('', <$fh>);
      }
  }
  
  sub get_fh {
      my $self = shift;
      if ($self->{filter_data}) {
          throw Apache::AxKit::Exception::IO( 
                  -text => "Can't get fh for Filter filehandle"
                  );
      }
      return $self->SUPER::get_fh();
  }
  
  sub get_strref {
      my $self = shift;
      if (exists $self->{filter_data}) {
          my $data = $self->{filter_data};
          return \$data;
      }
      return $self->SUPER::get_strref();
  }
  
  sub process {
      my $self = shift;
      
      my $xmlfile = $self->{file};
  
      local $^W;
      # always process this resource.
      chdir(dirname($xmlfile));
      return 1;
  }
  
  sub exists {
      my $self = shift;
      return 1 if $self->{filter_data};
      return $self->SUPER::exists();
  }
  
  sub has_changed () { 1; }
  
  sub mtime {
      my $self = shift;
      return time(); # always fresh
  }
  
  1;
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/Provider/Scalar.pm
  
  Index: Scalar.pm
  ===================================================================
  # $Id: Scalar.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
  
  package Apache::AxKit::Provider::Scalar;
  use strict;
  use vars qw/@ISA/;
  @ISA = ('Apache::AxKit::Provider');
  
  use Apache;
  use Apache::Log;
  use Apache::AxKit::Exception;
  use Apache::AxKit::Provider;
  use AxKit;
  
  sub new {
      my $class = shift;
      my $apache = shift;
      my $self = bless { apache => $apache }, $class;
      
      eval { $self->init(@_) };
      
      return $self;
  }
  
  sub apache_request {
      my $self = shift;
      return $self->{apache};
  }
  
  sub init {
      my $self = shift;
      $self->{data} = $_[0];
      $self->{styles} = $_[1];
      
  #    warn "Scalar Provider constructed with: $self->{data}\n";
  }
  
  sub process {
      my $self = shift;
      return 1;
  }
  
  sub exists {
      my $self = shift;
      return 1;
  }
  
  sub mtime {
      my $self = shift;
      return time(); # always fresh
  }
  
  sub get_fh {
      throw Apache::AxKit::Exception::IO( -text => "Can't get fh for Scalar" );
  }
  
  sub get_strref {
      my $self = shift;
      return \$self->{data};
  }
  
  sub key {
      my $self = shift;
      return 'scalar_provider';
  }
  
  sub get_styles {
      my $self = shift;
      return $self->{styles}, [];
  }
  
  1;
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/StyleChooser/Cookie.pm
  
  Index: Cookie.pm
  ===================================================================
  # $Id: Cookie.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
  
  package Apache::AxKit::StyleChooser::Cookie;
  
  use strict;
  use Apache::Constants;
  use Apache::Cookie;
  
  use vars qw($VERSION);
  
  $VERSION = '0.01';
  
  sub handler {
      my $r = shift;
      my $oreo = Apache::Cookie->fetch; # if dougm can call a cookie method "bake". . .
      if ( defined $oreo->{'axkit_preferred_style'} ) {
          $r->notes('preferred_style', $oreo->{'axkit_preferred_style'}->value);
      }
  
      return DECLINED;
  }
  
  1;
  __END__
  
  =head1 NAME
  
  Apache::AxKit::StyleChooser::Cookie - Choose stylesheets based on a browser cookie
  
  =head1 SYNOPSIS
  
      PerlHandler Apache::AxKit::StyleChooser::Cookie \
                  AxKit
  
  =head1 DESCRIPTION
  
  This module checks for the presence of a cookie named
  'axkit_preferred_style' and sets the preferred style accordingly.
  
  Remember, use the B<title> attribute in your stylesheet PI to define a
  matching style.
  
  =head1 AUTHOR
  
  Kip Hampton, kip@hampton.ws
  
  =head1 SEE ALSO
  
  AxKit.
  
  =cut
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/StyleChooser/FileSuffix.pm
  
  Index: FileSuffix.pm
  ===================================================================
  # $Id: FileSuffix.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
  
  package Apache::AxKit::StyleChooser::FileSuffix;
  
  use strict;
  use Apache::Constants;
  use Apache::URI;
  
  sub handler {
  	my $r = shift;
  	
  	my $file = $r->filename();
  	my($style) = ($file =~ m/\.(\w+?)$/);
  
  	if ($style && $file =~ s/\.\w+?$// && -e $file) {
  		$r->filename($file);
  #		warn "setting notes: $style\n";
  		$r->notes('preferred_style', $style);
  	}
  	return DECLINED;
  }
  
  1;
  __END__
  
  =head1 NAME
  
  Apache::AxKit::StyleChooser::FileSuffix - Choose stylesheet using file suffix
  
  =head1 SYNOPSIS
  
  	PerlTypeHandler Apache::AxKit::StyleChooser::FileSuffix
  	SetHandler perl-script
  	PerlHandler AxKit
  
  =head1 DESCRIPTION
  
  This module lets you pick a stylesheet based on the filename suffix. To use
  it, simply add this module to the list of PerlTypeHandlers:
  
  	PerlTypeHandler Apache::AxKit::StyleChooser::FileSuffix
  	SetHandler perl-script
  	PerlHandler AxKit
  
  Then simply by referencing your xml files as follows:
  
  	http://xml.server.com/myfile.xml.printable
  
  You will recieve the alternate stylesheets with title "printable". See 
  the HTML 4.0 specification for more details on stylesheet choice.
  
  Note that this installation is different from the other StyleChoosers,
  because the file we're requesting here doesn't actually exist.
  
  Thanks to Salve J. Nilsen <sj...@foo.no> for this module.
  
  =cut
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/StyleChooser/PathInfo.pm
  
  Index: PathInfo.pm
  ===================================================================
  # $Id: PathInfo.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
  
  package Apache::AxKit::StyleChooser::PathInfo;
  
  use strict;
  use Apache::Constants;
  use Apache::URI;
  
  sub handler {
  	my $r = shift;
  	
  	my $style = $r->path_info();
  
  	if ($style && $style ne '/') {
  		$r->path_info('');
  
  		my $uri = $r->uri();
  
  		$uri =~ s/\Q$style\E$//;
  		
  		$r->uri($uri);
  		
  		my $uri2 = Apache::URI->parse($r);
  		
  		$r->header_out('Content-Base', $uri2->unparse);
  		$r->header_out('Content-Location', $uri2->unparse);
  	
  		$style =~ s/^\///;
  #		warn "setting notes: $style\n";
  		$r->notes('preferred_style', $style);
  	}
  	return DECLINED;
  }
  
  1;
  __END__
  
  =head1 NAME
  
  Apache::AxKit::StyleChooser::PathInfo - Choose stylesheet using PATH_INFO
  
  =head1 SYNOPSIS
  
  	PerlHandler Apache::AxKit::StyleChooser::PathInfo \
  			AxKit
  
  =head1 DESCRIPTION
  
  This module lets you pick a stylesheet based on the extra PATH_INFO. To use
  it, simply add this module to the list of PerlHandlers prior to
  AxKit:
  
  	PerlHandler Apache::AxKit::StyleChooser::PathInfo \
  			AxKit
  
  Then simply by referencing your xml files as follows:
  
  	http://xml.server.com/myfile.xml/printable
  
  You will recieve the alternate stylesheets with title "printable". See 
  the HTML 4.0 specification for more details on stylesheet choice.
  
  =cut
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/StyleChooser/QueryString.pm
  
  Index: QueryString.pm
  ===================================================================
  # $Id: QueryString.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
  
  package Apache::AxKit::StyleChooser::QueryString;
  
  use strict;
  use Apache::Constants;
  
  sub handler {
  	my $r = shift;
  	
  	my %in = $r->args();
  	if ($in{style}) {
  		$r->notes('preferred_style', $in{style});
  	}
  	return DECLINED;
  }
  
  1;
  __END__
  
  =head1 NAME
  
  Apache::AxKit::StyleChooser::QueryString - Choose stylesheet using querystring
  
  =head1 SYNOPSIS
  
  	PerlHandler Apache::AxKit::StyleChooser::QueryString \
  			AxKit
  
  =head1 DESCRIPTION
  
  This module lets you pick a stylesheet based on the querystring. To use
  it, simply add this module to the list of PerlHandlers prior to
  the main AxKit handler:
  
  	PerlHandler Apache::AxKit::StyleChooser::QueryString \
  			AxKit
  
  Then simply by referencing your xml files as follows:
  
  	http://xml.server.com/myfile.xml?style=printable
  
  You will recieve the alternate stylesheets with title "printable". See 
  the HTML 4.0 specification for more details on stylesheet choice.
  
  =cut
  
  
  
  1.1                  xml-axkit/lib/Apache/AxKit/StyleChooser/UserAgent.pm
  
  Index: UserAgent.pm
  ===================================================================
  # $Id: UserAgent.pm,v 1.1 2002/01/13 20:45:12 matts Exp $
  
  package Apache::AxKit::StyleChooser::UserAgent;
  
  use strict;
  use vars qw($VERSION);
  
  $VERSION = '0.01';
  
  use Apache::Constants;
  
  sub handler {
      my $r = shift;
      my @UAMap;
      my @aoh = split /\s*,\s*/, $r->dir_config('AxUAStyleMap');
      foreach (@aoh) {
          push (@UAMap, [ split /\s*=>\s*/, $_ ]);
      }
  
  #    warn "checking UA: $ENV{HTTP_USER_AGENT}\n";
  
      UA: foreach my $ua (@UAMap) {
          if ($ENV{HTTP_USER_AGENT} =~ /$ua->[1]/g) {
  #            warn "found UA $ua->[1], setting 'preferred_style' to $ua->[0]\n";
              $r->notes('preferred_style', $ua->[0]);
              last UA;
          }
      }
  
      return DECLINED;
  }
  
  1;
  __END__
  
  =head1 NAME
  
  Apache::AxKit::StyleChooser::UserAgent - Choose stylesheets based on the user agent.
  
  =head1 SYNOPSIS
  
      In your .conf or .htaccess file(s):
      
      PerlHandler Apache::AxKit::StyleChooser::UserAgent \
                  AxKit
  
      PerlSetVar AxUAStyleMap "lynx     => Lynx,\
                               explorer => MSIE,\
                               opera    => Opera,\
                               netscape => Mozilla"
  
  =head1 DESCRIPTION
  
  This module sets the internal preferred style based on the user agent
  string presented by the connecting client.
  
  Remember, use the B<title> attribute in your stylesheet PI to define a
  matching style.
  
  =head1 AUTHOR
  
  Kip Hampton, khampton@totalcinema.com
  
  =head1 SEE ALSO
  
  AxKit.
  
  =cut
  
  
  
  1.1                  xml-axkit/t/00basic.t
  
  Index: 00basic.t
  ===================================================================
  use Test;
  BEGIN { plan tests => 2 }
  END { ok($loaded) }
  
  use AxKit;
  $loaded++;
  ok(1);
  
  
  
  1.1                  xml-axkit/t/01provider.t
  
  Index: 01provider.t
  ===================================================================
  use Test;
  BEGIN { plan tests => 4 }
  
  require "t/test_module.pl";
  
  test_module("Apache::AxKit::Provider");
  
  test_module("Apache::AxKit::Provider::File");
  
  test_module("Apache::AxKit::Provider::Scalar");
  
  test_module("Apache::AxKit::Provider::Filter", "Apache::Filter");
  
  
  
  1.1                  xml-axkit/t/02language.t
  
  Index: 02language.t
  ===================================================================
  use Test;
  BEGIN { plan tests => 9 }
  
  require "t/test_module.pl";
  
  test_module("Apache::AxKit::Language::XPathScript", "XML::XPath");
  
  test_module("Apache::AxKit::Language::AxPoint", "PDFLib");
  
  test_module("Apache::AxKit::Language::Sablot", "XML::Sablotron");
  
  test_module("Apache::AxKit::Language::LibXSLT", "XML::LibXSLT");
  
  test_module("Apache::AxKit::Language::XMLNewsRDF", "XMLNews::HTMLTemplate");
  
  test_module("Apache::AxKit::Language::XMLNewsNITF", "XMLNews::HTMLTemplate");
  
  test_module("Apache::AxKit::Language::XSP", "XML::XPath");
  
  test_module("Apache::AxKit::Language::XSP::TaglibHelper", "XML::XPath");
  
  test_module("Apache::AxKit::Language::PassiveTeX");
  
  
  
  1.1                  xml-axkit/t/03stylechooser.t
  
  Index: 03stylechooser.t
  ===================================================================
  use Test;
  BEGIN { plan tests => 5 }
  
  require "t/test_module.pl";
  
  test_module("Apache::AxKit::StyleChooser::FileSuffix");
  
  test_module("Apache::AxKit::StyleChooser::Cookie");
  
  test_module("Apache::AxKit::StyleChooser::PathInfo");
  
  test_module("Apache::AxKit::StyleChooser::QueryString");
  
  test_module("Apache::AxKit::StyleChooser::UserAgent");
  
  
  
  1.1                  xml-axkit/t/04plugins.t
  
  Index: 04plugins.t
  ===================================================================
  use Test;
  BEGIN { plan tests => 3 }
  
  require "t/test_module.pl";
  
  test_module("Apache::AxKit::Plugins::Fragment", "XML::XPath");
  
  test_module("Apache::AxKit::Plugins::Passthru");
  
  test_module("Apache::AxKit::Plugins::QueryStringCache");
  
  
  
  1.1                  xml-axkit/t/05mediachooser.t
  
  Index: 05mediachooser.t
  ===================================================================
  use Test;
  BEGIN { plan tests => 1 }
  
  require "t/test_module.pl";
  
  test_module("Apache::AxKit::MediaChooser::WAPCheck");
  
  
  
  1.1                  xml-axkit/t/test_module.pl
  
  Index: test_module.pl
  ===================================================================
  sub test_module {
      my ($module, @prereqs) = @_;
      foreach my $prereq (@prereqs) {
          my ($ok, $reason) = load_module($prereq);
          if (!$ok) {
              skip("Skip($prereq prerequisite module not available : $reason)", 1);
              return;
          }
      }
      my ($ok, $reason) = load_module($module);
      ok($ok, 1, $reason);
  }
  
  sub load_module {
      my $module = shift;
      $module =~ s/::/\//g;
      eval {
          require "$module.pm";
      };
      if ($@) {
          return wantarray ? (0, $@) : 0;
      }
      return 1;
  }
  
  1;