summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfukachan <fukachan>2001-10-18 08:32:16 +0000
committerfukachan <fukachan>2001-10-18 08:32:16 +0000
commitee46374edfb377f57169f2b1de9afd19bf052da2 (patch)
tree515661b27c563e1f64bef637f785dccf849adcf4
parentece119bc5adb2b01f8a6e5646d99870af695d9bf (diff)
downloadfml8-ee46374edfb377f57169f2b1de9afd19bf052da2.tar.gz
fml8-ee46374edfb377f57169f2b1de9afd19bf052da2.tar.bz2
fml8-ee46374edfb377f57169f2b1de9afd19bf052da2.zip
Initial revision
-rw-r--r--cpan/dist/HTML-FromText/FromText.pm736
-rw-r--r--cpan/dist/HTML-FromText/MANIFEST6
-rw-r--r--cpan/dist/HTML-FromText/Makefile.PL5
-rw-r--r--cpan/dist/HTML-FromText/README58
-rw-r--r--cpan/dist/HTML-FromText/TODO33
-rw-r--r--cpan/dist/HTML-FromText/t/text2html.t698
-rw-r--r--cpan/lib/HTML/FromText.pm736
7 files changed, 2272 insertions, 0 deletions
diff --git a/cpan/dist/HTML-FromText/FromText.pm b/cpan/dist/HTML-FromText/FromText.pm
new file mode 100644
index 00000000..2b7f1753
--- /dev/null
+++ b/cpan/dist/HTML-FromText/FromText.pm
@@ -0,0 +1,736 @@
+require 5.004;
+use strict;
+
+package HTML::FromText;
+use Carp;
+use Exporter;
+use Text::Tabs 'expand';
+use vars qw($RCSID $VERSION $QUIET @EXPORT @ISA);
+
+@ISA = qw(Exporter);
+@EXPORT = qw(text2html);
+$RCSID = q$Id: FromText.pm,v 1.14 1999/10/06 10:53:37 garethr Exp $;
+$VERSION = '1.005';
+$QUIET = 0;
+
+# This list of protocols is taken from RFC 1630: "Universal Resource
+# Identifiers in WWW". The protocol "file" is omitted because
+# experience suggests that it results in many false positives; "https"
+# postdates RFC 1630. The protocol "mailto" is handled separately, by
+# the email address matching code.
+
+my $protocol = join '|',
+ qw(afs cid ftp gopher http https mid news nntp prospero telnet wais);
+
+# The regular expressions matching email addresses use the following
+# syntax elements from RFC 822. I can't use the full details of
+# structured field bodies, because that would give too many false
+# positives. (See Tom Christiansen's ckaddr.gz for a full
+# implementation of the RFC 822.)
+#
+# addr-spec = local-part "@" domain
+# local-part = word *("." word)
+# word = atom
+# domain = sub-domain *("." sub-domain)
+# sub-domain = domain-ref
+# domain-ref = atom
+# atom = 1*<any CHAR except specials, SPACE and CTLs>
+# specials = "(" / ")" / "<" / ">" / "@" / "," / ";" / ":" / "\"
+# / <"> / "." / "[" / "]"
+#
+# I have ignored quoting, domain literals and comments.
+#
+# Note that '&' can legally appear in email addresses (for example,
+# 'fred&barney@stonehenge.com'). If the 'metachars' option is passed to
+# text2html then I must use '&amp;' to recognize '&'. Thus the regular
+# expression $atom[0] recognizes an atom in the case where the option
+# 'metachars' is false; $atom[1] recognizes an atom in the case where
+# 'metachars' is true. Similarly for the regular expressions $email[0]
+# and $email[1], which recognize email addresses.
+
+my @atom =
+ ( '[!#$%&\'*+\\-/0123456789=?ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~]+',
+ '(?:&amp;|[!#$%\'*+\\-/0123456789=?ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~])+' );
+
+my @email = ( "$atom[0](\\.$atom[0])*\@$atom[0](\\.$atom[0])*",
+ "$atom[1](\\.$atom[1])*\@$atom[1](\\.$atom[1])*" );
+
+my @alignments = ( '', '', ' ALIGN="RIGHT"', ' ALIGN="CENTER"' );
+
+sub string2html ($$) {
+ my $options = $_[1];
+ for ($_[0]) { # Modify in-place.
+
+ # METACHARS: mark up HTML metacharacters as corresponding entities.
+ if ($options->{metachars}) {
+ s/&/&amp;/g;
+ s/</&lt;/g;
+ s/>/&gt;/g;
+ s/\"/&quot;/g;
+ }
+
+ # EMAIL, URLS: spot electronic mail addresses and turn them into
+ # links. Note (1) if `urls' is set but not `email', then only
+ # addresses prefixed by `mailto:' will be marked up; (2) that we leave
+ # the `mailto:' prefix in the anchor text.
+ if ($options->{email} or $options->{urls}) {
+ s|((?:mailto:)?)($email[$options->{metachars}?1:0])|
+ ($options->{email} or $1)
+ ? "<TT><A HREF=\"mailto:$2\">$1$2</A></TT>" : $2|gex;
+ }
+
+ # URLS: mark up URLs as links (note that `mailto' links are handled
+ # above).
+ if ($options->{urls}) {
+ s|\b((?:$protocol):\S+[\w/])|<TT><A HREF="$1">$1</A></TT>|g;
+ }
+
+ # BOLD: mark up words in *asterisks* as bold.
+ if ($options->{bold}) {
+ s#(^|\s)\*([^*]+)\*(?=\s|$)#$1<B>$2</B>#g;
+ }
+
+ # UNDERLINE: mark up words in _underscores_ as underlined.
+ if ($options->{underline}) {
+ s#(^|\s)_([^_]+?)_(?=\s|$)#$1<U>$2</U>#g;
+ }
+ }
+
+ return $_[0];
+}
+
+sub text2html {
+ local $_ = shift; # Take a copy; don't modify in-place.
+ return $_ unless $_;
+
+ my %options = ( metachars => 1, @_ );
+
+ # Check options for sanity.
+ unless ($QUIET) {
+ carp "text2html: `spaces' will be ignored since `lines' is not specified"
+ if $options{spaces} and not $options{lines};
+ if ($options{paras}) {
+ if ($options{blockparas}) {
+ foreach my $o (qw(blockquotes blockcode)) {
+ carp "text2html: `$o' will be ignored since `blockparas' is specified" if $options{$o};
+ }
+ } elsif ($options{blockcode} and $options{blockquotes}) {
+ carp "text2html: `blockquotes' will be ignored since `blockcode' is specified";
+ }
+ } else {
+ foreach my $o (qw(bullets numbers blockquotes blockparas blockcode
+ title headings tables)) {
+ carp "text2html: `$o' will be ignored since `paras' is not specified"
+ if $options{$o};
+ }
+ }
+ }
+
+ # Expand tabs.
+ $_ = join "\n", expand(split /\r?\n/);
+
+ # PRE: put text in <PRE> element.
+ if ($options{pre}) {
+ string2html($_, \%options);
+ s|^|<PRE>|;
+ s|$|</PRE>|;
+ }
+
+ # LINES: preserve line breaks from original text.
+ elsif ($options{lines}) {
+ string2html($_, \%options);
+ s/\n/<BR>\n/gm;
+
+ # SPACES: preserve spaces from original text.
+ s/ /&nbsp;/g if $options{spaces};
+ }
+
+ # PARAS: treat text as sequence of paragraphs.
+ elsif ($options{paras}) {
+ my @paras;
+
+ # Remove initial and final blank lines.
+ s/^(?:\s*?\n)+//;
+ s/(?:\n\s*?)+$//;
+
+ # Split on a different regexp depending on what kinds of paragraphs
+ # will be recognised later. The idea is that bulleted lists like
+ # this:
+ #
+ # * item 1
+ # * item 2
+ #
+ # will be recognised as multiple paragraphs if the 'bullets' option
+ # is supplied, but as a single paragraph otherwise. (Similarly for
+ # numbered lists).
+ if ($options{bullets} and $options{numbers}) {
+ @paras = split
+ /(?:\s*\n)+ # (0 or more blank lines, followed by LF)
+ (?:\s*\n # Either 1 or more blank lines, or
+ |(?=\s*[*-]\s+ # bulleted item follows, or
+ |\s*(?:\d+)[.\)\]]?\s+)) # numbered item follows
+ /x;
+ } elsif ($options{bullets}) {
+ @paras = split
+ /(?:\s*\n)+ # (0 or more blank lines, followed by LF)
+ (?:\s*\n # Either 1 or more blank lines, or
+ |(?=\s*[*-]\s+)) # bulleted item follows.
+ /x;
+ } elsif ($options{numbers}) {
+ @paras = split
+ /(?:\s*\n)+ # (0 or more blank lines, followed by LF)
+ (?:\s*\n # Either 1 or more blank lines, or
+ |(?=\s*(?:\d+)[.\)\]]?\s+)) # numbered item follows.
+ /x;
+ } else {
+ @paras = split
+ /\s*\n(?:\s*\n)+ # 1 or more blank lines.
+ /x;
+ }
+
+ my $last = ''; # List type (OL/UL) of last paragraph
+ my $this; # List type (OL/UL) of this paragraph
+ my $first = 1; # True if this is first paragraph
+
+ foreach (@paras) {
+ my (@rows,@starts,@ends);
+ $this = '';
+
+ # TITLE: mark up first paragraph as level-1 heading.
+ if ($options{title} and $first) {
+ string2html($_,\%options);
+ s|^|<H1>|;
+ s|$|</H1>|;
+ }
+
+ # HEADINGS: mark up paragraphs with numbers at the start of the
+ # first line as headings.
+ elsif ($options{headings} and /^(\d+(\.\d+)*)\.?\s/) {
+ my $number = $1;
+ my $level = 1 + ($number =~ tr/././);
+ $level = 6 if $level > 6;
+ string2html($_,\%options);
+ s|^|<H$level>|;
+ s|$|</H$level>|;
+ }
+
+ # BULLETS: mark up paragraphs starting with bullets as items in an
+ # unnumbered list.
+ elsif ($options{bullets} and /^\s*[*-]\s+/) {
+ string2html($_,\%options);
+ s/^\s*[*-]\s+/<LI><P>/;
+ s|$|</P>|;
+ $this = 'UL';
+ }
+
+ # NUMBERS: mark up paragraphs starting with numbers as items in a
+ # numbered list.
+ elsif ($options{numbers} and /^\s*(\d+)[.\)\]]?\s+/) {
+ string2html($_,\%options);
+ s/^\s*(\d+)[.\)\]]?\s+/<LI VALUE="$1"><P>/;
+ s|$|</P>|;
+ $this = 'OL';
+ }
+
+ # TABLES: spot and mark up tables. We combine the lines of the
+ # paragraph using the string bitwise or (|) operator, the result
+ # being in $spaces. A character in $spaces is a space only if
+ # there was a space at that position in every line of the
+ # paragraph. $space can be used to search for contiguous spaces
+ # that occur on all lines of the paragraph. If this results in at
+ # least two columns, the paragraph is identified as a table.
+ #
+ # Note that this option appears before the various 'blockquotes'
+ # options because a table may well have whitespace to the left, in
+ # which case it must not be incorrectly recognised as a
+ # blockquote.
+ elsif ($options{tables} and do {
+ @rows = split /\n/, $_;
+ my $spaces;
+ my $max = 0;
+ my $min = length;
+ foreach my $row (@rows) {
+ ($spaces |= $row) =~ tr/ /\xff/c;
+ $min = length $row if length $row < $min;
+ $max = length $row if $max < length $row;
+ }
+ $spaces = substr $spaces, 0, $min;
+ push(@starts, 0) unless $spaces =~ /^ /;
+ while ($spaces =~ /((?:^| ) +)(?=[^ ])/g) {
+ push @ends, pos($spaces) - length $1;
+ push @starts, pos($spaces);
+ }
+ shift(@ends) if $spaces =~ /^ /;
+ push(@ends, $max);
+
+ # Two or more rows and two or more columns indicate a table.
+ 2 <= @rows and 2 <= @starts
+ }) {
+ # For each column, guess whether it should be left, centre or
+ # right aligned by examining all cells in that column for space
+ # to the left or the right. A simple majority among those cells
+ # that actually have space to one side or another decides (if no
+ # alignment gets a majority, left alignment wins by default).
+
+ my @align;
+ foreach my $col (0 .. $#starts) {
+ my @count = (0, 0, 0, 0);
+ foreach my $row (@rows) {
+ my $width = $ends[$col] - $starts[$col];
+ my $cell = substr $row, $starts[$col], $width;
+ ++ $count[($cell =~ /^ / ? 2 : 0)
+ + ($cell =~ / $/ || length($cell) < $width ? 1 : 0)];
+ }
+ $align[$col] = 0;
+ my $population = $count[1] + $count[2] + $count[3];
+ foreach (1 .. 3) {
+ if ($count[$_] * 2 > $population) {
+ $align[$col] = $_;
+ last;
+ }
+ }
+ }
+
+ foreach my $row (@rows) {
+ $row = join '', '<TR>', (map {
+ my $cell = substr $row, $starts[$_], $ends[$_] - $starts[$_];
+ $cell =~ s/^ +//;
+ $cell =~ s/ +$//;
+ string2html($cell,\%options);
+ ('<TD', $alignments[$align[$_]], '>', $cell, '</TD>')
+ } 0 .. $#starts), '</TR>';
+ }
+ my $tag = $starts[0] == 0 ? 'P' : 'BLOCKQUOTE';
+ $_ = join "\n", "<$tag><TABLE>", @rows, "</TABLE></$tag>";
+ }
+
+ # BLOCKPARAS, BLOCKCODE, BLOCKQUOTES: mark up indented paragraphs
+ # as block quotes of various kinds.
+ elsif (($options{blockparas} or $options{blockquotes}
+ or $options{blockcode}) and /^(\s+).*(?:\n\1.*)*$/) {
+ string2html($_,\%options);
+
+ # Every line in the paragraph starts with at white space, the common
+ # whitespace being in $1. Remove the common initial whitespace,
+ s/^$1//gm;
+
+ # BLOCKPARAS: treat as a paragraph.
+ if ($options{blockparas}) {
+ s|^|<P>|;
+ s|$|</P>|;
+ }
+
+ # BLOCKCODE, BLOCKQUOTES: preserve line breaks.
+ else {
+ s/\n/<BR>\n/gm;
+
+ # BLOCKCODE: preserve spaces, use fixed-width font.
+ if ($options{blockcode}) {
+ s| |&nbsp;|g;
+ s|^|<TT>|;
+ s|$|</TT>|;
+ }
+ }
+ s|^|<BLOCKQUOTE>|;
+ s|$|</BLOCKQUOTE>|;
+ }
+
+ # Didn't match any of the above, so just an ordinary paragraph.
+ else {
+ string2html($_,\%options);
+ s|^|<P>|;
+ s|$|</P>|;
+ }
+
+ # Insert <UL>, </UL>, <OL> or </OL> if this paragraph belongs to a
+ # different list type than the previous one.
+ if ($this ne $last) {
+ s|^|<$this>| if ($this ne '');
+ s|^|</$last>| if ($last ne '');
+ }
+ $last = $this;
+ $first = 0;
+ }
+ if ($this ne '') {
+ push @paras, "</$this>";
+ }
+ $_ = join "\n", @paras;
+ }
+
+ # None of PRE, LINES, PARAS specified: apply basic transformations.
+ else {
+ string2html($_,\%options);
+ }
+ return $_;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+HTML::FromText - mark up text as HTML
+
+=head1 SYNOPSIS
+
+ use HTML::FromText;
+ print text2html($text, urls => 1, paras => 1, headings => 1);
+
+=head1 DESCRIPTION
+
+The C<text2html> function marks up plain text as HTML. By default it
+expands tabs and converts HTML metacharacters into the corresponding
+entities. More complicated transformations, such as splitting the text
+into paragraphs or marking up bulleted lists, can be carried out by
+setting the appropriate options.
+
+=head1 SUMMARY OF OPTIONS
+
+These options always apply:
+
+ metachars Convert HTML metacharacters to entity references
+ urls Convert URLs to links
+ email Convert email addresses to links
+ bold Mark up words with *asterisks* in bold
+ underline Mark up words with _underscores_ as underlined
+
+You can then choose to treat the text according to one of these options:
+
+ pre Treat text as preformatted
+ lines Treat text as line-oriented
+ paras Treat text as paragraph-oriented
+
+(If more than one of these is specified, C<pre> takes precedence over
+C<lines> which takes precedence over C<paras>.) The following option
+applies when the C<lines> option is specified:
+
+ spaces Preserve spaces from the original text
+
+The following options apply when the C<paras> option is specified:
+
+ blockparas Mark up indented paragraphs as block quote
+ blockquotes Ditto, also preserve lines from original
+ blockcode Ditto, also preserve spaces from original
+ bullets Mark up bulleted paragraphs as unordered list
+ headings Mark up headings
+ numbers Mark up numbered paragraphs as ordered list
+ tables Mark up tables
+ title Mark up first paragraph as level 1 heading
+
+C<text2html> will issue a warning if it is passed nonsensical options,
+for example C<headings> but not C<paras>. These warnings can be
+supressed by setting $HTML::FromText::QUIET to true.
+
+=head1 OPTIONS
+
+=over 4
+
+=item blockparas
+
+=item blockquotes
+
+=item blockcode
+
+These options cause to C<text2html> to spot paragraphs where every line
+begins with whitespace, and mark them up as block quotes. If more than
+one of these options is specified, C<blockparas> takes precedence over
+C<blockcode>, which takes precedence over C<blockquotes>. All three
+options are ignored unless the C<paras> option is also set.
+
+The C<blockparas> option marks up the paragraph as a block quote with no
+other changes. For example,
+
+ Turing wrote,
+
+ I propose to consider the question,
+ "Can machines think?"
+
+becomes
+
+ <P>Turing wrote,</P>
+ <BLOCKQUOTE>I propose to consider the question,
+ &quot;Can machines think?&quot;</BLOCKQUOTE>
+
+The C<blockquotes> option preserves line breaks in the original text.
+For example,
+
+ From "The Waste Land":
+
+ Phlebas the Phoenecian, a fortnight dead,
+ Forgot the cry of gulls, and the deep sea swell
+
+becomes
+
+ <P>From &quot;The Waste Land&quot;:</P>
+ <BLOCKQUOTE>Phlebas the Phoenecian, a fortnight dead,<BR>
+ Forgot the cry of gulls, and the deep sea swell</BLOCKQUOTE>
+
+The C<blockcode> option preserves line breaks and spaces in the original
+text and renders the paragraph in a fixed-width font. For example:
+
+ Here's how to output numbers with commas:
+
+ sub commify {
+ local $_ = shift;
+ 1 while s/^(-?\d+)(\d{3})/$1,$2/;
+ $_;
+ }
+
+becomes
+
+ <P>Here's how to output numbers with commas:</P>
+ <BLOCKQUOTE><TT>sub&nbsp;commify&nbsp;{<BR>
+ &nbsp;&nbsp;local&nbsp;$_&nbsp;=&nbsp;shift;<BR>
+ &nbsp;&nbsp;1&nbsp;while&nbsp;s/^(-?\d+)(\d{3})/$1,$2/;<BR>
+ &nbsp;&nbsp;$_;<BR>
+ }</TT></BLOCKQUOTE>
+
+=item bold
+
+Words surrounded with asterisks are marked up in bold, so C<*abc*>
+becomes C<E<lt>BE<gt>abcE<lt>/BE<gt>>.
+
+=item bullets
+
+Spots bulleted paragraphs (beginning with optional whitespace, an
+asterisk or hyphen, and whitespace) and marks them up as an unordered
+list. Bulleted paragraphs don't have to be separated by blank lines.
+For example,
+
+ Shopping list:
+
+ * apples
+ * pears
+
+becomes
+
+ <P>Shopping list:</P>
+ <UL><LI><P>apples</P>
+ <LI><P>pears</P>
+ </UL>
+
+This option is ignored unless the C<paras> option is set.
+
+=item email
+
+Spots email addresses in the text and converts them to links. For example
+
+ Mail me at web@perl.com.
+
+becomes
+
+ Mail me at <TT><A HREF="mailto:web@perl.com">web@perl.com</A></TT>.
+
+=item headings
+
+Spots headings (paragraphs starting with numbers) and marks them up as
+headings of the appropriate level. For example,
+
+ 1. Introduction
+
+ 1.1 Background
+
+ 1.1.1 Previous work
+
+ 2. Conclusion
+
+becomes
+
+ <H1>1. Introduction</H1>
+ <H2>1.1 Background</H2>
+ <H3>1.1.1 Previous work</H3>
+ <H1>2. Conclusion</H1>
+
+This option is ignored unless the C<paras> option is set.
+
+=item lines
+
+Formats the text so as to preserve line breaks. For example,
+
+ Line 1
+ Line 2
+
+becomes
+
+ Line 1<BR>
+ Line 2
+
+If two or more of the options C<pre>, C<lines> and C<paras> are set,
+then C<pre> takes precedence over C<lines>, which takes precedence over
+C<paras>.
+
+=item metachars
+
+Converts HTML metacharacters into their corresponding entity references.
+Ampersand (C<E<amp>>) becomes C<E<amp>amp;>, less than (C<E<lt>>)
+becomes C<E<amp>lt;>, greater than (C<E<gt>>) becomes C<E<amp>gt;>, and
+quote (") becomes C<E<amp>quot;>. This option is 1 by default.
+
+=item numbers
+
+Spots numbered paragraphs (beginning with whitespace, digits, an
+optional period/parenthesis/bracket, and whitespace) and marks them up
+as an ordered list. Numbered paragraphs don't have to be separated by
+blank lines. For example,
+
+ To do:
+
+ 1. Write thesis
+ 2. Submit it
+ 3. Celebrate
+
+becomes
+
+ <P>To do:</P>
+ <OL><LI VALUE="1"><P>Write thesis</P>
+ <LI VALUE="2"><P>Submit it</P>
+ <LI VALUE="3"><P>Celebrate</P>
+ </OL>
+
+This option is ignored unless the C<paras> option is set.
+
+=item paras
+
+Format the text into paragraphs. Paragraphs are separated by one or
+more blank lines. For example,
+
+ Paragraph 1
+
+ Paragraph 2
+
+becomes
+
+ <P>Paragraph 1</P>
+ <P>Paragraph 2</P>
+
+If two or more of the options C<pre>, C<lines> and C<paras> are set,
+then C<pre> takes precedence over C<lines>, which takes precedence over
+C<paras>.
+
+=item pre
+
+Wrap the whole input in a C<E<lt>PREE<gt>> element. For example,
+
+ preformatted
+ text
+
+becomes
+
+ <PRE>preformatted
+ text</PRE>
+
+If two or more of the options C<pre>, C<lines> and C<paras> are set,
+then C<pre> takes precedence over C<lines>, which takes precedence over
+C<paras>.
+
+=item spaces
+
+Preserves spaces throughout the text. For example,
+
+ Line 1
+ Line 2
+ Line 3
+
+becomes
+
+ Line 1<BR>
+ &nbsp;Line&nbsp;&nbsp;2<BR>
+ &nbsp;&nbsp;Line&nbsp;&nbsp;&nbsp;3
+
+This option is ignored unless the C<lines> option is set.
+
+=item tables
+
+Spots tables and marks them up appropriately. Columns must be separated
+by two or more spaces (this prevents accidental incorrect recognition of
+a paragraph where interword spaces happen to line up). If there are two
+or more rows in a paragraph and all rows share the same set of (two or
+more) columns, the paragraph is assumed to be a table. For example
+
+ -e File exists.
+ -z File has zero size.
+ -s File has nonzero size (returns size).
+
+becomes
+
+ <P><TABLE>
+ <TR><TD>-e</TD><TD>File exists.</TD></TR>
+ <TR><TD>-z</TD><TD>File has zero size.</TD></TR>
+ <TR><TD>-s</TD><TD>File has nonzero size (returns size).</TD></TR>
+ </TABLE></P>
+
+C<text2html> guesses for each column whether it is intended to be left,
+centre or right aligned.
+
+This option is ignored unless the C<paras> option is set.
+
+=item title
+
+Formats the first paragraph of the text as a first-level heading.
+For example,
+
+ Paragraph 1
+
+ Paragraph 2
+
+becomes
+
+ <H1>Paragraph 1</H1>
+ <P>Paragraph 2</P>
+
+This option is ignored unless the C<paras> option is set.
+
+=item underline
+
+Words surrounded with underscores are marked up with underline, so C<_abc_>
+becomes C<E<lt>UE<gt>abcE<lt>/UE<gt>>.
+
+=item urls
+
+Spots Uniform Resource Locators (URLs) in the text and converts them
+to links. For example
+
+ See https://perl.com/.
+
+becomes
+
+ See <TT><A HREF="https://perl.com/">https://perl.com/</A></TT>.
+
+=back
+
+=head1 SEE ALSO
+
+The C<HTML::Entities> module (part of the LWP package) provides
+functions for encoding and decoding HTML entities.
+
+Tom Christiansen has a complete implementation of RFC 822 structured
+field bodies. See
+C<http://www.perl.com/CPAN/authors/Tom_Christiansen/scripts/ckaddr.gz>.
+
+Seth Golub's C<txt2html> utility does everything that C<HTML::FromText>
+does, and a few things that it would like to do. See
+C<http://www.thehouse.org/txt2html/>.
+
+RFC 822: "Standard for the Format of ARPA Internet Text Messages"
+describes the syntax of email addresses (the more esoteric features of
+structured field bodies, in particular quoted-strings, domain literals
+and comments, are not recognized by C<HTML::FromText>). See
+C<ftp://src.doc.ic.ac.uk/rfc/rfc822.txt>.
+
+RFC 1630: "Universal Resource Identifiers in WWW" lists the protocols
+that may appear in URLs. C<HTML::FromText> also recognizes "https:",
+but ignores "file:" because experience suggests that it results in too
+many false positives. See C<ftp://src.doc.ic.ac.uk/rfc/rfc1630.txt>.
+
+=head1 AUTHOR
+
+Gareth Rees C<E<lt>garethr@cre.canon.co.ukE<gt>>.
+
+=head1 COPYRIGHT
+
+Copyright (c) 1999 Canon Research Centre Europe. All rights reserved.
+This module is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=cut
diff --git a/cpan/dist/HTML-FromText/MANIFEST b/cpan/dist/HTML-FromText/MANIFEST
new file mode 100644
index 00000000..1d625b62
--- /dev/null
+++ b/cpan/dist/HTML-FromText/MANIFEST
@@ -0,0 +1,6 @@
+FromText.pm
+MANIFEST
+Makefile.PL
+README
+TODO
+t/text2html.t
diff --git a/cpan/dist/HTML-FromText/Makefile.PL b/cpan/dist/HTML-FromText/Makefile.PL
new file mode 100644
index 00000000..e73edc44
--- /dev/null
+++ b/cpan/dist/HTML-FromText/Makefile.PL
@@ -0,0 +1,5 @@
+use ExtUtils::MakeMaker;
+WriteMakefile( NAME => 'HTML::FromText',
+ VERSION_FROM => 'FromText.pm',
+ dist => { COMPRESS => 'gzip', SUFFIX => 'gz' },
+ );
diff --git a/cpan/dist/HTML-FromText/README b/cpan/dist/HTML-FromText/README
new file mode 100644
index 00000000..cf740136
--- /dev/null
+++ b/cpan/dist/HTML-FromText/README
@@ -0,0 +1,58 @@
+NAME
+ HTML::FromText - mark up text as HTML
+
+SYNOPSIS
+ use HTML::FromText;
+ print text2html($text, urls => 1, paras => 1, headings => 1);
+
+DESCRIPTION
+ The text2html function marks up plain text as HTML. By
+ default it converts HTML metacharacters into the
+ corresponding entities. More sophisticated transformations,
+ such as splitting the text into paragraphs or marking up
+ bulleted lists, can be carried out by setting the
+ appropriate options.
+
+INSTALLATION
+ perl Makefile.PL && make && make test && make install
+
+HISTORY
+ 1.005 Options 'bold' and 'underline' are more aggressive in
+ recognising markup.
+
+ 1.004 New options 'blockparas' (mark up block quotes as ordinary
+ paragraphs) and 'blockcode' (mark up block quotes in
+ fixed-width font while preserving line breaks and spaces).
+
+ Tabs are expanded throughout the text (it was a bug not to
+ do so in earlier versions because alignment could be lost,
+ block quotes not recognised, etc).
+
+ New option 'tables'.
+
+ 1.003 Recognize '&' in email addresses, as specified by RFC822.
+
+ 1.002 Much improved recognition of e-mail addresses with special
+ characters, as specified by RFC822.
+
+ When 'urls' is supplied, the prefix mailto: on email
+ addresses is preserved.
+
+ New option 'pre' wraps text in <PRE>...</PRE>.
+
+ When anchor text is in fixed-width font, the <A> element is
+ inside the <TT> element, as required by the HTML DTD.
+
+ 1.001 Original CPAN release.
+
+BUGS
+ There are transformations it doesn't do.
+
+AUTHOR
+ Gareth Rees <garethr@cre.canon.co.uk>.
+
+COPYRIGHT
+ Copyright (c) 1999 Canon Research Centre Europe. All rights
+ reserved. This module is free software; you can
+ redistribute it and/or modify it under the same terms as
+ Perl itself.
diff --git a/cpan/dist/HTML-FromText/TODO b/cpan/dist/HTML-FromText/TODO
new file mode 100644
index 00000000..6ebf6838
--- /dev/null
+++ b/cpan/dist/HTML-FromText/TODO
@@ -0,0 +1,33 @@
+---------------------------------IDEAS----------------------------------
+
+1. Have a more general means of representing paragraph transformations
+ and applying them so that text2html can be extended. Each
+ transformation would have (a) an option name, (b) a sub for testing
+ whether the transformation is applicable to the paragraph and (c) a
+ sub for applying the option. (There must be a means of passing
+ information from step (b) to step (c) as for the table
+ transformation.)
+
+ (But are there that many other transformations that can be applied on
+ a paragraph basis? Other kinds of transformations would be harder to
+ generalise like this. Think about the need to have a different idea
+ about where paragraph boundaries are depending on the options.)
+
+2. The current approach is to test the transformations for applicability
+ one by one and apply the first one that matches. It might be better
+ to test all the transformations and score them, then apply the
+ best scoring. This would allow heuristics to be applied.
+
+3. The current approach goes through the paragraphs one by one with no
+ lookahead. Multiple passes through the paragraphs could allow
+ heuristics to be applied, e.g., to distinguish nested lists from
+ adjacent lists.
+
+4. These transformations could be implemented:
+
+ * lettered paragraphs a) b) c) etc
+
+ * underlined headings
+
+ * quoted text in e-mail message (impossible to accurately given the
+ enormous diversity of quoting styles -- would 60% be good enough?)
diff --git a/cpan/dist/HTML-FromText/t/text2html.t b/cpan/dist/HTML-FromText/t/text2html.t
new file mode 100644
index 00000000..0e78a21c
--- /dev/null
+++ b/cpan/dist/HTML-FromText/t/text2html.t
@@ -0,0 +1,698 @@
+# HTML::FromText test suite (-*- cperl -*-)
+
+use strict;
+use HTML::FromText;
+$^W = 1;
+
+# Each test is represented by three data chunks, separated by a line
+# containing only a form-feed character (0x0c). The first chunk is
+# the options to pass to text2html(), the second is the input, and the
+# third the expected output.
+
+$/ = "\n\f\n";
+my @tests = ();
+while (<DATA>) {
+ chomp;
+ push @tests, $_;
+}
+my $n = @tests / 3;
+print "1..$n\n";
+foreach my $i (1..$n) {
+ my $j = 3 * ($i - 1);
+ my $input = $tests[$j + 1];
+ my $expected = $tests[$j + 2];
+ my @options = eval $tests[$j];
+ my $output = text2html($input, @options);
+ unless ($output eq $expected) {
+ print STDERR
+ "\n",'--expected','-'x60,
+ "\n",$expected,
+ "\n",'--but-found','-'x59,
+ "\n",$output,
+ "\n",'-'x70,
+ "\n";
+ print "not ";
+ }
+ print "ok $i\n";
+}
+
+__DATA__
+
+()
+
+<B>&lt;&amp;&gt;</B>
+
+&lt;B&gt;&amp;lt;&amp;amp;&amp;gt;&lt;/B&gt;
+
+
+(metachars => 0)
+
+<B>&lt;&amp;&gt;</B>
+
+<B>&lt;&amp;&gt;</B>
+
+
+(email => 1)
+
+real@email.address, real2@email.addresss.
+fake@:email.address, another@[fake].address
+mailto:me@foo.bar.com
+<tricky@subdomain.domain>
+#$%=strange!?@characters.=+=in=+=_.address
+
+<TT><A HREF="mailto:real@email.address">real@email.address</A></TT>, <TT><A HREF="mailto:real2@email.addresss">real2@email.addresss</A></TT>.
+fake@:email.address, another@[fake].address
+<TT><A HREF="mailto:me@foo.bar.com">mailto:me@foo.bar.com</A></TT>
+&lt;<TT><A HREF="mailto:tricky@subdomain.domain">tricky@subdomain.domain</A></TT>&gt;
+<TT><A HREF="mailto:#$%=strange!?@characters.=+=in=+=_.address">#$%=strange!?@characters.=+=in=+=_.address</A></TT>
+
+
+(metachars => 1, email => 1)
+
+An email address with an & in it: fred&barney@stonehenge.com.
+
+An email address with an &amp; in it: <TT><A HREF="mailto:fred&amp;barney@stonehenge.com">fred&amp;barney@stonehenge.com</A></TT>.
+
+
+(metachars => 0, email => 1)
+
+An email address with an & in it: fred&barney@stonehenge.com. Generates
+non-legal HTML, but that was what was asked for!
+
+An email address with an & in it: <TT><A HREF="mailto:fred&barney@stonehenge.com">fred&barney@stonehenge.com</A></TT>. Generates
+non-legal HTML, but that was what was asked for!
+
+
+(urls => 1)
+
+See http://foo.bar.com.
+What about http://foo.com/bar/baz?
+http://foo.com/bar/baz?quux.
+ftp://spong.gov/a/b/c/d.e/f.g/h/ should have trailing /
+...gopher://x.y.z/foo...
+mailto:mail@address.com is translated
+but mail@address.com on its own is not
+
+See <TT><A HREF="http://foo.bar.com">http://foo.bar.com</A></TT>.
+What about <TT><A HREF="http://foo.com/bar/baz">http://foo.com/bar/baz</A></TT>?
+<TT><A HREF="http://foo.com/bar/baz?quux">http://foo.com/bar/baz?quux</A></TT>.
+<TT><A HREF="ftp://spong.gov/a/b/c/d.e/f.g/h/">ftp://spong.gov/a/b/c/d.e/f.g/h/</A></TT> should have trailing /
+...<TT><A HREF="gopher://x.y.z/foo">gopher://x.y.z/foo</A></TT>...
+<TT><A HREF="mailto:mail@address.com">mailto:mail@address.com</A></TT> is translated
+but mail@address.com on its own is not
+
+
+(bold => 1, underline => 1)
+
+*Words* in *bold* _underline_ and *bold* again, but 5*4, 3_1 unaffected;
+_underline_ *more* *bold*
+_more_ _underline_
+Now *several words in bold* and _several in underline_ but
+equations like 5*x + 5*y or zeta_i + phi_i are not marked up.
+Here's a *phrase in bold
+crossing a newline* and an _underlined phrase
+crossing a newline_
+Single letter words: *a* _b_ *c* _d_
+
+<B>Words</B> in <B>bold</B> <U>underline</U> and <B>bold</B> again, but 5*4, 3_1 unaffected;
+<U>underline</U> <B>more</B> <B>bold</B>
+<U>more</U> <U>underline</U>
+Now <B>several words in bold</B> and <U>several in underline</U> but
+equations like 5*x + 5*y or zeta_i + phi_i are not marked up.
+Here's a <B>phrase in bold
+crossing a newline</B> and an <U>underlined phrase
+crossing a newline</U>
+Single letter words: <B>a</B> <U>b</U> <B>c</B> <U>d</U>
+
+
+(paras => 1, bold => 1, underline => 1)
+
+*Bold* works OK in a paragraph context
+and so does _underline_
+
+_Underline works OK_ in a paragraph context
+and *so does bold*
+
+<P><B>Bold</B> works OK in a paragraph context
+and so does <U>underline</U></P>
+<P><U>Underline works OK</U> in a paragraph context
+and <B>so does bold</B></P>
+
+
+(lines => 1)
+
+line 1
+line 2
+line 3
+line 4
+
+line 1<BR>
+line 2<BR>
+line 3<BR>
+line 4
+
+
+(lines => 1, spaces => 1)
+
+line 1
+ line 2
+ line 3
+ line 4
+ tab 1
+ tab 2
+
+line&nbsp;1<BR>
+&nbsp;line&nbsp;&nbsp;2<BR>
+&nbsp;&nbsp;line&nbsp;&nbsp;&nbsp;3<BR>
+&nbsp;&nbsp;&nbsp;line&nbsp;&nbsp;&nbsp;&nbsp;4<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tab&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tab&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2
+
+
+(paras => 1)
+
+paragraph
+one
+
+paragraph
+two
+
+a
+long
+paragraph
+three
+
+<P>paragraph
+one</P>
+<P>paragraph
+two</P>
+<P>a
+long
+paragraph
+three</P>
+
+
+(paras => 1, title => 1)
+
+this
+is the
+title
+
+and this
+the text
+
+<H1>this
+is the
+title</H1>
+<P>and this
+the text</P>
+
+
+(paras => 1, headings => 1)
+
+1. Chapter one
+
+2. Chapter two
+
+2.1 Section two point one
+
+2.1.1 Subsection two point one point one
+
+2.1.1.3 Subsubsection two point one point one point three
+
+2.1.1.3.7 Heading level 5
+
+2.1.1.3.7.1 Heading level 6 (a long heading across
+two lines)
+
+paragraph text
+
+2.1.1.3.7.1.2 There are no more than 6 heading levels.
+
+paragraph text
+
+3. Chapter three
+
+<H1>1. Chapter one</H1>
+<H1>2. Chapter two</H1>
+<H2>2.1 Section two point one</H2>
+<H3>2.1.1 Subsection two point one point one</H3>
+<H4>2.1.1.3 Subsubsection two point one point one point three</H4>
+<H5>2.1.1.3.7 Heading level 5</H5>
+<H6>2.1.1.3.7.1 Heading level 6 (a long heading across
+two lines)</H6>
+<P>paragraph text</P>
+<H6>2.1.1.3.7.1.2 There are no more than 6 heading levels.</H6>
+<P>paragraph text</P>
+<H1>3. Chapter three</H1>
+
+
+(paras => 1, bullets => 1)
+
+Ordinary text
+
+ * bulleted paragraph
+
+ * another bulleted paragarph
+ with two lines
+
+ordinary text
+
+* bullet flush left
+
+ * bullet with tabs
+
+ - bullet with hyphen
+
+<P>Ordinary text</P>
+<UL><LI><P>bulleted paragraph</P>
+<LI><P>another bulleted paragarph
+ with two lines</P>
+</UL><P>ordinary text</P>
+<UL><LI><P>bullet flush left</P>
+<LI><P>bullet with tabs</P>
+<LI><P>bullet with hyphen</P>
+</UL>
+
+
+(paras => 1, headings => 1, numbers => 1)
+
+1. This is a heading, not a numbered paragraph
+
+ 1. number one
+
+ 2. number two
+
+ 31. number thirty-one
+
+ordinary text
+
+ 1. another number one
+
+ * bulleted paragraph not recognised
+
+ 3. number three
+
+<H1>1. This is a heading, not a numbered paragraph</H1>
+<OL><LI VALUE="1"><P>number one</P>
+<LI VALUE="2"><P>number two</P>
+<LI VALUE="31"><P>number thirty-one</P>
+</OL><P>ordinary text</P>
+<OL><LI VALUE="1"><P>another number one</P>
+</OL><P> * bulleted paragraph not recognised</P>
+<OL><LI VALUE="3"><P>number three</P>
+</OL>
+
+
+(paras => 1, numbers => 1, bullets => 1)
+
+ 1. a numbered item
+
+ 2. and another
+
+ * switching to bullets starts a new list
+
+ 3. as does switching back
+
+<OL><LI VALUE="1"><P>a numbered item</P>
+<LI VALUE="2"><P>and another</P>
+</OL><UL><LI><P>switching to bullets starts a new list</P>
+</UL><OL><LI VALUE="3"><P>as does switching back</P>
+</OL>
+
+
+(paras => 1, numbers => 1, bullets => 1)
+
+* a bulleted list
+* with all the bullets next to each other
+* blah
+* blah
+
+<UL><LI><P>a bulleted list</P>
+<LI><P>with all the bullets next to each other</P>
+<LI><P>blah</P>
+<LI><P>blah</P>
+</UL>
+
+
+(paras => 1, numbers => 1, bullets => 1)
+
+1. a numbered list
+2. with all the numbers next to each other
+3. blah
+4. blah
+
+<OL><LI VALUE="1"><P>a numbered list</P>
+<LI VALUE="2"><P>with all the numbers next to each other</P>
+<LI VALUE="3"><P>blah</P>
+<LI VALUE="4"><P>blah</P>
+</OL>
+
+
+(paras => 1, numbers => 1, bullets => 1)
+
+* switching between
+111 numbers
+* and
+222 bullets
+
+<UL><LI><P>switching between</P>
+</UL><OL><LI VALUE="111"><P>numbers</P>
+</OL><UL><LI><P>and</P>
+</UL><OL><LI VALUE="222"><P>bullets</P>
+</OL>
+
+
+(paras => 1, numbers => 1, bullets => 1)
+
+Ordinary paragraphs
+* mixed up with
+numbers
+789 and
+bullets
+
+<P>Ordinary paragraphs</P>
+<UL><LI><P>mixed up with
+numbers</P>
+</UL><OL><LI VALUE="789"><P>and
+bullets</P>
+</OL>
+
+
+(paras => 1, numbers => 1, bullets => 1)
+
+000 different
+002. kinds
+003) of
+004] numbered
+001 list
+
+<OL><LI VALUE="000"><P>different</P>
+<LI VALUE="002"><P>kinds</P>
+<LI VALUE="003"><P>of</P>
+<LI VALUE="004"><P>numbered</P>
+<LI VALUE="001"><P>list</P>
+</OL>
+
+
+(paras => 1, blockquotes => 1)
+
+Here's a block quote:
+
+ line 1
+ line 2
+ line 3
+ line 4
+
+end of block quote
+
+<P>Here's a block quote:</P>
+<BLOCKQUOTE>line 1<BR>
+line 2<BR>
+line 3<BR>
+line 4</BLOCKQUOTE>
+<P>end of block quote</P>
+
+
+(paras => 1, blockquotes => 1)
+
+A block quote with variable spacing:
+
+ line 1
+ line 2
+ line 3
+
+end of block quote
+
+<P>A block quote with variable spacing:</P>
+<BLOCKQUOTE>line 1<BR>
+ line 2<BR>
+ line 3</BLOCKQUOTE>
+<P>end of block quote</P>
+
+
+(paras => 1, blockquotes => 1)
+
+Ditto, spacing goes the other way:
+
+ line 1
+ line 2
+ line 3
+
+end of block quote
+
+<P>Ditto, spacing goes the other way:</P>
+<BLOCKQUOTE> line 1<BR>
+ line 2<BR>
+line 3</BLOCKQUOTE>
+<P>end of block quote</P>
+
+
+(paras => 1, blockquotes => 1)
+
+This shouldn't be recognized as blockquote:
+
+ despite the spaces on this line,
+ and this one,
+this is just an ordinary paragraph?
+
+<P>This shouldn't be recognized as blockquote:</P>
+<P> despite the spaces on this line,
+ and this one,
+this is just an ordinary paragraph?</P>
+
+
+(paras => 1, bullets => 1, numbers => 1, blockquotes => 1)
+
+
+This is not a blockquote, despite initial and final blank lines.
+
+
+<P>This is not a blockquote, despite initial and final blank lines.</P>
+
+
+(pre => 1)
+
+preformatted
+text
+
+<PRE>preformatted
+text</PRE>
+
+
+(paras => 1, blockparas => 1)
+
+Turing wrote,
+
+ I propose to consider the question, "Can machines think?"
+ This should begin with definitions of the meaning of the
+ terms "machine" and "think".
+
+<P>Turing wrote,</P>
+<BLOCKQUOTE><P>I propose to consider the question, &quot;Can machines think?&quot;
+This should begin with definitions of the meaning of the
+terms &quot;machine&quot; and &quot;think&quot;.</P></BLOCKQUOTE>
+
+
+(paras => 1, blockquotes => 1)
+
+From "The Waste Land":
+
+ Phlebas the Phoenecian, a fortnight dead,
+ Forgot the cry of gulls, and the deep sea swell
+
+<P>From &quot;The Waste Land&quot;:</P>
+<BLOCKQUOTE>Phlebas the Phoenecian, a fortnight dead,<BR>
+Forgot the cry of gulls, and the deep sea swell</BLOCKQUOTE>
+
+
+(paras => 1, blockcode => 1)
+
+Here's how to output numbers with commas (from perlfaq4):
+
+ sub commify {
+ local $_ = shift;
+ 1 while s/^(-?\d+)(\d{3})/$1,$2/;
+ $_;
+ }
+
+<P>Here's how to output numbers with commas (from perlfaq4):</P>
+<BLOCKQUOTE><TT>sub&nbsp;commify&nbsp;{<BR>
+&nbsp;&nbsp;local&nbsp;$_&nbsp;=&nbsp;shift;<BR>
+&nbsp;&nbsp;1&nbsp;while&nbsp;s/^(-?\d+)(\d{3})/$1,$2/;<BR>
+&nbsp;&nbsp;$_;<BR>
+}</TT></BLOCKQUOTE>
+
+
+()
+
+Line mixing tabs and metachars:
+ &&& <>
+
+Line mixing tabs and metachars:
+ &amp;&amp;&amp; &lt;&gt;
+
+
+(paras => 1, tables => 1)
+
+ 1, 1 1, 2 1, 3
+ 2, 1 2, 2 2, 3
+ 3, 1 3, 2 3, 3
+
+<BLOCKQUOTE><TABLE>
+<TR><TD>1, 1</TD><TD>1, 2</TD><TD>1, 3</TD></TR>
+<TR><TD>2, 1</TD><TD>2, 2</TD><TD>2, 3</TD></TR>
+<TR><TD>3, 1</TD><TD>3, 2</TD><TD>3, 3</TD></TR>
+</TABLE></BLOCKQUOTE>
+
+
+(paras => 1, tables => 1)
+
+Tables can be left-aligned:
+
+1, 1 1, 2 1, 3
+2, 1 2, 2 2, 3
+3, 1 3, 2 3, 3
+
+<P>Tables can be left-aligned:</P>
+<P><TABLE>
+<TR><TD>1, 1</TD><TD>1, 2</TD><TD>1, 3</TD></TR>
+<TR><TD>2, 1</TD><TD>2, 2</TD><TD>2, 3</TD></TR>
+<TR><TD>3, 1</TD><TD>3, 2</TD><TD>3, 3</TD></TR>
+</TABLE></P>
+
+
+(paras => 1, tables => 1)
+
+ despite its appearance
+ this table has
+ only two columns
+
+<BLOCKQUOTE><TABLE>
+<TR><TD>despite its</TD><TD>appearance</TD></TR>
+<TR><TD>this table</TD><TD>has</TD></TR>
+<TR><TD>only two</TD><TD>columns</TD></TR>
+</TABLE></BLOCKQUOTE>
+
+
+(paras => 1, tables => 1)
+
+ tables
+ must
+ have
+ two
+ columns
+
+<P> tables
+ must
+ have
+ two
+ columns</P>
+
+
+(paras => 1, tables => 1)
+
+ tables can
+ have only
+ one space
+ at the left
+
+<BLOCKQUOTE><TABLE>
+<TR><TD>tables</TD><TD>can</TD></TR>
+<TR><TD>have</TD><TD>only</TD></TR>
+<TR><TD>one</TD><TD>space</TD></TR>
+<TR><TD>at the</TD><TD>left</TD></TR>
+</TABLE></BLOCKQUOTE>
+
+
+(paras => 1, tables => 1)
+
+ tables must have two rows
+
+<P> tables must have two rows</P>
+
+
+(paras => 1, tables => 1)
+
+ this table has varying lengths of column
+ at the right
+
+<BLOCKQUOTE><TABLE>
+<TR><TD>this</TD><TD>table</TD><TD>has varying lengths of column</TD></TR>
+<TR><TD>at</TD><TD>the</TD><TD>right</TD></TR>
+</TABLE></BLOCKQUOTE>
+
+
+(paras => 1, tables => 1)
+
+This table contains right-aligned cells:
+
+ p p^2 p^3 p^4
+ 2 4 8 16
+ 3 9 27 81
+ 5 25 125 625
+ 7 49 343 2401
+
+<P>This table contains right-aligned cells:</P>
+<BLOCKQUOTE><TABLE>
+<TR><TD>p</TD><TD ALIGN="RIGHT">p^2</TD><TD ALIGN="RIGHT">p^3</TD><TD ALIGN="RIGHT">p^4</TD></TR>
+<TR><TD>2</TD><TD ALIGN="RIGHT">4</TD><TD ALIGN="RIGHT">8</TD><TD ALIGN="RIGHT">16</TD></TR>
+<TR><TD>3</TD><TD ALIGN="RIGHT">9</TD><TD ALIGN="RIGHT">27</TD><TD ALIGN="RIGHT">81</TD></TR>
+<TR><TD>5</TD><TD ALIGN="RIGHT">25</TD><TD ALIGN="RIGHT">125</TD><TD ALIGN="RIGHT">625</TD></TR>
+<TR><TD>7</TD><TD ALIGN="RIGHT">49</TD><TD ALIGN="RIGHT">343</TD><TD ALIGN="RIGHT">2401</TD></TR>
+</TABLE></BLOCKQUOTE>
+
+
+(paras => 1, tables => 1)
+
+This table contains metacharacters:
+
+ & &amp;
+ < &lt;
+ > &gt;
+
+<P>This table contains metacharacters:</P>
+<BLOCKQUOTE><TABLE>
+<TR><TD>&amp;</TD><TD>&amp;amp;</TD></TR>
+<TR><TD>&lt;</TD><TD>&amp;lt;</TD></TR>
+<TR><TD>&gt;</TD><TD>&amp;gt;</TD></TR>
+</TABLE></BLOCKQUOTE>
+
+
+(paras => 1, tables => 1)
+
+Here's a table with centre-aligned columns:
+
+1 1
+2 1 1
+3 1 2 1
+4 1 3 3 1
+5 1 4 6 4 1
+
+<P>Here's a table with centre-aligned columns:</P>
+<P><TABLE>
+<TR><TD>1</TD><TD ALIGN="CENTER">1</TD></TR>
+<TR><TD>2</TD><TD ALIGN="CENTER">1 1</TD></TR>
+<TR><TD>3</TD><TD ALIGN="CENTER">1 2 1</TD></TR>
+<TR><TD>4</TD><TD ALIGN="CENTER">1 3 3 1</TD></TR>
+<TR><TD>5</TD><TD ALIGN="CENTER">1 4 6 4 1</TD></TR>
+</TABLE></P>
+
+
+(paras => 1, blockparas => 1, tables => 1)
+
+ This should get recognised
+ as a blockquote despite
+ the unorthodox spacing.
+
+ But this is a table
+ XXX XXXX XX X XXXXX
+
+<BLOCKQUOTE><P>This should get recognised
+as a blockquote despite
+the unorthodox spacing.</P></BLOCKQUOTE>
+<BLOCKQUOTE><TABLE>
+<TR><TD>But</TD><TD>this</TD><TD>is</TD><TD>a</TD><TD>table</TD></TR>
+<TR><TD>XXX</TD><TD>XXXX</TD><TD>XX</TD><TD>X</TD><TD>XXXXX</TD></TR>
+</TABLE></BLOCKQUOTE>
+
diff --git a/cpan/lib/HTML/FromText.pm b/cpan/lib/HTML/FromText.pm
new file mode 100644
index 00000000..2b7f1753
--- /dev/null
+++ b/cpan/lib/HTML/FromText.pm
@@ -0,0 +1,736 @@
+require 5.004;
+use strict;
+
+package HTML::FromText;
+use Carp;
+use Exporter;
+use Text::Tabs 'expand';
+use vars qw($RCSID $VERSION $QUIET @EXPORT @ISA);
+
+@ISA = qw(Exporter);
+@EXPORT = qw(text2html);
+$RCSID = q$Id: FromText.pm,v 1.14 1999/10/06 10:53:37 garethr Exp $;
+$VERSION = '1.005';
+$QUIET = 0;
+
+# This list of protocols is taken from RFC 1630: "Universal Resource
+# Identifiers in WWW". The protocol "file" is omitted because
+# experience suggests that it results in many false positives; "https"
+# postdates RFC 1630. The protocol "mailto" is handled separately, by
+# the email address matching code.
+
+my $protocol = join '|',
+ qw(afs cid ftp gopher http https mid news nntp prospero telnet wais);
+
+# The regular expressions matching email addresses use the following
+# syntax elements from RFC 822. I can't use the full details of
+# structured field bodies, because that would give too many false
+# positives. (See Tom Christiansen's ckaddr.gz for a full
+# implementation of the RFC 822.)
+#
+# addr-spec = local-part "@" domain
+# local-part = word *("." word)
+# word = atom
+# domain = sub-domain *("." sub-domain)
+# sub-domain = domain-ref
+# domain-ref = atom
+# atom = 1*<any CHAR except specials, SPACE and CTLs>
+# specials = "(" / ")" / "<" / ">" / "@" / "," / ";" / ":" / "\"
+# / <"> / "." / "[" / "]"
+#
+# I have ignored quoting, domain literals and comments.
+#
+# Note that '&' can legally appear in email addresses (for example,
+# 'fred&barney@stonehenge.com'). If the 'metachars' option is passed to
+# text2html then I must use '&amp;' to recognize '&'. Thus the regular
+# expression $atom[0] recognizes an atom in the case where the option
+# 'metachars' is false; $atom[1] recognizes an atom in the case where
+# 'metachars' is true. Similarly for the regular expressions $email[0]
+# and $email[1], which recognize email addresses.
+
+my @atom =
+ ( '[!#$%&\'*+\\-/0123456789=?ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~]+',
+ '(?:&amp;|[!#$%\'*+\\-/0123456789=?ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~])+' );
+
+my @email = ( "$atom[0](\\.$atom[0])*\@$atom[0](\\.$atom[0])*",
+ "$atom[1](\\.$atom[1])*\@$atom[1](\\.$atom[1])*" );
+
+my @alignments = ( '', '', ' ALIGN="RIGHT"', ' ALIGN="CENTER"' );
+
+sub string2html ($$) {
+ my $options = $_[1];
+ for ($_[0]) { # Modify in-place.
+
+ # METACHARS: mark up HTML metacharacters as corresponding entities.
+ if ($options->{metachars}) {
+ s/&/&amp;/g;
+ s/</&lt;/g;
+ s/>/&gt;/g;
+ s/\"/&quot;/g;
+ }
+
+ # EMAIL, URLS: spot electronic mail addresses and turn them into
+ # links. Note (1) if `urls' is set but not `email', then only
+ # addresses prefixed by `mailto:' will be marked up; (2) that we leave
+ # the `mailto:' prefix in the anchor text.
+ if ($options->{email} or $options->{urls}) {
+ s|((?:mailto:)?)($email[$options->{metachars}?1:0])|
+ ($options->{email} or $1)
+ ? "<TT><A HREF=\"mailto:$2\">$1$2</A></TT>" : $2|gex;
+ }
+
+ # URLS: mark up URLs as links (note that `mailto' links are handled
+ # above).
+ if ($options->{urls}) {
+ s|\b((?:$protocol):\S+[\w/])|<TT><A HREF="$1">$1</A></TT>|g;
+ }
+
+ # BOLD: mark up words in *asterisks* as bold.
+ if ($options->{bold}) {
+ s#(^|\s)\*([^*]+)\*(?=\s|$)#$1<B>$2</B>#g;
+ }
+
+ # UNDERLINE: mark up words in _underscores_ as underlined.
+ if ($options->{underline}) {
+ s#(^|\s)_([^_]+?)_(?=\s|$)#$1<U>$2</U>#g;
+ }
+ }
+
+ return $_[0];
+}
+
+sub text2html {
+ local $_ = shift; # Take a copy; don't modify in-place.
+ return $_ unless $_;
+
+ my %options = ( metachars => 1, @_ );
+
+ # Check options for sanity.
+ unless ($QUIET) {
+ carp "text2html: `spaces' will be ignored since `lines' is not specified"
+ if $options{spaces} and not $options{lines};
+ if ($options{paras}) {
+ if ($options{blockparas}) {
+ foreach my $o (qw(blockquotes blockcode)) {
+ carp "text2html: `$o' will be ignored since `blockparas' is specified" if $options{$o};
+ }
+ } elsif ($options{blockcode} and $options{blockquotes}) {
+ carp "text2html: `blockquotes' will be ignored since `blockcode' is specified";
+ }
+ } else {
+ foreach my $o (qw(bullets numbers blockquotes blockparas blockcode
+ title headings tables)) {
+ carp "text2html: `$o' will be ignored since `paras' is not specified"
+ if $options{$o};
+ }
+ }
+ }
+
+ # Expand tabs.
+ $_ = join "\n", expand(split /\r?\n/);
+
+ # PRE: put text in <PRE> element.
+ if ($options{pre}) {
+ string2html($_, \%options);
+ s|^|<PRE>|;
+ s|$|</PRE>|;
+ }
+
+ # LINES: preserve line breaks from original text.
+ elsif ($options{lines}) {
+ string2html($_, \%options);
+ s/\n/<BR>\n/gm;
+
+ # SPACES: preserve spaces from original text.
+ s/ /&nbsp;/g if $options{spaces};
+ }
+
+ # PARAS: treat text as sequence of paragraphs.
+ elsif ($options{paras}) {
+ my @paras;
+
+ # Remove initial and final blank lines.
+ s/^(?:\s*?\n)+//;
+ s/(?:\n\s*?)+$//;
+
+ # Split on a different regexp depending on what kinds of paragraphs
+ # will be recognised later. The idea is that bulleted lists like
+ # this:
+ #
+ # * item 1
+ # * item 2
+ #
+ # will be recognised as multiple paragraphs if the 'bullets' option
+ # is supplied, but as a single paragraph otherwise. (Similarly for
+ # numbered lists).
+ if ($options{bullets} and $options{numbers}) {
+ @paras = split
+ /(?:\s*\n)+ # (0 or more blank lines, followed by LF)
+ (?:\s*\n # Either 1 or more blank lines, or
+ |(?=\s*[*-]\s+ # bulleted item follows, or
+ |\s*(?:\d+)[.\)\]]?\s+)) # numbered item follows
+ /x;
+ } elsif ($options{bullets}) {
+ @paras = split
+ /(?:\s*\n)+ # (0 or more blank lines, followed by LF)
+ (?:\s*\n # Either 1 or more blank lines, or
+ |(?=\s*[*-]\s+)) # bulleted item follows.
+ /x;
+ } elsif ($options{numbers}) {
+ @paras = split
+ /(?:\s*\n)+ # (0 or more blank lines, followed by LF)
+ (?:\s*\n # Either 1 or more blank lines, or
+ |(?=\s*(?:\d+)[.\)\]]?\s+)) # numbered item follows.
+ /x;
+ } else {
+ @paras = split
+ /\s*\n(?:\s*\n)+ # 1 or more blank lines.
+ /x;
+ }
+
+ my $last = ''; # List type (OL/UL) of last paragraph
+ my $this; # List type (OL/UL) of this paragraph
+ my $first = 1; # True if this is first paragraph
+
+ foreach (@paras) {
+ my (@rows,@starts,@ends);
+ $this = '';
+
+ # TITLE: mark up first paragraph as level-1 heading.
+ if ($options{title} and $first) {
+ string2html($_,\%options);
+ s|^|<H1>|;
+ s|$|</H1>|;
+ }
+
+ # HEADINGS: mark up paragraphs with numbers at the start of the
+ # first line as headings.
+ elsif ($options{headings} and /^(\d+(\.\d+)*)\.?\s/) {
+ my $number = $1;
+ my $level = 1 + ($number =~ tr/././);
+ $level = 6 if $level > 6;
+ string2html($_,\%options);
+ s|^|<H$level>|;
+ s|$|</H$level>|;
+ }
+
+ # BULLETS: mark up paragraphs starting with bullets as items in an
+ # unnumbered list.
+ elsif ($options{bullets} and /^\s*[*-]\s+/) {
+ string2html($_,\%options);
+ s/^\s*[*-]\s+/<LI><P>/;
+ s|$|</P>|;
+ $this = 'UL';
+ }
+
+ # NUMBERS: mark up paragraphs starting with numbers as items in a
+ # numbered list.
+ elsif ($options{numbers} and /^\s*(\d+)[.\)\]]?\s+/) {
+ string2html($_,\%options);
+ s/^\s*(\d+)[.\)\]]?\s+/<LI VALUE="$1"><P>/;
+ s|$|</P>|;
+ $this = 'OL';
+ }
+
+ # TABLES: spot and mark up tables. We combine the lines of the
+ # paragraph using the string bitwise or (|) operator, the result
+ # being in $spaces. A character in $spaces is a space only if
+ # there was a space at that position in every line of the
+ # paragraph. $space can be used to search for contiguous spaces
+ # that occur on all lines of the paragraph. If this results in at
+ # least two columns, the paragraph is identified as a table.
+ #
+ # Note that this option appears before the various 'blockquotes'
+ # options because a table may well have whitespace to the left, in
+ # which case it must not be incorrectly recognised as a
+ # blockquote.
+ elsif ($options{tables} and do {
+ @rows = split /\n/, $_;
+ my $spaces;
+ my $max = 0;
+ my $min = length;
+ foreach my $row (@rows) {
+ ($spaces |= $row) =~ tr/ /\xff/c;
+ $min = length $row if length $row < $min;
+ $max = length $row if $max < length $row;
+ }
+ $spaces = substr $spaces, 0, $min;
+ push(@starts, 0) unless $spaces =~ /^ /;
+ while ($spaces =~ /((?:^| ) +)(?=[^ ])/g) {
+ push @ends, pos($spaces) - length $1;
+ push @starts, pos($spaces);
+ }
+ shift(@ends) if $spaces =~ /^ /;
+ push(@ends, $max);
+
+ # Two or more rows and two or more columns indicate a table.
+ 2 <= @rows and 2 <= @starts
+ }) {
+ # For each column, guess whether it should be left, centre or
+ # right aligned by examining all cells in that column for space
+ # to the left or the right. A simple majority among those cells
+ # that actually have space to one side or another decides (if no
+ # alignment gets a majority, left alignment wins by default).
+
+ my @align;
+ foreach my $col (0 .. $#starts) {
+ my @count = (0, 0, 0, 0);
+ foreach my $row (@rows) {
+ my $width = $ends[$col] - $starts[$col];
+ my $cell = substr $row, $starts[$col], $width;
+ ++ $count[($cell =~ /^ / ? 2 : 0)
+ + ($cell =~ / $/ || length($cell) < $width ? 1 : 0)];
+ }
+ $align[$col] = 0;
+ my $population = $count[1] + $count[2] + $count[3];
+ foreach (1 .. 3) {
+ if ($count[$_] * 2 > $population) {
+ $align[$col] = $_;
+ last;
+ }
+ }
+ }
+
+ foreach my $row (@rows) {
+ $row = join '', '<TR>', (map {
+ my $cell = substr $row, $starts[$_], $ends[$_] - $starts[$_];
+ $cell =~ s/^ +//;
+ $cell =~ s/ +$//;
+ string2html($cell,\%options);
+ ('<TD', $alignments[$align[$_]], '>', $cell, '</TD>')
+ } 0 .. $#starts), '</TR>';
+ }
+ my $tag = $starts[0] == 0 ? 'P' : 'BLOCKQUOTE';
+ $_ = join "\n", "<$tag><TABLE>", @rows, "</TABLE></$tag>";
+ }
+
+ # BLOCKPARAS, BLOCKCODE, BLOCKQUOTES: mark up indented paragraphs
+ # as block quotes of various kinds.
+ elsif (($options{blockparas} or $options{blockquotes}
+ or $options{blockcode}) and /^(\s+).*(?:\n\1.*)*$/) {
+ string2html($_,\%options);
+
+ # Every line in the paragraph starts with at white space, the common
+ # whitespace being in $1. Remove the common initial whitespace,
+ s/^$1//gm;
+
+ # BLOCKPARAS: treat as a paragraph.
+ if ($options{blockparas}) {
+ s|^|<P>|;
+ s|$|</P>|;
+ }
+
+ # BLOCKCODE, BLOCKQUOTES: preserve line breaks.
+ else {
+ s/\n/<BR>\n/gm;
+
+ # BLOCKCODE: preserve spaces, use fixed-width font.
+ if ($options{blockcode}) {
+ s| |&nbsp;|g;
+ s|^|<TT>|;
+ s|$|</TT>|;
+ }
+ }
+ s|^|<BLOCKQUOTE>|;
+ s|$|</BLOCKQUOTE>|;
+ }
+
+ # Didn't match any of the above, so just an ordinary paragraph.
+ else {
+ string2html($_,\%options);
+ s|^|<P>|;
+ s|$|</P>|;
+ }
+
+ # Insert <UL>, </UL>, <OL> or </OL> if this paragraph belongs to a
+ # different list type than the previous one.
+ if ($this ne $last) {
+ s|^|<$this>| if ($this ne '');
+ s|^|</$last>| if ($last ne '');
+ }
+ $last = $this;
+ $first = 0;
+ }
+ if ($this ne '') {
+ push @paras, "</$this>";
+ }
+ $_ = join "\n", @paras;
+ }
+
+ # None of PRE, LINES, PARAS specified: apply basic transformations.
+ else {
+ string2html($_,\%options);
+ }
+ return $_;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+HTML::FromText - mark up text as HTML
+
+=head1 SYNOPSIS
+
+ use HTML::FromText;
+ print text2html($text, urls => 1, paras => 1, headings => 1);
+
+=head1 DESCRIPTION
+
+The C<text2html> function marks up plain text as HTML. By default it
+expands tabs and converts HTML metacharacters into the corresponding
+entities. More complicated transformations, such as splitting the text
+into paragraphs or marking up bulleted lists, can be carried out by
+setting the appropriate options.
+
+=head1 SUMMARY OF OPTIONS
+
+These options always apply:
+
+ metachars Convert HTML metacharacters to entity references
+ urls Convert URLs to links
+ email Convert email addresses to links
+ bold Mark up words with *asterisks* in bold
+ underline Mark up words with _underscores_ as underlined
+
+You can then choose to treat the text according to one of these options:
+
+ pre Treat text as preformatted
+ lines Treat text as line-oriented
+ paras Treat text as paragraph-oriented
+
+(If more than one of these is specified, C<pre> takes precedence over
+C<lines> which takes precedence over C<paras>.) The following option
+applies when the C<lines> option is specified:
+
+ spaces Preserve spaces from the original text
+
+The following options apply when the C<paras> option is specified:
+
+ blockparas Mark up indented paragraphs as block quote
+ blockquotes Ditto, also preserve lines from original
+ blockcode Ditto, also preserve spaces from original
+ bullets Mark up bulleted paragraphs as unordered list
+ headings Mark up headings
+ numbers Mark up numbered paragraphs as ordered list
+ tables Mark up tables
+ title Mark up first paragraph as level 1 heading
+
+C<text2html> will issue a warning if it is passed nonsensical options,
+for example C<headings> but not C<paras>. These warnings can be
+supressed by setting $HTML::FromText::QUIET to true.
+
+=head1 OPTIONS
+
+=over 4
+
+=item blockparas
+
+=item blockquotes
+
+=item blockcode
+
+These options cause to C<text2html> to spot paragraphs where every line
+begins with whitespace, and mark them up as block quotes. If more than
+one of these options is specified, C<blockparas> takes precedence over
+C<blockcode>, which takes precedence over C<blockquotes>. All three
+options are ignored unless the C<paras> option is also set.
+
+The C<blockparas> option marks up the paragraph as a block quote with no
+other changes. For example,
+
+ Turing wrote,
+
+ I propose to consider the question,
+ "Can machines think?"
+
+becomes
+
+ <P>Turing wrote,</P>
+ <BLOCKQUOTE>I propose to consider the question,
+ &quot;Can machines think?&quot;</BLOCKQUOTE>
+
+The C<blockquotes> option preserves line breaks in the original text.
+For example,
+
+ From "The Waste Land":
+
+ Phlebas the Phoenecian, a fortnight dead,
+ Forgot the cry of gulls, and the deep sea swell
+
+becomes
+
+ <P>From &quot;The Waste Land&quot;:</P>
+ <BLOCKQUOTE>Phlebas the Phoenecian, a fortnight dead,<BR>
+ Forgot the cry of gulls, and the deep sea swell</BLOCKQUOTE>
+
+The C<blockcode> option preserves line breaks and spaces in the original
+text and renders the paragraph in a fixed-width font. For example:
+
+ Here's how to output numbers with commas:
+
+ sub commify {
+ local $_ = shift;
+ 1 while s/^(-?\d+)(\d{3})/$1,$2/;
+ $_;
+ }
+
+becomes
+
+ <P>Here's how to output numbers with commas:</P>
+ <BLOCKQUOTE><TT>sub&nbsp;commify&nbsp;{<BR>
+ &nbsp;&nbsp;local&nbsp;$_&nbsp;=&nbsp;shift;<BR>
+ &nbsp;&nbsp;1&nbsp;while&nbsp;s/^(-?\d+)(\d{3})/$1,$2/;<BR>
+ &nbsp;&nbsp;$_;<BR>
+ }</TT></BLOCKQUOTE>
+
+=item bold
+
+Words surrounded with asterisks are marked up in bold, so C<*abc*>
+becomes C<E<lt>BE<gt>abcE<lt>/BE<gt>>.
+
+=item bullets
+
+Spots bulleted paragraphs (beginning with optional whitespace, an
+asterisk or hyphen, and whitespace) and marks them up as an unordered
+list. Bulleted paragraphs don't have to be separated by blank lines.
+For example,
+
+ Shopping list:
+
+ * apples
+ * pears
+
+becomes
+
+ <P>Shopping list:</P>
+ <UL><LI><P>apples</P>
+ <LI><P>pears</P>
+ </UL>
+
+This option is ignored unless the C<paras> option is set.
+
+=item email
+
+Spots email addresses in the text and converts them to links. For example
+
+ Mail me at web@perl.com.
+
+becomes
+
+ Mail me at <TT><A HREF="mailto:web@perl.com">web@perl.com</A></TT>.
+
+=item headings
+
+Spots headings (paragraphs starting with numbers) and marks them up as
+headings of the appropriate level. For example,
+
+ 1. Introduction
+
+ 1.1 Background
+
+ 1.1.1 Previous work
+
+ 2. Conclusion
+
+becomes
+
+ <H1>1. Introduction</H1>
+ <H2>1.1 Background</H2>
+ <H3>1.1.1 Previous work</H3>
+ <H1>2. Conclusion</H1>
+
+This option is ignored unless the C<paras> option is set.
+
+=item lines
+
+Formats the text so as to preserve line breaks. For example,
+
+ Line 1
+ Line 2
+
+becomes
+
+ Line 1<BR>
+ Line 2
+
+If two or more of the options C<pre>, C<lines> and C<paras> are set,
+then C<pre> takes precedence over C<lines>, which takes precedence over
+C<paras>.
+
+=item metachars
+
+Converts HTML metacharacters into their corresponding entity references.
+Ampersand (C<E<amp>>) becomes C<E<amp>amp;>, less than (C<E<lt>>)
+becomes C<E<amp>lt;>, greater than (C<E<gt>>) becomes C<E<amp>gt;>, and
+quote (") becomes C<E<amp>quot;>. This option is 1 by default.
+
+=item numbers
+
+Spots numbered paragraphs (beginning with whitespace, digits, an
+optional period/parenthesis/bracket, and whitespace) and marks them up
+as an ordered list. Numbered paragraphs don't have to be separated by
+blank lines. For example,
+
+ To do:
+
+ 1. Write thesis
+ 2. Submit it
+ 3. Celebrate
+
+becomes
+
+ <P>To do:</P>
+ <OL><LI VALUE="1"><P>Write thesis</P>
+ <LI VALUE="2"><P>Submit it</P>
+ <LI VALUE="3"><P>Celebrate</P>
+ </OL>
+
+This option is ignored unless the C<paras> option is set.
+
+=item paras
+
+Format the text into paragraphs. Paragraphs are separated by one or
+more blank lines. For example,
+
+ Paragraph 1
+
+ Paragraph 2
+
+becomes
+
+ <P>Paragraph 1</P>
+ <P>Paragraph 2</P>
+
+If two or more of the options C<pre>, C<lines> and C<paras> are set,
+then C<pre> takes precedence over C<lines>, which takes precedence over
+C<paras>.
+
+=item pre
+
+Wrap the whole input in a C<E<lt>PREE<gt>> element. For example,
+
+ preformatted
+ text
+
+becomes
+
+ <PRE>preformatted
+ text</PRE>
+
+If two or more of the options C<pre>, C<lines> and C<paras> are set,
+then C<pre> takes precedence over C<lines>, which takes precedence over
+C<paras>.
+
+=item spaces
+
+Preserves spaces throughout the text. For example,
+
+ Line 1
+ Line 2
+ Line 3
+
+becomes
+
+ Line 1<BR>
+ &nbsp;Line&nbsp;&nbsp;2<BR>
+ &nbsp;&nbsp;Line&nbsp;&nbsp;&nbsp;3
+
+This option is ignored unless the C<lines> option is set.
+
+=item tables
+
+Spots tables and marks them up appropriately. Columns must be separated
+by two or more spaces (this prevents accidental incorrect recognition of
+a paragraph where interword spaces happen to line up). If there are two
+or more rows in a paragraph and all rows share the same set of (two or
+more) columns, the paragraph is assumed to be a table. For example
+
+ -e File exists.
+ -z File has zero size.
+ -s File has nonzero size (returns size).
+
+becomes
+
+ <P><TABLE>
+ <TR><TD>-e</TD><TD>File exists.</TD></TR>
+ <TR><TD>-z</TD><TD>File has zero size.</TD></TR>
+ <TR><TD>-s</TD><TD>File has nonzero size (returns size).</TD></TR>
+ </TABLE></P>
+
+C<text2html> guesses for each column whether it is intended to be left,
+centre or right aligned.
+
+This option is ignored unless the C<paras> option is set.
+
+=item title
+
+Formats the first paragraph of the text as a first-level heading.
+For example,
+
+ Paragraph 1
+
+ Paragraph 2
+
+becomes
+
+ <H1>Paragraph 1</H1>
+ <P>Paragraph 2</P>
+
+This option is ignored unless the C<paras> option is set.
+
+=item underline
+
+Words surrounded with underscores are marked up with underline, so C<_abc_>
+becomes C<E<lt>UE<gt>abcE<lt>/UE<gt>>.
+
+=item urls
+
+Spots Uniform Resource Locators (URLs) in the text and converts them
+to links. For example
+
+ See https://perl.com/.
+
+becomes
+
+ See <TT><A HREF="https://perl.com/">https://perl.com/</A></TT>.
+
+=back
+
+=head1 SEE ALSO
+
+The C<HTML::Entities> module (part of the LWP package) provides
+functions for encoding and decoding HTML entities.
+
+Tom Christiansen has a complete implementation of RFC 822 structured
+field bodies. See
+C<http://www.perl.com/CPAN/authors/Tom_Christiansen/scripts/ckaddr.gz>.
+
+Seth Golub's C<txt2html> utility does everything that C<HTML::FromText>
+does, and a few things that it would like to do. See
+C<http://www.thehouse.org/txt2html/>.
+
+RFC 822: "Standard for the Format of ARPA Internet Text Messages"
+describes the syntax of email addresses (the more esoteric features of
+structured field bodies, in particular quoted-strings, domain literals
+and comments, are not recognized by C<HTML::FromText>). See
+C<ftp://src.doc.ic.ac.uk/rfc/rfc822.txt>.
+
+RFC 1630: "Universal Resource Identifiers in WWW" lists the protocols
+that may appear in URLs. C<HTML::FromText> also recognizes "https:",
+but ignores "file:" because experience suggests that it results in too
+many false positives. See C<ftp://src.doc.ic.ac.uk/rfc/rfc1630.txt>.
+
+=head1 AUTHOR
+
+Gareth Rees C<E<lt>garethr@cre.canon.co.ukE<gt>>.
+
+=head1 COPYRIGHT
+
+Copyright (c) 1999 Canon Research Centre Europe. All rights reserved.
+This module is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=cut