develooper Front page | perl.perl5.porters | Postings from September 2000

New Tools for Old

Thread Previous | Thread Next
Tom Christiansen
September 5, 2000 08:34
New Tools for Old
Message ID:
I've been working on the perldoc rewrite, as documented in the new
Camel's intro chapter.  If you've read it, that description provides
just the vaguest sketch of the real story.  Here is a more fleshed
out form of that so you see more what's happening.

Things are starting to come together nicely, and I think I'll be
able to give you stuff to play with and test out soon enough.  Here's
my general working description.  This all pretty much works save
for messy edges I intend to smooth out in the near future.

This all fits together, but you may need to read the whole thing
if you want to see the underlying componentry.  That is, how low-level
the access that you can get actually is.

Note that these are all the same

    perlman (normal mode)

    podgrep (same, but non Perl-installation aware mode)

    perldoc (for legacy operation; may have a few differing
	    CLI flags, but these are deprecated.)

But see below.  In reality, perlman is just something of a front-end
for a bunch of little semi-related programs and/or function calls.
Passing a full path to perlman is the same as calling podgrep,
although with podgrep the pager is not autocalled.

===================== NORMAL USAGE =================

$ perlman 

    Prints out a short usage message.

$ perlsub
$ perlman perlsub

    Shows you the perlsub manpage, formatted and paged.  (The program
    will use its own name as the name of the manpage to display if
    that name is not from a set of expected installation names.)
    If the pre-converted version is available in Config's $man1dir,
    the system man program will be invoked upon it; this confers
    all the speed and caching (and security?) of your native man
    system, because in fact it really uses your system, without
    having to know too much about it.  This in practice speeds up
    simple displays dramaticaly.

    If there is no converted version but if you appear to be on a
    manly system, the page will be manually sent through pod2man
    and nroff and your pager.  If you are unmanly, you get it run
    through pod2text and thence to your pager once again.

If you want to do things the slow way, you can ignore the nroff
translations using the "-t" flag, which means "pod2text" only.
These are all the same:

    $ perlsub -t
    $ perlman -t perlsub 
    $ pod2text `podpath perlsub` | $PAGER
    $ pod2text /usr/local/lib/perl5/5.6.0/pod/perlsub.pod | $PAGER

==================== SEARCH MODES =======================

    [ section, paragraph, line; should this be a --mode=line style
      option? ]

    Search modes are one of the following:

	no flag   => section search
	-p flag   => paragraph search
	-a flag   => line search
	-C flag   => code search (verbatim/indented podagraphs) 
	-L flag   => outline search (pod directives)

These are all the same:

$ perlsub local
$ perlman perlsub local
$ podgrep `podpath perlsub` local | $PAGER
$ podgrep /usr/local/lib/perl5/5.6.0/pod/perlsub.pod local | $PAGER

    This runs section-search mode against the raw perlsub podpage,
    where we're looking for any pod sections whose *headers* match
    the pattern "local".  By default, this text is sent through 
    pod2text and, if stdout isatty, your pager.  

    What's a section?  A section is defined to be an =foo pod
    directive plus any undirected text following that up until
    another pod directive of the same or higher level.  For example,
    a =head1 would include subordinate =head2s and =items, but be
    terminated by another =head1.  An =item is lower than any =headN,
    and is terminated by any =headN, or any =item that did not occur
    immediately following the matched one without intervening text;
    this latter rule allows a paragraph with multiple, consecutive
    =item tags to be matched, such as you find in perlfunc and
    perlvar.  That way you can say things like

	$ perlfunc split
	$ perlvar OFS
	$ perldiag 'assigned to typeglob'

    To get each of those =item entries.

$ perldata -p typeglob

    This runs paragraph-search mode, displaying any paragraph that
    matches "typeglob".  Pod directive paragraphs matching the
    pattern are not immediately catted out.  The paragraphs are by
    default tagged with the paragraph number from the pod source,
    and, if multiple files are possible (see Metapages below), the
    manpage name itself.  These paragraphs are sent through your
    pager (and pod2text?).  Because paragraphs break across line
    boundaries, in paragraph mode, all whitespace chunks are converted
    into \s+ and the (?ms) flags are added to control ^, $, and .

$ perlmod -a typeglob

    The -a flag runs in line-search mode, showing all lines matching
    the pattern "typeglob".  Lines are tagged with the number from
    their source, and, with multiple files, the name of the file.
    Your pager will be used for output, but not pod2text, as this
    is line-based.

