5 Sepia - Simple Emacs-Perl Interface
11 M-x load-library RET sepia RET
14 At the prompt in the C<*sepia-repl*> buffer:
18 For more information, please see F<sepia/index.html>.
28 use Scalar
::Util
'looks_like_number';
34 use vars
qw($PS1 $dies $STOPDIE $STOPWARN %REPL %RK %REPL_DOC
35 $PACKAGE $WANTARRAY $PRINTER $STRICT $PRINT_PRETTY);
38 eval { require PadWalker; import PadWalker qw(peek_my) };
40 *peek_my
= sub { +{ } };
42 eval { require Lexical
::Persistence
; import Lexical
::Persistence
};
45 print STDERR
"Strict mode requires Lexical::Persistence.\n";
50 my $x = as_boolean
(shift, $STRICT);
52 $STRICT = new Lexical
::Persistence
;
59 eval { require Module
::CoreList
};
61 *Sepia
::core_version
= sub { '???' };
63 *Sepia
::core_version
= sub { Module
::CoreList
->first_release(@_) };
69 Sepia is a set of features to make Emacs a better tool for Perl
70 development. This package contains the Perl side of the
71 implementation, including all user-serviceable parts (for the
72 cross-referencing facility see L<Sepia::Xref>). This document is
73 aimed as Sepia developers; for user documentation, see
76 Though not intended to be used independent of the Emacs interface, the
77 Sepia module's functionality can be used through a rough procedural
80 =head2 C<@compls = completions($string [, $type])>
82 Find a list of completions for C<$string> with glob type C<$type>,
83 which may be "SCALAR", "HASH", "ARRAY", "CODE", "IO", or the special
84 value "VARIABLE", which means either scalar, hash, or array.
85 Completion operates on word subparts separated by [:_], so
86 e.g. "S:m_w" completes to "Sepia::my_walksymtable".
88 =head2 C<@compls = method_completions($expr, $string [,$eval])>
90 Complete among methods on the object returned by C<$expr>. The
91 C<$eval> argument, if present, is a function used to do the
92 evaluation; the default is C<eval>, but for example the Sepia REPL
93 uses C<Sepia::repl_eval>. B<Warning>: Since it has to evaluate
94 C<$expr>, method completion can be extremely problematic. Use with
101 # Do that crazy multi-word identifier completion thing:
103 return qr/.*/ if $re eq '';
106 s/(?:^|(?<=[A-Za-z\d]))(([^A-Za-z\d])\2*)/[A-Za-z\\d]*$2+/g;
108 } split /:+/, $re, -1;
110 if ($re !~ /[^\w\d_^:]/) {
111 $re =~ s/(?<=[A-Za-z\d])(([^A-Za-z\d])\2*)/[A-Za-z\\d]*$2+/g;
121 my $re = shift || '';
123 if (@_ == 0 || !defined $_[0]) {
124 map "$stash$_", grep /$re/, keys %$stash;
127 _completions1
("$stash$_", @_);
128 } grep /$re.*::$/, keys %$stash;
134 _completions1
'::', _apropos_re
($_[0]);
139 %sigil = qw(ARRAY @ SCALAR $ HASH %);
145 my ($str, $type, $infunc) = @_;
150 defined *{$_}{CODE
} || defined *{$_}{IO
}
151 || (/::$/ && defined *{$_}{HASH
});
155 if ($type eq 'SCALAR') {
157 } elsif ($type eq 'VARIABLE') {
158 defined ${$_} || defined *{$_}{HASH
} || defined *{$_}{ARRAY
};
163 if (defined $infunc && defined *{$infunc}{CODE
}) {
164 my ($apre) = _apropos_re
($str);
165 my $st = $sigil{$type};
167 (my $tmp = $_) =~ s/^\Q$st//;
173 ## Complete "simple" sequences as abbreviations, e.g.:
174 ## wtci -> Want_To_Complete_It, NOT
176 if (!@ret && $str !~ /[^\w\d]/) {
177 my $broad = join '.*', map "\\b$_", split '', $str;
180 defined *{$_}{CODE
} || defined *{$_}{IO
}
181 || (/::$/ && defined *{$_}{HASH
});
182 } _completions1
'::', qr/$broad/;
185 $type eq 'SCALAR' ?
defined ${$_} : defined *{$_}{$type}
186 } _completions1
'::', qr/$broad/;
188 if (defined $infunc && defined *{$infunc}{CODE
}) {
189 my $st = $sigil{$type};
191 (my $tmp = $_) =~ s/^\Q$st//;
196 ## Complete packages so e.g. "new B:T" -> "new Blah::Thing"
197 ## instead of "new Blah::Thing::"
199 @ret = map { /(.*)::$/ ?
($1, $_) : $_ } @ret;
201 ## XXX: Control characters, $", and $1, etc. confuse Emacs, so
204 length > 0 && !looks_like_number
$_ && !/^[^\w\d_]$/ && !/^_</ && !/^[[:cntrl:]]/
205 } map { s/^:://; $_ } @ret;
208 sub method_completions
210 my ($expr, $fn, $eval) = @_;
217 $x = $eval->("ref($expr)");
218 } elsif ($eval->('defined(%{'.$expr.'::})')) {
224 my $re = _apropos_re
$fn;
225 print STDERR
"$x / $re\n";
226 return sort { $a cmp $b } map { s/.*:://; $_ }
227 grep { defined *{$_}{CODE
} && /::$re/ } methods
($x, 1);
231 =head2 C<@locs = location(@names)>
233 Return a list of [file, line, name] triples, one for each function
243 if (my ($pfx, $name) = $str =~ /^([\%\$\@]?)(.+)/) {
245 warn "Sorry -- can't lookup variables.";
248 # XXX: svref_2object only seems to work with a package
249 # tacked on, but that should probably be done
251 $name = 'main::'.$name unless $name =~ /::/;
252 my $cv = B
::svref_2object
(\
&{$name});
253 if ($cv && defined($cv = $cv->START) && !$cv->isa('B::NULL')) {
254 my ($file, $line) = ($cv->file, $cv->line);
255 if ($file !~ /^\//) {
263 my ($shortname) = $name =~ /^(?:.*::)([^:]+)$/;
264 [Cwd
::abs_path
($file), $line, $shortname || $name]
266 # warn "Bad CV for $name: $cv";
277 =head2 C<@matches = apropos($name [, $is_regex])>
279 Search for function C<$name>, either in all packages or, if C<$name>
280 is qualified, only in one package. If C<$is_regex> is true, the
281 non-package part of C<$name> is a regular expression.
285 sub my_walksymtable
(&*)
291 &$f for keys %$stash;
292 _walk
("$stash$_") for grep /(?<!main)::$/, keys %$stash;
299 my ($it, $re, @types) = @_;
302 $stashp = grep /STASH/, @types;
303 @types = grep !/STASH/, @types;
308 if ($it =~ /^(.*::)([^:]+)$/) {
309 my ($stash, $name) = ($1, $2);
310 if (!defined %$stash) {
314 my $name = qr/^$name/;
319 my $stashnm = "$stash$_";
322 || scalar grep { defined *{$stashnm}{$_} } @types)
325 defined &$it ?
$it : ();
329 my $findre = $re ?
qr/$it/ : qr/^\Q$it\E$/;
331 push @ret, "$stash$_" if /$findre/;
333 map { s/^:*(?:main:+)*//;$_ } @ret;
337 =head2 C<@names = mod_subs($pack)>
339 Find subs in package C<$pack>.
347 my $stash = \
%{"$p\::"};
348 if (defined $stash) {
349 grep { defined &{"$p\::$_"} } keys %$stash;
353 =head2 C<@decls = mod_decls($pack)>
355 Generate a list of declarations for all subroutines in package
366 my $proto = prototype(\
&{"$pack\::$sn"});
367 $proto = defined($proto) ?
"($proto)" : '';
370 return wantarray ?
@ret : join '', @ret;
373 =head2 C<$info = module_info($module, $type)>
375 Emacs-called function to get module information.
384 $info = Module
::Info
->new_from_file($m);
386 (my $file = $m) =~ s
|::|/|g
;
388 if (exists $INC{$file}) {
389 $info = Module
::Info
->new_from_loaded($m);
391 $info = Module
::Info
->new_from_module($m);
399 =head2 C<$file = mod_file($mod)>
401 Find the likely file owner for module C<$mod>.
409 while ($m && !exists $INC{"$m.pm"}) {
410 $m =~ s
#(?:^|/)[^/]+$##;
412 $m ?
$INC{"$m.pm"} : undef;
415 =head2 C<@mods = package_list>
417 Gather a list of all distributions on the system. XXX UNUSED
425 eval 'require ExtUtils::Installed';
426 $INST = new ExtUtils
::Installed
;
433 sort { $a cmp $b } inst
()->modules;
436 =head2 C<@mods = module_list>
438 Gather a list of all packages (.pm files, really) installed on the
439 system, grouped by distribution. XXX UNUSED
445 @_ = package_list
unless @_;
446 my $incre = join '|', map quotemeta, @INC;
447 $incre = qr
|(?
:$incre)/|;
451 s/$incre//; s
|/|::|g
;$_
452 } grep /\.pm$/, $inst->files($_)]
456 =head2 C<@mods = doc_list>
458 Gather a list of all documented packages (.?pm files, really)
459 installed on the system, grouped by distribution. XXX UNUSED
465 @_ = package_list
unless @_;
469 s/.*man.\///; s|/|::|g
;s/\..?pm//; $_
470 } grep /\..pm$/, $inst->files($_)]
474 =head2 C<lexicals($subname)>
476 Return a list of C<$subname>'s lexical variables. Note that this
477 includes all nested scopes -- I don't know if or how Perl
478 distinguishes inner blocks.
484 my $cv = B
::svref_2object
(\
&{+shift});
485 return unless $cv && ($cv = $cv->PADLIST);
486 my ($names, $vals) = $cv->ARRAY;
488 my $name = $_->PV; $name =~ s/\0.*$//; $name
489 } grep B
::class($_) ne 'SPECIAL', $names->ARRAY;
492 =head2 C<$lisp = tolisp($perl)>
494 Convert a Perl scalar to some ELisp equivalent.
500 my $thing = @_ == 1 ?
shift : \
@_;
503 if (!defined $thing) {
505 } elsif (looks_like_number
$thing) {
508 ## XXX Elisp and perl have slightly different
509 ## escaping conventions, so we do this crap instead.
510 $thing =~ s/["\\]/\\\1/g;
513 } elsif ($t eq 'GLOB') {
514 (my $name = $$thing) =~ s/\*main:://;
516 } elsif ($t eq 'ARRAY') {
517 '(' . join(' ', map { tolisp
($_) } @
$thing).')'
518 } elsif ($t eq 'HASH') {
519 '(' . join(' ', map {
520 '(' . tolisp
($_) . " . " . tolisp
($thing->{$_}) . ')'
522 } elsif ($t eq 'Regexp') {
523 "'(regexp . \"" . quotemeta($thing) . '")';
524 # } elsif ($t eq 'IO') {
530 =head2 C<printer(\@res [, $iseval])>
532 Print C<@res> appropriately on the current filehandle. If C<$iseval>
533 is true, use terse format. Otherwise, use human-readable format,
534 which can use either L<Data::Dumper>, L<YAML>, or L<Data::Dump>.
540 local $Data::Dumper
::Deparse
= 1;
541 local $Data::Dumper
::Indent
= 0;
544 local $_ = Data
::Dumper
::Dumper
(@res > 1 ? \
@res : $res[0]);
560 eval { require YAML
};
571 eval { require Data
::Dump
};
583 my ($iseval, $wantarray) = @_;
585 $::__
= @res == 1 ?
$res[0] : [@res];
589 } elsif (@res == 1 && (ref $res[0]) =~ /^PDL/) {
591 } elsif (!$iseval && $PRINT_PRETTY && @res > 1 && !grep ref, @res) {
592 $res = columnate
(sort @res);
599 print ';;;', length $res, "\n$res\n";
607 Execute a command interpreter on FH. The prompt has a few bells and
610 * Obviously-incomplete lines are treated as multiline input (press
611 'return' twice or 'C-c' to discard).
613 * C<die> is overridden to enter a recursive interpreter at the point
614 C<die> is called. From within this interpreter, you can examine a
615 backtrace by calling "bt", return from C<die> with "r EXPR", or
616 go ahead and die by pressing Control-c.
618 Behavior is controlled in part through the following package-globals:
622 =item C<$PACKAGE> -- evaluation package
624 =item C<$PRINTER> -- result printer (default: print_dumper)
626 =item C<$PS1> -- the default prompt
628 =item C<$STOPDIE> -- true to enter the inspector on C<die()>
630 =item C<$STOPWARN> -- true to enter the inspector on C<warn()>
632 =item C<$STRICT> -- whether 'use strict' is applied to input
634 =item C<$WANTARRAY> -- evaluation context
636 =item C<$PRINT_PRETTY> -- format some output nicely (default = 1)
638 Format some values nicely, independent of $PRINTER. Currently, this
639 displays arrays of scalars as columns.
641 =item C<%REPL> -- maps shortcut names to handlers
643 =item C<%REPL_DOC> -- maps shortcut names to documentation
657 $PRINTER = \
&Sepia
::print_dumper
;
659 %REPL = (help
=> \
&Sepia
::repl_help
,
660 cd
=> \
&Sepia
::repl_chdir
,
661 methods
=> \
&Sepia
::repl_methods
,
662 package => \
&Sepia
::repl_package
,
663 who
=> \
&Sepia
::repl_who
,
664 wantarray => \
&Sepia
::repl_wantarray
,
665 format
=> \
&Sepia
::repl_format
,
666 strict
=> \
&Sepia
::repl_strict
,
667 quit
=> \
&Sepia
::repl_quit
,
668 reload
=> \
&Sepia
::repl_reload
,
672 'cd DIR Change directory to DIR',
674 'format [dumper|dump|yaml|plain]
675 Set output formatter (default: dumper)',
677 'help Display this message',
679 methods X [RE] List methods for reference or package X,
680 matching optional pattern RE.
683 'package PACKAGE Set evaluation package to PACKAGE',
685 'quit Quit the REPL',
687 'strict [0|1] Turn \'use strict\' mode on or off',
689 'wantarray [0|1] Set or toggle evaluation context',
691 who PACKAGE [RE] List variables and subs in PACKAGE matching optional
695 'reload Reload Sepia.pm and relaunch the REPL.',
697 %RK = abbrev
keys %REPL;
702 "$PACKAGE ".($WANTARRAY ?
'@' : '$').$PS1
707 Data
::Dumper
->Dump([$_[0]], [$_[1]]);
713 my ($expr, $env) = @_;
717 next unless /^([\$\@%])(.+)/;
718 $str .= "local *$2 = \$::ENV->{'$_'}; ";
720 eval "do { no strict; $str $expr }";
725 my ($lev, $exp) = $_[0] =~ /^\s*(\d+)\s+(.*)/;
727 (0, eval_in_env
($exp, peek_my
(0+$lev)));
734 my $sub = (caller $i)[3];
737 print "[$i] $sub:\n";
739 for (sort keys %$h) {
740 local @res = $h->{$_};
741 print "\t$_ = ", $PRINTER->(), "\n";
749 print "REPL commands (prefixed with ','):\n";
750 for (sort keys %REPL) {
752 exists $REPL_DOC{$_} ?
"$REPL_DOC{$_}\n": "$_ (undocumented)\n";
761 $t = 'dumper' if $t eq '';
762 my %formats = abbrev
qw(dumper dump yaml plain);
763 if (exists $formats{$t}) {
765 $PRINTER = \
&{'print_'.$formats{$t}};
767 warn "No such format '$t' (dumper, dump, yaml, plain).\n";
774 chomp(my $dir = shift);
775 $dir =~ s/^~\//$ENV{HOME
}\
//;
776 $dir =~ s/\$HOME/$ENV{HOME}/;
780 my $ecmd = '(cd "'.Cwd
::getcwd
().'")';
781 print ";;;###".length($ecmd)."\n$ecmd\n";
783 warn "Can't chdir\n";
790 my ($pack, $re) = @_;
794 sort grep /$re/, map {
795 (defined %{$pack.'::'.$_} ?
'%'.$_ : (),
796 defined ${$pack.'::'.$_} ?
'$'.$_ : (), # ?
797 defined @
{$pack.'::'.$_} ?
'@'.$_ : (),
798 defined &{$pack.'::'.$_} ?
$_ : (),
800 } grep !/::$/ && !/^(?:_<|[^\w])/, keys %{$pack.'::'};
807 my $width = $ENV{COLUMNS
} || 80;
809 $len = length if $len < length;
811 my $nc = int($width / ($len+1)) || 1;
812 my $nr = int(@_ / $nc) + (@_ % $nc ?
1 : 0);
813 my $fmt = ('%-'.($len+1).'s') x
($nc-1) . "%s\n";
814 my @incs = map { $_ * $nr } 0..$nc-1;
816 for my $r (0..$nr-1) {
817 $str .= sprintf $fmt, map { $_ || '' } @_[map { $r + $_ } @incs];
825 my ($pkg, $re) = split ' ', shift;
826 print columnate who
($pkg || $PACKAGE, $re);
832 my ($pack, $qualified) = @_;
834 my @own = $qualified ?
grep {
836 } map { "$pack\::$_" } keys %{$pack.'::'}
838 defined *{"$pack\::$_"}{CODE
}
839 } keys %{$pack.'::'};
840 (@own, defined @
{$pack.'::ISA'}
841 ?
(map methods
($_, $qualified), @
{$pack.'::ISA'}) : ());
846 my ($x, $re) = split ' ', shift;
850 $x = repl_eval
("ref $x");
855 print columnate
sort { $a cmp $b } grep /$re/, methods
$x;
861 my ($val, $cur) = @_;
863 length($val) ?
$val : !$cur;
868 $WANTARRAY = as_boolean
shift, $WANTARRAY;
874 chomp(my $p = shift);
876 if (defined %{$p.'::'}) {
878 # my $ecmd = '(setq sepia-eval-package "'.$p.'")';
879 # print ";;;###".length($ecmd)."\n$ecmd\n";
881 warn "Can't go to package $p -- doesn't exist!\n";
895 print "Reload failed:\n$@\n";
905 Inspector commands (prefixed with ','):
906 ^C Pop one debugger level
907 backtrace show backtrace
908 inspect N ... inspect lexicals in frame(s) N ...
909 eval N EXPR evaluate EXPR in lexical environment of frame N
910 return EXPR return EXPR
911 die/warn keep on dying/warning
928 my ($buf, $wantarray, $pkg) = @_;
930 local $PACKAGE = $pkg || $PACKAGE;
933 $buf = 'scalar($buf)';
935 my $ctx = join(',', keys %{$STRICT->get_context('_')});
936 $ctx = $ctx ?
"my ($ctx);" : '';
937 $buf = eval "sub { package $PACKAGE; use strict; $ctx $buf }";
939 print STDERR
"ERROR\n$@\n";
944 $buf = "do { package $PACKAGE; no strict; $buf }";
953 ## Collects warnings for REPL
967 print ';;;'.length($tmp)."\n$tmp\n";
971 print "warning: $_\n";
979 my ($fh, $level) = @_;
980 select((select($fh), $|=1)[0]);
985 my $nextrepl = sub { $sigged = 1; };
988 my $MSG = "('\\C-c' to exit, ',h' for help)";
990 backtrace
=> \
&Sepia
::debug_backtrace
,
991 inspect
=> \
&Sepia
::debug_inspect
,
992 eval => \
&Sepia
::debug_upeval
,
993 return => \
&Sepia
::debug_return
,
994 help
=> \
&Sepia
::debug_help
,
996 local *CORE
::GLOBAL
::die = sub {
997 ## Protect us against people doing weird things.
998 if ($STOPDIE && !$SIG{__DIE__
}) {
1000 local $dies = $dies+1;
1001 local $PS1 = "*$dies*> ";
1003 local %Sepia::REPL
= (
1004 %dhooks, die => sub { local $Sepia::STOPDIE
=0; die @dieargs });
1005 local %Sepia::RK
= abbrev
keys %Sepia::REPL
;
1006 print "@_\n\tin ".caller()."\nDied $MSG\n";
1007 return Sepia
::repl
($fh, 1);
1009 CORE
::die(Carp
::shortmess
@_);
1012 local *CORE
::GLOBAL
::warn = sub {
1013 ## Again, this is above our pay grade:
1014 if ($STOPWARN && $SIG{__WARN__
} eq 'Sepia::sig_warn') {
1016 local $dies = $dies+1;
1017 local $PS1 = "*$dies*> ";
1019 local %Sepia::REPL
= (
1020 %dhooks, warn => sub { local $Sepia::STOPWARN
=0; warn @dieargs });
1021 local %Sepia::RK
= abbrev
keys %Sepia::REPL
;
1022 print "@_\nWarned $MSG\n";
1023 return Sepia
::repl
($fh, 1);
1025 ## Avoid showing up in location information.
1026 CORE
::warn(Carp
::shortmess
@_);
1028 print <<EOS if $dies == 0;
1029 Sepia version $Sepia::VERSION.
1030 Press ",h" for help, or "^D" or ",q" to exit.
1033 my @sigs = qw(INT TERM PIPE ALRM);
1035 $SIG{$_} = $nextrepl for @sigs;
1036 repl
: while (my $in = <$fh>) {
1046 if ($buf =~ /^<<(\d+)\n(.*)/) {
1051 while ($len && defined($tmp = read $fh, $buf, $len, length $buf)) {
1056 ## Only install a magic handler if no one else is playing.
1057 local $SIG{__WARN__
} = $SIG{__WARN__
};
1059 unless ($SIG{__WARN__
}) {
1060 $SIG{__WARN__
} = 'Sepia::sig_warn';
1062 if ($buf =~ /^,(\S+)\s*(.*)/s) {
1063 ## Inspector shortcuts
1065 if (exists $Sepia::RK
{$short}) {
1069 ($ret, @res) = $Sepia::REPL
{$Sepia::RK
{$short}}->($arg, wantarray);
1071 return wantarray ?
@res : $res[0];
1074 if (grep /^$short/, keys %Sepia::REPL
) {
1075 print "Ambiguous shortcut '$short': ",
1076 join(', ', sort grep /^$short/, keys %Sepia::REPL
),
1079 print "Unrecognized shortcut '$short'\n";
1087 @res = repl_eval
$buf, wantarray;
1090 ## Always return results for an eval request
1091 Sepia
::printer \
@res, 1, wantarray;
1092 Sepia
::printer
[$@
], 1, wantarray;
1093 # print_warnings $iseval;
1096 } elsif ($@
=~ /at EOF$/m) {
1097 ## Possibly-incomplete line
1099 print "Error:\n$@\n*** cancel ***\n", prompt
;
1106 # $@ =~ s/(.*) at eval .*/$1/;
1107 print "error: $@\n";
1114 if ($buf !~ /;$/ && $buf !~ /^,/) {
1115 ## Be quiet if it ends with a semicolon, or if we
1116 ## executed a shortcut.
1117 Sepia
::printer \
@res, $iseval, wantarray;
1120 print_warnings
$iseval;
1127 tolisp
(repl_eval
(shift));
1130 =head2 C<$status = html_module_list($file [, $prefix])>
1132 Generate an HTML list of installed modules, looking inside of
1133 packages. If C<$prefix> is missing, uses "about://perldoc/".
1135 =head2 C<$status = html_package_list($file [, $prefix])>
1137 Generate an HTML list of installed top-level modules, without looking
1138 inside of packages. If C<$prefix> is missing, uses
1143 sub html_module_list
1145 my ($file, $base) = @_;
1146 $base ||= 'about://perldoc/';
1148 return unless $inst;
1149 return unless open OUT
, ">$file";
1150 print OUT
"<html><body><ul>";
1153 for (package_list
) {
1154 push @
{$ns{$1}}, $_ if /^([^:]+)/;
1156 for (sort keys %ns) {
1157 print OUT
qq{<li
><b
>$_</b
><ul
>} if @
{$ns{$_}} > 1;
1158 for (sort @
{$ns{$_}}) {
1160 s/.*man.\///; s|/|::|g
; s/\..?pm//; $_
1161 } grep /\.\dpm$/, sort $inst->files($_);
1163 print OUT
qq{<li
><a href
="$base$fs[0]">$fs[0]</a
>};
1165 print OUT
qq{<li
>$_<ul
>};
1167 print OUT
qq{<li
><a href
="$base$_">$_</a
>};
1172 print OUT
qq{</ul
>} if @
{$ns{$_}} > 1;
1174 print OUT
"</ul></body></html>\n";
1179 sub html_package_list
1181 my ($file, $base) = @_;
1182 return unless inst
();
1183 $base ||= 'about://perldoc/';
1184 return unless open OUT
, ">$file";
1185 print OUT
"<html><body><ul>";
1188 for (package_list
) {
1189 push @
{$ns{$1}}, $_ if /^([^:]+)/;
1191 for (sort keys %ns) {
1192 if (@
{$ns{$_}} == 1) {
1194 qq{<li
><a href
="$base$ns{$_}[0]">$ns{$_}[0]</a
>};
1196 print OUT
qq{<li
><b
>$_</b
><ul
>};
1197 print OUT
qq{<li
><a href
="$base$_">$_</a
>}
1198 for sort @
{$ns{$_}};
1199 print OUT
qq{</ul
>};
1202 print OUT
"</ul></body></html>\n";
1212 See the README file included with the distribution.
1216 Sean O'Rourke, E<lt>seano@cpan.orgE<gt>
1218 Bug reports welcome, patches even more welcome.
1222 Copyright (C) 2005-2007 Sean O'Rourke. All rights reserved, some
1223 wrongs reversed. This module is distributed under the same terms as