Hi AxKittens!

I've written a patch to AxKit to tidy up AxHandleDirs and makes the directory handler process pluggable, so different XML grammars can be used, depending upon an implementor's needs.

This adds a new configuration directive "AxDirHandler <Module>" which is used to indicate the desired directory handler plugin, but AxHandleDirs On|Off is still required to toggle if directory listings will be handled. If AxDirHandler is not specified, the default module Apache::AxKit::DirHandler::Basic is used (this is essentially a copy-and-paste job from the current directory handler, so backwards-compatibility is assured).

Even if you don't use any of the additional directory handler plugins, this patch is still desirable because it supplies an mtime() for the directory (which is the greatest mtime value of any file in the directory). This way we can cache the directory results, and any XSL transform applied to it.

I've created one directory plugin that I think is pretty sexy: Apache::AxKit::DirHandler::RDF. This spits out an RDF description of a directory's contents. By itself this isn't too fancy - very useful, but not too fancy - but it can take additional configuration directives (in the form of PerlSetVar AxDirHandler*):

AxDirHandlerRecurse On|Off   -- Recurses through subdirectories

AxDirHandlerInspect {Images,MimeType} -- This is similar to AxAddXSLParams, and allows you to add mime-type information and image metadata to appropriate files.

Further to this, the A::A::DirHandler::RDF module can be inherited to add additional file inspection options.

If you want to see a real-live demo of this, go to http://devbox.nachbaur.com (though I may be styling the output at sometime in the future since this is, obviously, a development machine).

For more information on how it works, see the POD documentation within the patch. BTW, I'm thinking it would make sense for Apache::AxKit::DirHandler::RDF to be a separate CPAN module, so we don't add YA set of module dependencies (namely File::MimeInfo and Image::Info) to AxKit.
diff -Nurw --exclude=CVS xml-axkit.orig/axconfig.c xml-axkit/axconfig.c
--- xml-axkit.orig/axconfig.c   2003-07-07 15:29:50.000000000 -0700
+++ xml-axkit/axconfig.c        2004-02-20 14:19:09.000000000 -0800
@@ -104,6 +104,7 @@
     new->config_reader_module = 0;
     new->contentprovider_module = 0;
     new->styleprovider_module = 0;
+    new->dir_handler_module = 0;
     new->default_style = 0;
     new->default_media = 0;
     new->cache_module = 0;
@@ -429,6 +430,13 @@
         new->trace_intermediate = ap_pstrdup(p, 
parent_dir->trace_intermediate);
     }
 
+    if (subdir->dir_handler_module) {
+        new->dir_handler_module = ap_pstrdup(p, subdir->dir_handler_module);
+    }
+    else if (parent_dir->dir_handler_module) {
+        new->dir_handler_module = ap_pstrdup(p, 
parent_dir->dir_handler_module);
+    }
+
     new->debug_tidy =
         subdir->debug_tidy != -1 ? subdir->debug_tidy :
                                      parent_dir->debug_tidy;
@@ -1338,7 +1346,12 @@
     { "AxHandleDirs", ap_set_flag_slot,
       (void *)XtOffsetOf(axkit_dir_config, handle_dirs),
       OR_ALL, FLAG,
-      "On or Off [default] to make AxKit process directory requests using 
XML::Directory" },
+      "On or Off [default] to make AxKit produce XML for directory requests" },
+
+    { "AxDirHandler", ax_set_module_slot,
+      (void *)XtOffsetOf(axkit_dir_config, dir_handler_module),
+      OR_ALL, TAKE1,
+      "alternative module to use for generating directory xml" },
 
     { "AxIgnoreStylePI", ap_set_flag_slot,
       (void *)XtOffsetOf(axkit_dir_config, ignore_style_pi),
@@ -1460,6 +1473,10 @@
         hv_store(retval, "HandleDirs",
                 10, (newSViv(cfg->handle_dirs)), 0);
     }
