3 sub Version { $VERSION; }
8 @EXPORT = qw(parse_dir);
13 use HTTP::Date qw(str2time);
19 my($dir, $tz, $fstype, $error) = @_;
22 $fstype = "File::Listing::" . lc $fstype;
25 push(@args, $tz) if(@_ >= 2);
26 push(@args, $error) if(@_ >= 4);
28 $fstype->parse(@args);
32 sub line { Carp::croak("Not implemented yet"); }
33 sub init { } # Dummy sub
38 # This routine was originally borrowed from Graham Barr's
45 s/^(.)// and $type = $1;
49 $mode |= 1 if $1 ne "-" &&
55 $type eq "d" and $mode |= 0040000 or # Directory
56 $type eq "l" and $mode |= 0120000 or # Symbolic Link
57 $mode |= 0100000; # Regular File
59 $mode |= 0004000 if /^...s....../i;
60 $mode |= 0002000 if /^......s.../i;
61 $mode |= 0001000 if /^.........t/i;
69 my($pkg, $dir, $tz, $error) = @_;
71 # First let's try to determine what kind of dir parameter we have
72 # received. We allow both listings, reference to arrays and
73 # file handles to read from.
75 if (ref($dir) eq 'ARRAY') {
78 elsif (ref($dir) eq 'GLOB') {
82 Carp::croak("Illegal argument to parse_dir()");
84 elsif ($dir =~ /^\*\w+(::\w+)+$/) {
85 # This scalar looks like a file handle, so we assume it is
88 # A normal scalar listing
89 $dir = [ split(/\n/, $dir) ];
95 if (ref($dir) eq 'ARRAY') {
97 push(@files, $pkg->line($_, $tz, $error));
104 push(@files, $pkg->line($_, $tz, $error));
107 wantarray ? @files : \@files;
112 package File::Listing::unix;
114 use HTTP::Date qw(str2time);
116 # A place to remember current directory from last line parsed.
117 use vars qw($curdir @ISA);
119 @ISA = qw(File::Listing);
131 shift; # package name
133 my($tz, $error) = @_;
138 my ($kind, $size, $date, $name);
139 if (($kind, $size, $date, $name) =
140 /^([\-FlrwxsStTdD]{10}) # Type and permission bits
144 (\w{3}\s+\d+\s+(?:\d{1,2}:\d{2}|\d{4})|\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}) # Date
145 \s+ # Some more space
150 return if $name eq '.' || $name eq '..';
151 $name = "$curdir/$name" if length $curdir;
153 if ($kind =~ /^l/ && $name =~ /(.*) -> (.*)/ ) {
157 elsif ($kind =~ /^[\-F]/) { # (hopefully) a regular file
160 elsif ($kind =~ /^[dD]/) {
162 $size = undef; # Don't believe the reported size
164 return [$name, $type, $size, str2time($date, $tz),
165 File::Listing::file_mode($kind)];
168 elsif (/^(.+):$/ && !/^[dcbsp].*\s.*\s.*:$/ ) {
170 return () if $dir eq '.';
174 elsif (/^[Tt]otal\s+(\d+)$/ || /^\s*$/) {
177 elsif (/not found/ || # OSF1, HPUX, and SunOS return
179 /No such file/ || # IRIX returns
180 # "UX:ls: ERROR: Cannot access $file: No such file or directory"
182 # "$file: No such file or directory"
183 /cannot find/ # Windows NT returns
184 # "The system cannot find the path specified."
186 return () unless defined $error;
187 &$error($_) if ref($error) eq 'CODE';
188 warn "Error: $_\n" if $error eq 'warn';
191 elsif ($_ eq '') { # AIX, and Linux return nothing
192 return () unless defined $error;
193 &$error("No such file or directory") if ref($error) eq 'CODE';
194 warn "Warning: No such file or directory\n" if $error eq 'warn';
198 # parse failed, check if the dosftp parse understands it
199 File::Listing::dosftp->init();
200 return(File::Listing::dosftp->line($_,$tz,$error));
207 package File::Listing::dosftp;
209 use HTTP::Date qw(str2time);
211 # A place to remember current directory from last line parsed.
212 use vars qw($curdir @ISA);
214 @ISA = qw(File::Listing);
226 shift; # package name
228 my($tz, $error) = @_;
232 my ($date, $size_or_dir, $name, $size);
234 # 02-05-96 10:48AM 1415 src.slf
235 # 09-10-96 09:18AM <DIR> sl_util
236 if (($date, $size_or_dir, $name) =
237 /^(\d\d-\d\d-\d\d\s+\d\d:\d\d\wM) # Date and time info
239 (<\w{3}>|\d+) # Dir or Size
240 \s+ # Some more space
244 return if $name eq '.' || $name eq '..';
245 $name = "$curdir/$name" if length $curdir;
247 if ($size_or_dir eq '<DIR>') {
249 $size = ""; # directories have no size in the pc listing
253 $size = $size_or_dir;
255 return [$name, $type, $size, str2time($date, $tz), undef];
258 return () unless defined $error;
259 &$error($_) if ref($error) eq 'CODE';
260 warn "Can't parse: $_\n" if $error eq 'warn';
268 package File::Listing::vms;
269 @File::Listing::vms::ISA = qw(File::Listing);
271 package File::Listing::netware;
272 @File::Listing::netware::ISA = qw(File::Listing);
276 package File::Listing::apache;
280 @ISA = qw(File::Listing);
287 shift; # package name
289 my($tz, $error) = @_; # ignored for now...
291 if (m!<A\s+HREF=\"([^\"]+)\">.*</A>.*?(\d+)-([a-zA-Z]+)-(\d+)\s+(\d+):(\d+)\s+(?:([\d\.]+[kM]?|-))!i) {
292 my($filename, $filesize) = ($1, $7);
293 my($d,$m,$y, $H,$M) = ($2,$3,$4,$5,$6);
295 $filesize = 0 if $filesize eq '-';
296 if ($filesize =~ s/k$//i) {
299 elsif ($filesize =~ s/M$//) {
300 $filesize *= 1024*1024;
302 elsif ($filesize =~ s/G$//) {
303 $filesize *= 1024*1024*1024;
305 $filesize = int $filesize;
308 my $filetime = Time::Local::timelocal(0,$M,$H,$d,_monthabbrev_number($m)-1,_guess_year($y)-1900);
309 my $filetype = ($filename =~ s|/$|| ? "d" : "f");
310 return [$filename, $filetype, $filesize, $filetime, undef];
329 sub _monthabbrev_number {
353 File::Listing - parse directory listing
357 use File::Listing qw(parse_dir);
358 $ENV{LANG} = "C"; # dates in non-English locales not supported
359 for (parse_dir(`ls -l`)) {
360 ($name, $type, $size, $mtime, $mode) = @$_;
361 next if $type ne 'f'; # plain file
365 # directory listing can also be read from a file
366 open(LISTING, "zcat ls-lR.gz|");
367 $dir = parse_dir(\*LISTING, '+0000');
371 This module exports a single function called parse_dir(), which can be
372 used to parse directory listings.
374 The first parameter to parse_dir() is the directory listing to parse.
375 It can be a scalar, a reference to an array of directory lines or a
376 glob representing a filehandle to read the directory listing from.
378 The second parameter is the time zone to use when parsing time stamps
379 in the listing. If this value is undefined, then the local time zone is
382 The third parameter is the type of listing to assume. Currently
383 supported formats are 'unix', 'apache' and 'dosftp'. The default
384 value 'unix'. Ideally, the listing type should be determined
387 The fourth parameter specifies how unparseable lines should be treated.
388 Values can be 'ignore', 'warn' or a code reference. Warn means that
389 the perl warn() function will be called. If a code reference is
390 passed, then this routine will be called and the return value from it
391 will be incorporated in the listing. The default is 'ignore'.
393 Only the first parameter is mandatory.
395 The return value from parse_dir() is a list of directory entries. In
396 a scalar context the return value is a reference to the list. The
397 directory entries are represented by an array consisting of [
398 $filename, $filetype, $filesize, $filetime, $filemode ]. The
399 $filetype value is one of the letters 'f', 'd', 'l' or '?'. The
400 $filetime value is the seconds since Jan 1, 1970. The
401 $filemode is a bitmask like the mode returned by stat().
405 Based on lsparse.pl (from Lee McLoughlin's ftp mirror package) and
406 Net::FTP's parse_dir (Graham Barr).