$ perldata -C code
$ perldata -Ca code

    The -C flag runs in code-search mode, either on paragraphs (by
    default, or with -p) or lines (with -a).  Only verbatim pod
    paragraphs are listed, usually code.  By default, -p is assumed
    if -C is used and -a is not.

$ perlxs -L Keyword
$ perlman -L perlxs Keyword

    The -L flag (same as the ol(1) program below) will search only in 
    the outline.  It's essentially the same as

	$ ol perlxs | grep Keyword

===================== CONJUNCTIVE SEARCHES ======================

    If you ask for more than one search string, these are by default
    ANDed together.  The -o flag makes them ORed together.  For example:

	$ perldata -pi typeglob variable

    Prints only those paragraphs that case-insensitively contain both
    "typeglob" AND "variable".  With the -o flag, you get those
    with either of them.

	$ perldata -opi typeglob variable


	-i	case-insensitive using (?i)
	-w	whole words only using \bPAT\b, presuming P and T are 
	-l	list filenames matching, but not matches

    Maybe: (but these tend to conflict)
	-e EXPR for patterns beginning with -?
	-c	just show count of matches
	-o 	always show filename  (conflicts with -o above)
	-h	hide filenames
	-n	show recnos (but this is the default!)
	-x	exact matches only, so nothing else on line; eg, 
		perlsub -x BUGS to find just the BUGS section,
		not any section with "BUGS" in the name.

For example

    $ perldata -ai typeglob

    This searches perldata for any lines (the -a flag) that match
    "typeglob" case-insensitively (the -i flag).

=============== MULTIPLE PAGES AND METAPAGES ============

You can search multiple pages at once explicitly using a comma
separated pagelist:

    $ perlman -i perlsub,perldata variables

    That does a case-insensitive section-search against those
    two manpages.

or by a "metapage" specification.  For example, the "perlfaq" metapage
is really all the stanard perlfaq? manpages.

    $ perlfaq -w round

is the same as

    $ perlman -w perlfaq1,perlfaq2,perlfaq3,perlfaq4,perlfaq5,perlfaq6,perlfaq7,perlfaq8,perlfaq9 round

But much easier to type.

Some metapages also set or clear specific flags, usually change the
default search mode (which is section search) into something else.

    Metapage		Meaning

    perlhelp		All standard Perl manpages, with -a (line mode).
    perlfaq		All perlfaq? manpages.
    perltoc		All standard manpages, with -L (outline mode).
    stdmods		All CORE module manpages, with -a flag.
    modpods		All module manpages, with -a flag.
    sitemods		All non-CORE (site) module manpages, with -a flag.

These can be used with non-searches also.  If no search is provided
with a metabase, the -l is assumed to list out what files they are
consulting, as in:

    $ sitepods
    (Lists all the modules in your sitelib directories)

The module-related metapages all have to run a find (well, its perl equivalent)
on the relevant directories.  There is no central database, nor is it all
in one place.

| =============== HOOKS FROM perlman INTO OTHER PROGRAMS ================= |

| podgrep - grep the pods |

This is a regular search (pod-section search as described below
be default, but with the other search modes enabled by flags.)

    $ podgrep `podpath manpage` pattern
    $ podgrep /full/path pattern
    $ perlman manpage pattern
    $ manpage pattern

| docpath - show paths to docpages |

    The "docpath" program can be invoked as "perlman -l" if no search
    is given.  The "podpath" program, which doesn't include manpages,
    is the same as "perlman -lt", where the -t flag makes it only think
    about pod2textual bits.

  On manpages:
    $ docpath perlfunc
    $ perlfunc -l
    $ perlman -l perlfunc 

    $ podpath perlfunc
    $ perlfunc -lt 
    $ perlman -lt perlfunc 

  On modules:
    $ docpath LWP
    $ perlman -l LWP

  On programs:
    $ docpath perlbug
    $ perlman -l perlbug