+    if (cfg->dir_handler_module) {
+        hv_store(retval, "DirHandler",
+                10, (newSVpv(cfg->dir_handler_module, 0)), 0);
+    }
 
     if (cfg->dependency_checks != -1) {
         hv_store(retval, "DependencyChecks",
diff -Nurw --exclude=CVS xml-axkit.orig/axconfig.h xml-axkit/axconfig.h
--- xml-axkit.orig/axconfig.h   2002-06-27 18:26:01.000000000 -0700
+++ xml-axkit/axconfig.h        2004-02-20 13:31:33.000000000 -0800
@@ -43,6 +43,7 @@
     char * cache_module;
     char * output_charset;
     char * trace_intermediate;
+    char * dir_handler_module;
     int    debug_tidy;
     int    debug_level;
     int    translate_output;
diff -Nurw --exclude=CVS xml-axkit.orig/lib/Apache/AxKit/ConfigReader.pm 
xml-axkit/lib/Apache/AxKit/ConfigReader.pm
--- xml-axkit.orig/lib/Apache/AxKit/ConfigReader.pm     2003-08-04 
16:05:17.000000000 -0700
+++ xml-axkit/lib/Apache/AxKit/ConfigReader.pm  2004-02-20 13:40:54.000000000 
-0800
@@ -193,6 +193,18 @@
             0;
 }
 
+sub DirHandlerClass {
+    my $self = shift;
+    if (my $alternate = $self->{cfg}{DirHandler} || 
+            $self->{apache}->dir_config('AxDirHandler')) {
+        AxKit::load_module($alternate);
+        return $alternate;
+    }
+    
+    AxKit::load_module('Apache::AxKit::DirHandler::Basic');
+    return 'Apache::AxKit::DirHandler::Basic';
+}
+
 sub IgnoreStylePI {
     my $self = shift;
     return $self->{cfg}{IgnoreStylePI} ||
diff -Nurw --exclude=CVS xml-axkit.orig/lib/Apache/AxKit/DirHandler/Basic.pm 
xml-axkit/lib/Apache/AxKit/DirHandler/Basic.pm
--- xml-axkit.orig/lib/Apache/AxKit/DirHandler/Basic.pm 1969-12-31 
16:00:00.000000000 -0800
+++ xml-axkit/lib/Apache/AxKit/DirHandler/Basic.pm      2004-02-20 
16:47:46.000000000 -0800
@@ -0,0 +1,88 @@
+# $Id$
+
+package Apache::AxKit::DirHandler::Basic;
+use strict;
+use vars qw/@ISA/;
[EMAIL PROTECTED] = ('Apache::AxKit::DirHandler');
+
+use Apache;
+use Apache::Log;
+use AxKit;
+use Apache::AxKit::DirHandler;
+use File::Spec;
+use Fcntl qw(O_RDONLY LOCK_SH);
+
+sub get_strref {
+    my $self = shift;
+    local (*DIR);
+    my $dir = $self->{directory};
+    if (opendir(DIR, $dir)) {
+        my $output = '<?xml version="1.0" encoding="UTF-8"?>
+<filelist xmlns="http://axkit.org/2002/filelist";>
+';
+        while(my $line = readdir(DIR)) {
+            my $xmlline = AxKit::ToUTF8($line);
+            $xmlline =~ s/&/&amp;/;
+            $xmlline =~ s/</&lt;/;
+            my @stat = stat(File::Spec->catfile($dir,$line));
+            my $attr = "size=\"$stat[7]\" atime=\"$stat[8]\" 
mtime=\"$stat[9]\" ctime=\"$stat[10]\"";
+            $attr .= ' readable="1"' if (-r _);
+            $attr .= ' writable="1"' if (-w _);
+            $attr .= ' executable="1"' if (-x _);
+            
+            if (-f _) {
+                $output .= "<file $attr>$xmlline</file>\n";
+            } elsif (-d _) {
+                $output .= "<directory $attr>$xmlline</directory>\n";
+            } else {
+                $output .= "<unknown $attr>$xmlline</unknown>\n";
+            }
+        }
+        $output .= "</filelist>\n";
+        return \$output;
+    }
+    return undef;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Apache::AxKit::DirHandler::Basic - directory handler implementing a simple XML 
output
+
+=head1 SYNOPSIS
+
+Override the base DirHandler class and enable it by using:
+
+    AxHandleDirs on
+    AxDirHandler Apache::AxKit::DirHandler::Basic
+
+    # or
+
+    AxHandleDirs on
+    # This module is the default
+
+=head1 DESCRIPTION
+
+This module is the default Directory Handler when no handler is explicitly
+specified.  It is intended to be simple to use and easy to get started with.
+
+=head1 SAMPLE OUTPUT
+
+  <?xml version="1.0" encoding="UTF-8"?>
+  <filelist xmlns="http://axkit.org/2002/filelist";>
+    <directory size="4096" atime="1077320634" mtime="1077312841" 
ctime="1077312841" readable="1" executable="1">.</directory>
+    <directory size="4096" atime="1077316522" mtime="1076743711" 
ctime="1076743711" readable="1" executable="1">..</directory>
+    <file size="0" atime="1076770889" mtime="1076770889" ctime="1076770889" 
readable="1">index.xml</file>
+    <directory size="4096" atime="1076954718" mtime="1076774280" 
ctime="1076774280" readable="1" executable="1">images</directory>
+    <file size="0" atime="1077320634" mtime="1076770894" ctime="1076770894" 
readable="1">.htaccess</file>
+    <file size="580626" atime="1077319951" mtime="1076774007" 
ctime="1076774007" readable="1">link-4.1a.tar.gz</file>
+    <file size="708" atime="1077319951" mtime="1076774018" ctime="1076774018" 
readable="1" executable="1">Bender.pl</file>
+  </filelist>
+
+=head1 SEE ALSO
+
+L<Apache::AxKit::DirHandler>
+
+=cut
diff -Nurw --exclude=CVS xml-axkit.orig/lib/Apache/AxKit/DirHandler/RDF.pm 
xml-axkit/lib/Apache/AxKit/DirHandler/RDF.pm
--- xml-axkit.orig/lib/Apache/AxKit/DirHandler/RDF.pm   1969-12-31 
16:00:00.000000000 -0800
+++ xml-axkit/lib/Apache/AxKit/DirHandler/RDF.pm        2004-02-21 
10:18:57.000000000 -0800
@@ -0,0 +1,366 @@
+# $Id$
+
+package Apache::AxKit::DirHandler::RDF;
+use strict;
+use vars qw/@ISA/;
[EMAIL PROTECTED] = ('Apache::AxKit::DirHandler');
+
+use Apache;
+use Apache::Log;
+use Apache::Util;
+use AxKit;
+use Apache::AxKit::DirHandler;
+use Image::Info qw(image_info);
+use File::MimeInfo::Magic;
+use File::Spec;
+use File::stat;
+use File::Basename;
+use Fcntl qw(O_RDONLY LOCK_SH);
+
+#
+# override the default init so we can pull out
+# our configuration options
+sub init {
+    my $self = shift;
+    my $r = $self->{provider}->apache_request;
+
+    # fetch configuration about whether or not we should recurse through
+    # the directory tree.  Set this as an object property.
+    my $recurse = lc($r->dir_config('AxDirHandlerRecurse'));
+    print STDERR "Recursing dirconfig value is '$recurse'\n";
+    if ($recurse eq '1' or $recurse =~ /(yes|on)/) {
+        $self->{recurse} = 1;
+    } else {
+        $self->{recurse} = 0;
+    }
+
+    # fetch configuration listing what file inspection options should
+    # be enabled in the RDF.
+    my $rdfopts = lc($r->dir_config('AxDirHandlerInspect'));
+    $self->{inspect} = {};
+    foreach my $opt (split(/\s+/, $rdfopts)) {
+        next unless ($opt =~ /^\w+$/);
+        if ($self->can("inspect_$opt")) {
+            $self->{inspect}->{$opt} = "inspect_$opt";
+        }
+    }
+}
+
+#
+# handle directory requests
+sub get_strref {
+    my $self = shift;
+    my $path = shift;
+    local (*DIR);
+    my $dir = $path ? File::Spec->catfile($self->{directory}, $path)
+                    : $self->{directory};
+    print STDERR "Processing directory '$dir'\n";
+    if (opendir(DIR, $dir)) {
+        my $output = '';
+        unless ($path) {
+            # Only output the RDF tag if we're just starting out; this is
+            # to accomodate recursive lookups
+            $output = qq{<?xml version="1.0" encoding="UTF-8"?>\n} .
+            qq{<rdf:RDF 
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"; } .
+            qq{xmlns:nc="http://home.netscape.com/NC-rdf"; } .
+            qq{xmlns:image="http://axkit.org/2004/Image"; } .
+            qq{xmlns:fs="http://axkit.org/2004/FileSystem";>\n};
+        }
+        while(my $file = readdir(DIR)) {
+            # Skip this file if we're recursing, and this is one of
+            # the "." or ".." directories
+            next if ($self->{recurse} and $path and -d _ and $file =~ 
/^\.{1,2}$/);
+            my $filepath = $path ? "$path/$file" : $file;
+
+            # XML-ize the filename, and stat it
+            print STDERR "Processing file '" . File::Spec->catfile($dir,$file) 
. "'\n";
+            my $xmlfile = AxKit::ToUTF8($filepath);
+            $xmlfile =~ s/&/&amp;/;
+            $xmlfile =~ s/</&lt;/;
+            my $stat = stat(File::Spec->catfile($dir,$file));
+            
+            # Skip this file if we can't figure out a URI for it.  This
+            # shouldn't happen, but you never can tell...
+            my ($uri, $uri_str) = $self->_request_uri($xmlfile);
+            next unless (defined($uri));
+
+            # Start spitting out relevant metadata
+            my $full_path = $self->{provider}->apache_request->document_root . 
$uri->path;
+            $output .= qq{  <rdf:Description rdf:about="$uri_str">\n};
+            $output .= qq{    <nc:Name>$xmlfile</nc:Name>\n};
+            $output .= qq{    <nc:URL>$uri_str</nc:URL>\n};
+            $output .= qq{    <fs:Filename>$xmlfile</fs:Filename>\n};
+            $output .= qq{    <fs:Path>} . $uri->path . qq{</fs:Path>\n};
+            $output .= qq{    <fs:Directory>} . dirname($uri->path) . 
qq{</fs:Directory>\n};
+            $output .= qq{    <fs:FullPath>} . $full_path . 
qq{</fs:FullPath>\n};
+            $output .= qq{    <fs:LastModified>} . 
Apache::Util::ht_time($stat->mtime) . qq{</fs:LastModified>\n};
+            $output .= qq{    <fs:LastAccessed>} . 
Apache::Util::ht_time($stat->atime) . qq{</fs:LastAccessed>\n};
+            $output .= qq{    <fs:ByteSize>} . $stat->size . 
qq{</fs:ByteSize>\n};
+            $output .= qq{    <fs:BlockSize>} . $stat->blocks . 
qq{</fs:BlockSize>\n};
+            $output .= qq{    <fs:FormattedSize>} . 
$self->_format_filesize($stat->size) . qq{</fs:FormattedSize>\n};
+
+            # Tell the client what kind of inode this is
+            if (-f _) {
+                $output .= qq{    <fs:InodeType>file</fs:InodeType>\n};
+            } elsif (-d _) {
+                $output .= qq{    <fs:InodeType>directory</fs:InodeType>\n};
+            } else {
+                $output .= qq{    <fs:InodeType>unknown</fs:InodeType>\n};
+            }
+
+            # If enabled, handle any additional file inspection options
+            foreach my $opt (values(%{$self->{inspect}})) {
+                $output .= $self->$opt($full_path);
+            }
+            $output .= qq{  </rdf:Description>\n};
+
+            # If recursion is enabled, and this isn't a "." or ".." directory,
+            # add this file onto the path and recurse into it.
+            if ($self->{recurse} and -d _ and $file !~ /^\.{1,2}$/) {
+                my $recurse_path = $path ? "$path/$file" : $file;
+                print STDERR "Recursing into $recurse_path\n";
+                my $recurse_output = $self->get_strref($recurse_path);
+                $output .= $$recurse_output;
+            }
+        }
+        unless ($path) {
+            # We only want to tack on the closing RDF tag if we're not
+            # in a recursion block
+            $output .= "</rdf:RDF>\n";
+        }
+        return \$output;
+    }
+    return undef;
+}
+
+# 
+# add mime-type information, if enabled.
+sub inspect_mimetype {
+    my $self = shift;
+    my ($filename) = @_;
+    my $mime_type = mimetype($filename);
+    if ($mime_type) {
+        return qq{    <fs:MIMEType>$mime_type</fs:MIMEType>\n};
+    }
+    return undef;
+}
+
+#
+# add image meta-data, if enabled
+sub inspect_images {
+    my $self = shift;
+    my ($filename) = @_;
+    my $output = "";
+    my $info = image_info($filename);
+    # use the file_ext data to determine if this is an image
+    if ($info and $info->{file_ext}) {
+        # hurl out RDF content based on Image::Info data that could be useful
+        $output .= qq{    <image:Width>} . $info->{width} . 
qq{</image:Width>\n};
+        $output .= qq{    <image:Height>} . $info->{height} . 
qq{</image:Height>\n};
+        $output .= qq{    <image:Resolution>} . $info->{resolution} . 
qq{</image:Resolution>\n};
+        $output .= qq{    <image:Extension>} . $info->{file_ext} . 
qq{</image:Extension>\n};
+        $output .= qq{    <image:ColorType>} . $info->{color_type} . 
qq{</image:ColorType>\n};
+        if (exists($info->{SamplesPerPixel})) {
+            $output .= qq{    <image:SamplesPerPixel>} . 
$info->{SamplesPerPixel} . qq{</image:SamplesPerPixel>\n};
+        }
+        if (exists($info->{Comment})) {
+            $output .= qq{    <image:Comment>} . $info->{Comment} . 
qq{</image:Comment>\n};
+        }
+        if (exists($info->{Interlace})) {
+            $output .= qq{    <image:Interlaced>} . $info->{Interlace} . 
qq{</image:Interlaced>\n};
+        }
+        if (exists($info->{Compression})) {
+            $output .= qq{    <image:Compression>} . $info->{Compression} . 
qq{</image:Compression>\n};
+        }
+        if (exists($info->{LastModificationTime})) {
+            $output .= qq{    <image:LastModified>} . 
$info->{LastModificationTime} . qq{</image:LastModified>\n};
+        }
+    }
+    return $output;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Apache::AxKit::DirHandler::RDF - directory handler implementing RDF output
+
+=head1 SYNOPSIS
+
+Override the base DirHandler class and enable it by using:
+
+    AxHandleDirs on
+    AxDirHandler Apache::AxKit::DirHandler::RDF
+
+    # optional
+    # PerlSetVar AxDirHandlerInspect "Images MimeType"
+    # PerlSetVar AxDirHandlerRecurse On
+
+=head1 DESCRIPTION
+
+This is an alternate Directory Handler module that outputs an RDF 
representation
+of a directory's contents.  Several namespaces are used to represent different
+pieces of information.  For convenience sake, the rdf:about attribute is the 
full
+URL of the indicated file or directory.
+
+Two namespaces are used to describe the filename.  Mozilla's "NC-rdf" namespace
+is used to make processing AxKit's directory output easier from within a 
Mozilla/XUL
+application.
+
+The "FileSystem" AxKit namespace is used to describe all other aspects of a 
file's
+attributes, as well as formatting various aspects of the filename and path in 
ways
+that may be convenient for site developers.
+
+Using File::MMagic, this module will try to determine the MIME type of each 
processed
+file.
+
+Finally, if a file is an image, this directory handler will attempt to process 
it
+with Image::Info.  The image dimensions, compression type and resolution will 
be
+outputted to the resulting RDF.
+
+=head1 OPTIONS
+
+This module allows you to customize, to some extent, what you want your RDF 
output
+to contain.  The following options can all be set using the following syntax:
+
+  PerlSetVar OPTION_NAME OPTION_VALUE
+
+=head2 AxDirHandlerRecurse
+
+If set to a true value (on, yes, 1), this will cause any directory listed in 
the
+requested URI to be recursed, supplying RDF for each file and subdirectory in 
turn.
+"." and ".." sub-directories will not be processed since, presumably, those 
entries
+would have already been defined in the parent RDF nodes.
+
+=head2 AxDirHandlerInspect
+
+This permits the toggling of various file inspection RDF statements to be 
included
+with appropriate files.  This allows for additional inspection methods to be 
added
+to either Apache::AxKit::DirHandler::RDF, or to any subclass of it.
+
+=over 4
+
+=item Images
+
+This will supply metadata about any images this directory handler finds.  Image
+type, dimensions, color-depth and -type, a suggested filename extension, 
comments
+and other useful information is added to the RDF output.
+
+=item MimeType
+
+This option, if enabled, sill include a fs:MIMEType statement to the RDF output
+indicating the MIME type of the files listed.  This relies upon 
L<File::MimeInfo::Magic>,
+so please see that for more information.
+
+=back
+
+=head1 SAMPLE OUTPUT
+
+  <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+           xmlns:nc="http://home.netscape.com/NC-rdf";
+           xmlns:image="http://axkit.org/2004/Image";
+           xmlns:fs="http://axkit.org/2004/FileSystem";>
+    <rdf:Description rdf:about="http://devbox.nachbaur.com/images/";>
+      <nc:Name>.</nc:Name>
+      <nc:URL>http://devbox.nachbaur.com/images/</nc:URL>
+      <fs:Filename>.</fs:Filename>
+      <fs:Path>/images/</fs:Path>
+      <fs:Directory>/</fs:Directory>
+      
<fs:FullPath>/var/www/sites/devbox.nachbaur.com/htdocs/images/</fs:FullPath>
+      <fs:LastModified>Fri, 20 Feb 2004 23:48:42 GMT</fs:LastModified>
+      <fs:LastAccessed>Sat, 21 Feb 2004 17:29:18 GMT</fs:LastAccessed>
+      <fs:ByteSize>4096</fs:ByteSize>
+      <fs:BlockSize>8</fs:BlockSize>
+      <fs:FormattedSize>4.00kB</fs:FormattedSize>
+      <fs:InodeType>directory</fs:InodeType>
+      <fs:MIMEType>inode/directory</fs:MIMEType>
+    </rdf:Description>
+    <rdf:Description rdf:about="http://devbox.nachbaur.com/";>
+      <nc:Name>..</nc:Name>
+      <nc:URL>http://devbox.nachbaur.com/</nc:URL>
+      <fs:Filename>..</fs:Filename>
+      <fs:Path>/</fs:Path>
+      <fs:Directory>/</fs:Directory>
+      <fs:FullPath>/var/www/sites/devbox.nachbaur.com/htdocs/</fs:FullPath>
+      <fs:LastModified>Fri, 20 Feb 2004 21:34:01 GMT</fs:LastModified>
+      <fs:LastAccessed>Sat, 21 Feb 2004 17:24:08 GMT</fs:LastAccessed>
+      <fs:ByteSize>4096</fs:ByteSize>
+      <fs:BlockSize>8</fs:BlockSize>
+      <fs:FormattedSize>4.00kB</fs:FormattedSize>
+      <fs:InodeType>directory</fs:InodeType>
+      <fs:MIMEType>inode/directory</fs:MIMEType>
+    </rdf:Description>
+    <rdf:Description rdf:about="http://devbox.nachbaur.com/images/foo";>
+      <nc:Name>foo</nc:Name>
+      <nc:URL>http://devbox.nachbaur.com/images/foo</nc:URL>
+      <fs:Filename>foo</fs:Filename>
+      <fs:Path>/images/foo</fs:Path>
+      <fs:Directory>/images</fs:Directory>
+      
<fs:FullPath>/var/www/sites/devbox.nachbaur.com/htdocs/images/foo</fs:FullPath>
+      <fs:LastModified>Sat, 14 Feb 2004 15:40:09 GMT</fs:LastModified>
+      <fs:LastAccessed>Sat, 21 Feb 2004 17:24:08 GMT</fs:LastAccessed>
+      <fs:ByteSize>4096</fs:ByteSize>
+      <fs:BlockSize>8</fs:BlockSize>
+      <fs:FormattedSize>4.00kB</fs:FormattedSize>
+      <fs:InodeType>directory</fs:InodeType>
+      <fs:MIMEType>inode/directory</fs:MIMEType>
+    </rdf:Description>
+    <rdf:Description rdf:about="http://devbox.nachbaur.com/images/bar";>
+      <nc:Name>bar</nc:Name>
+      <nc:URL>http://devbox.nachbaur.com/images/bar</nc:URL>
+      <fs:Filename>bar</fs:Filename>
+      <fs:Path>/images/bar</fs:Path>
+      <fs:Directory>/images</fs:Directory>
+      
<fs:FullPath>/var/www/sites/devbox.nachbaur.com/htdocs/images/bar</fs:FullPath>
+      <fs:LastModified>Sat, 14 Feb 2004 15:39:00 GMT</fs:LastModified>
+      <fs:LastAccessed>Sat, 21 Feb 2004 17:24:08 GMT</fs:LastAccessed>
+      <fs:ByteSize>0</fs:ByteSize>
+      <fs:BlockSize>0</fs:BlockSize>
+      <fs:FormattedSize>0.00B</fs:FormattedSize>
+      <fs:InodeType>file</fs:InodeType>
+      <fs:MIMEType>text/plain</fs:MIMEType>
+    </rdf:Description>
+    <rdf:Description 
rdf:about="http://devbox.nachbaur.com/images/mandrake-small.jpg";>
+      <nc:Name>mandrake-small.jpg</nc:Name>
+      <nc:URL>http://devbox.nachbaur.com/images/mandrake-small.jpg</nc:URL>
+      <fs:Filename>mandrake-small.jpg</fs:Filename>
+      <fs:Path>/images/mandrake-small.jpg</fs:Path>
+      <fs:Directory>/images</fs:Directory>
+      
<fs:FullPath>/var/www/sites/devbox.nachbaur.com/htdocs/images/mandrake-small.jpg</fs:FullPath>
+      <fs:LastModified>Sat, 14 Feb 2004 15:58:00 GMT</fs:LastModified>
+      <fs:LastAccessed>Sat, 21 Feb 2004 17:24:08 GMT</fs:LastAccessed>
+      <fs:ByteSize>2544</fs:ByteSize>
+      <fs:BlockSize>8</fs:BlockSize>
+      <fs:FormattedSize>2.48kB</fs:FormattedSize>
+      <fs:InodeType>file</fs:InodeType>
+      <image:Width>48</image:Width>
+      <image:Height>48</image:Height>
+      <image:Resolution>1 dpi</image:Resolution>
+      <image:Extension>jpg</image:Extension>
+      <image:ColorType>YCbCr</image:ColorType>
+      <image:SamplesPerPixel>3</image:SamplesPerPixel>
+      <fs:MIMEType>image/jpeg</fs:MIMEType>
+    </rdf:Description>
+    <rdf:Description 
rdf:about="http://devbox.nachbaur.com/images/link-4.1a.tar.gz";>
+      <nc:Name>link-4.1a.tar.gz</nc:Name>
+      <nc:URL>http://devbox.nachbaur.com/images/link-4.1a.tar.gz</nc:URL>
+      <fs:Filename>link-4.1a.tar.gz</fs:Filename>
+      <fs:Path>/images/link-4.1a.tar.gz</fs:Path>
+      <fs:Directory>/images</fs:Directory>
+      
<fs:FullPath>/var/www/sites/devbox.nachbaur.com/htdocs/images/link-4.1a.tar.gz</fs:FullPath>
+      <fs:LastModified>Fri, 20 Feb 2004 23:48:42 GMT</fs:LastModified>
+      <fs:LastAccessed>Sat, 21 Feb 2004 17:24:08 GMT</fs:LastAccessed>
+      <fs:ByteSize>580626</fs:ByteSize>
+      <fs:BlockSize>1144</fs:BlockSize>
+      <fs:FormattedSize>567.02kB</fs:FormattedSize>
+      <fs:InodeType>file</fs:InodeType>
+      <fs:MIMEType>application/x-compressed-tar</fs:MIMEType>
+    </rdf:Description>
+  </rdf:RDF>
+
+=head1 SEE ALSO
+
+L<Apache::AxKit::DirHandler>
+
+=cut
diff -Nurw --exclude=CVS xml-axkit.orig/lib/Apache/AxKit/DirHandler.pm 
xml-axkit/lib/Apache/AxKit/DirHandler.pm
--- xml-axkit.orig/lib/Apache/AxKit/DirHandler.pm       1969-12-31 
16:00:00.000000000 -0800
+++ xml-axkit/lib/Apache/AxKit/DirHandler.pm    2004-02-21 10:12:49.000000000 
-0800
@@ -0,0 +1,179 @@
+# $Id$
+
+package Apache::AxKit::DirHandler;
+use strict;
+use Apache;
+use Apache::Log;
+use Apache::URI;
+use AxKit;
+use Data::Dumper;
+
+sub new {
+    my $class = shift;
+    my $provider = shift;
+    my $self = bless {
+        provider => $provider,
+        directory => AxKit::FromUTF8($provider->{file}),
+    }, $class;
+
+    # init() ourselves, if a subclass wants to
+    $self->init(@_);
+
+    return $self;
+}
+
+sub init {
+    # blank - override to provide functionality
+}
+
+#
+# return mtime information for all files in this directory
+sub mtime {
+    my $self = shift;
+    my $dir = $self->{directory};
+    my $mtime = 0;
+       if (opendir(DIR, $dir)) {
+               while(my $line = readdir(DIR)) {
+            # stat the current file and, if it's mtime is bigger than
+            # anything we've so far seen, snag it
+                       my @stat = stat(File::Spec->catfile($dir,$line));
+                       $mtime = $stat[9] if ($stat[9] > $mtime);
+               }
+        AxKit::Debug(8, "Directory mtime is $mtime");
+               return $mtime;
+       }
+    AxKit::Debug(8, "Directory mtime calculations failed");
+    return undef;
+}
+
+#
+# return the filename for the current request...its just plain easier
+# to use this way IMHO.
+sub _request_filename {
+    my $self = shift;
+    my $provider = $self->{provider};
+    my $apache = $provider->apache_request;
+    return $apache->filename;
+}
+
+#
+# take a filename, and return a proper URI for that, given the
+# current apache request
+sub _request_uri {
+    my $self = shift;
+    my $path = shift;
+    my $provider = $self->{provider};
+    my $r = $provider->apache_request;
+    my $uri = Apache::URI->parse($r);
+
+    # Traverse through to the ".." directory
+    if ($path and $path eq '..') {
+        # We don't want to climb above the document_root
+        return undef if ($uri->path eq '/');
+
+        # trim off the last directory on the URI and return
+        # the modified Apache::URI object
+        my $new_path = $uri->path;
+        $new_path =~ s{/[^/]+/$} {/};
+        $uri->path($new_path);
+    }
+    
+    # Process the "." directory
+    elsif ($path and $path eq '.') {
+        # do nothing, since this is our current URL
+    }
+    
+    # For all other files, tack the filename onto the end of the URI
+    elsif ($path) {
+        $uri->path($uri->path . $path);
+    }
+
+    return ($uri, $uri->unparse) if (wantarray);
+    return $uri->unparse;
+}
+
+#
+# pretty-format a filesize in kilobytes, megabytes, etc
+sub _format_filesize {
+    my $self = shift;
+    my $size = shift;
+    my $factor = undef;
+    my $base = 1024;
+    my @suffix = qw( B kB MB GB TB PB );
+    for ($factor = $#suffix; $factor > 0; $factor--) {
+        last if ($size / $base ** $factor >= 1);
+    }
+    return sprintf('%.2f', $size / $base ** $factor) . $suffix[$factor];
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Apache::AxKit::DirHandler - base class for Directory handlers
+
+=head1 SYNOPSIS
+
+Override the base DirHandler class and enable it by using:
+
+    AxDirHandler MyClass
+
+    # alternatively use:
+    # PerlSetVar AxDirHandler MyClass
+
+=head1 DESCRIPTION
+
+AxKit supports the capability to handle directory requests.  Therefore, 
instead of
+relying on Apache to serve a Directory Index file or generating a file listing,
+AxKit will generate XML representing the content of the indicated directory for
+processing by your stylesheet pipeline.
+
+In many cases the default XML grammar provided will be sufficient, but for 
those
+instances when something more specific is necessary, this default behavior can 
be
+overridden.
+
+This class is a base-class implementing basic behavior, but must be inherited 
for
+directory listings to function.  To write your own directory handler, simply 
override
+this class and implement the C<get_strref()> method.
+
+=head1 PUBLIC METHODS
+
+The following are the methods available from this class:
+
+=head2 get_strref
+
+This method is called to generate the XML contents of a directory.  The 
"directory"
+property of the object contains the path of the directory to be returned.  The 
return
+value is expected to be a reference to the XML string to be returned.
+
+=head2 init()
+
+This method is called shortly after object construction, and can be used to 
initialize
+anything necessary to the operation of a directory handler.
+
+=head2 mtime()
+
+This returns the latest last modified time of any file or directory within the 
requested
+directory.  This is used for caching purposes.
+
+=head1 PRIVATE METHODS
+
+Apache::AxKit::DirHandler provides a few convenience methods that can make the 
business
+of processing directory listings easier.
+
+=head2 _request_filename
+
+This returns the requested filename from the Apache object.
+
+=head2 _request_uri
+
+Given a filename relative to the currently processed directory, this will 
return a full
+URI for the file.  If called in a scalar context it will return the full URI, 
while in an
+array context it will return both an Apache::URI object and the "unparsed" URI 
string.
+
+=head2 _format_filesize
+
+This will return a fancy filesize string (XkB, etc) for a given byte-size.
+
+=cut
diff -Nurw --exclude=CVS xml-axkit.orig/lib/Apache/AxKit/Provider/File.pm 
xml-axkit/lib/Apache/AxKit/Provider/File.pm
--- xml-axkit.orig/lib/Apache/AxKit/Provider/File.pm    2003-09-17 
14:58:38.000000000 -0700
+++ xml-axkit/lib/Apache/AxKit/Provider/File.pm 2004-02-20 15:25:31.000000000 
-0800
@@ -119,36 +119,14 @@
 
 sub get_dir_xml {
        my $self = shift;
-       local (*DIR);
-       my $dir = AxKit::FromUTF8($self->{file});
-       if (opendir(DIR, $dir)) {
-               my $output = '<?xml version="1.0" encoding="UTF-8"?>
-<filelist xmlns="http://axkit.org/2002/filelist";>
-';
-               while(my $line = readdir(DIR)) {
-                       my $xmlline = AxKit::ToUTF8($line);
-                       $xmlline =~ s/&/&amp;/;
-                       $xmlline =~ s/</&lt;/;
-                       my @stat = stat(File::Spec->catfile($dir,$line));
-                       my $attr = "size=\"$stat[7]\" atime=\"$stat[8]\" 
mtime=\"$stat[9]\" ctime=\"$stat[10]\"";
-                       $attr .= ' readable="1"' if (-r _);
-                       $attr .= ' writable="1"' if (-w _);
-                       $attr .= ' executable="1"' if (-x _);
-                       
-                       if (-f _) {
-                               $output .= "<file $attr>$xmlline</file>\n";
-                       } elsif (-d _) {
-                               $output .= "<directory 
$attr>$xmlline</directory>\n";
-                       } else {
-                               $output .= "<unknown 
$attr>$xmlline</unknown>\n";
-                       }
-               }
-               $output .= "</filelist>\n";
-               AxKit::Debug(8,"Generated file list: $output");
+    my $r = AxKit::Apache->request;
+    my $package = $AxKit::Cfg->DirHandlerClass();
+    my $dirhandler = $self->{dirhandler_obj}
+        || ($self->{dirhandler_obj} = $package->new($self));
+    my $output = $dirhandler->get_strref();
+    AxKit::Debug(8,"Generated file list: $$output") if (defined($$output));
                return $output;
        }
-       return undef;
-}
 
 sub decline {
     my $self = shift;
@@ -207,6 +185,12 @@
 
 sub mtime {
     my $self = shift;
+    if ($self->_is_dir and $AxKit::Cfg->HandleDirs()) {
+        my $package = $AxKit::Cfg->DirHandlerClass();
+        my $dirhandler = $self->{dirhandler_obj}
+            || ($self->{dirhandler_obj} = $package->new($self));
+        return $dirhandler->mtime();
+    }
     return $self->{mtime} if defined $self->{mtime};
     return ($self->{mtime} = (stat(AxKit::FromUTF8($self->{file})))[9]);
 }
@@ -233,7 +217,7 @@
     my $self = shift;
     if ($self->_is_dir()) {
         my $xml = $self->{dir_xml} || $self->get_dir_xml();
-        return \$xml if $xml;
+        return $xml if $$xml;
         throw Apache::AxKit::Exception::IO(
           -text => "directory $self->{file} cannot be read");
     }

Reply via email to