Alternative invocations work as follows (this is really all the
same program).  These are also all available as functions from the
PM::Tools::podpath module.  If called as functions, then all paths
are returned in list context, but just the first found in scalar
context, which short-circuits the first.  The program versions show
all paths; see pmpath(1) below for more details.

    The docpath program means pod2manpath + podpath (that is, paths
	to the manpages plus the podpages)
    The podpath program means stdpodpath + pmpodpath + progpodpath
	(that is, just podpages)

    use PM::Tools qw/podpath/;		# imports podpath only
    use PM::Tools::podpath;		# imports podpath only
    use PM::Tools::podpath /:ALL/;	# imports all podpath tools

    stdpodpath("perlfunc") => /usr/local/perl/lib/pod/perlfunc.pod
    pmpodpath("CGI") => /usr/local/perl/lib/
    pmpodpath("POSIX") => /usr/local/lib/perl5/5.6.0/OpenBSD.i386-openbsd/POSIX.pod
    progpodpath("perlbug") => /usr/local/perl/bin/perlbug

    pod2manpath("perlfunc") => /usr/local/man/man1/perlfunc.1
    pod2manpath("CGI") => /usr/local/perl/man/man3/CGI.3

| pmpath - find path to perl modules |

    If you just want the path to the module, not its documentation,
    use "pmpath".  The podpath functions/programs will prefer a ".pod"
    over a ".pm", and they will open up programs to search for pods.
    This only cares about modules.

    As the pmpath(1) program

    $ pmpath POSIX

    $ pmpath IO::Socket

    $ pmpath LWP

    $ pmpath CGI LWP
    CGI: /usr/local/lib/perl5/5.6.0/
    LWP: /usr/local/lib/perl5/site_perl/5.6.0/ /usr/local/lib/perl5/site_perl/5.00554/LWP.

    $ pmpath -I/some/dir MyMod

    Or, as a module; first load module:

	use PM::Tools qw(pmpath);   
	use PM::Tools::pmpath;     # this form imports pmpath 

    Then in scalar context:

	my $path = pmpath("CGI");

    Or in list context:

	my @paths = pmpath("LWP");

| catpod - cat out just the pods |

    This program produces just the unformatted pods, but not the
    non-pod which will be filtered out, from a file.  If an absolute
    path is given, that will be the literal filename.  Otherwise,
    a podpath() search will be run to find the real path.

	$ catpod CGI
    is really 
	$ catpod `podpath CGI`
    is really 
	$ catpod /usr/local/lib/perl5/5.6.0/

    And, since this is the "perlman -u" unformatted flag, the same as:

	$ perlman -u CGI

| ol - display pod outline |

    (Should this be called olpod or podol instead?) 

    These are all (presumably) equivalent:

    $ ol /usr/local/lib/perl5/5.6.0/pod/perlsub.pod
    $ ol `podpath perlsub`
    $ ol perlsub
    $ perlsub -O
    $ perlman -O perlsub 

    The "ol" program prints the pod outline for the given file. 
    If the file is not absolute, podpath() will be used to find 
    it, as shown above.

    $ ol -2 perlsub

    You may specify a max level to display, so -1 would be only
    =head1, -2 would be no lower than =head2, etc.  =items are
    considered to be of the ninth level.

    The ol program can also be invoked as "perlman -L" or 
    eg "perlman -L2":

    $ perlsub -L 
    $ perlman -L perlsub 

    $ perlsub -L2 
    $ perlman -L2 perlsub 

Note that if you provide a search in conjunction with -L, you are
in "outline search" mode described above.

| pmcat - cat out complete module contents |

The pmcat locates the path to the named module and cats out the
complete contents (through your pager, probably, though).  This is
different from catpod in that it doesn't select only pod, or in
fact, prefer pod if both .pod and .pm versions are available, as
with POSIX.  The -U flag (for *really* unformatted) to perlman does
the same thing.

    $ pmcat CGI
    $ pmcat `pmpath CGI`
    $ $PAGER /usr/local/lib/perl5/5.6.0/
    $ perlman -U /usr/local/lib/perl5/5.6.0/

| other podpath funcprogs |

The PM::Tools::podpath module also supports the following
simple functions, which may be called as programs, too.

    $ podlibdir 

    $ mandirs 

    $ pmanpath 


What about apropos and whatis (that is, perlman -k or perlman -f)?
Those are either easy or hard.  If there's a whatis database in the
perl mandirs, then you just call native man.  If not, it's rough,
and you really would need to run pretty slowly to do a manual apropos
on the pods.  I suppose that "perlman -k foo" could be "perlhelp
-x NAME foo" or some such if you don't have a podful makewhatis to
build and install a podtree-specific database.  But if you did,
then you should think hard about what else the database should hold.

Thread Previous | Thread Next Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at | Group listing | About