﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-Python, Java, Life, etc-随笔分类-Perl</title><link>http://www.blogjava.net/pyguru/category/494.html</link><description>A blog of technology and life.</description><language>zh-cn</language><lastBuildDate>Fri, 02 Mar 2007 01:59:37 GMT</lastBuildDate><pubDate>Fri, 02 Mar 2007 01:59:37 GMT</pubDate><ttl>60</ttl><item><title>Parsing MIME &amp; HTML</title><link>http://www.blogjava.net/pyguru/archive/2005/02/19/1312.html</link><dc:creator>pyguru</dc:creator><author>pyguru</author><pubDate>Fri, 18 Feb 2005 16:33:00 GMT</pubDate><guid>http://www.blogjava.net/pyguru/archive/2005/02/19/1312.html</guid><wfw:comment>http://www.blogjava.net/pyguru/comments/1312.html</wfw:comment><comments>http://www.blogjava.net/pyguru/archive/2005/02/19/1312.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/pyguru/comments/commentRss/1312.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/pyguru/services/trackbacks/1312.html</trackback:ping><description><![CDATA[


<!-- $Id: top,v 1.1.1.1 2005/01/01 14:55:47 lem Exp $ -->
<div class="header">
<a class="toplink" href="http://mipagina.cantv.net/lem/perl/index-en.html">
<img class="logo" alt="Logo" src="http://mipagina.cantv.net/lem/images/perl.gif"></a><span class="title">Parsing MIME &amp; HTML</span><br>

<!-- $Id: navbar,v 1.1.1.1 2005/01/02 00:14:27 lem Exp $ -->
<span class="navbar"><a href="http://mipagina.cantv.net/lem/index-en.html"></a></span></div>
<div class="main"><p>Understanding an email message encoded with
MIME can be very, very, very difficult. It can get frustrating due to
the number of options and different ways to do the actual
encoding. Now add to that the sometimes too-liberal interpretations of
the relevant RFCs by the email client designers and you will begin to
get the idea. This article will show you how this task can be
laughably simple thanks to Perl's extensive bag of tricks, <a href="http://www.cpan.org/">CPAN<img alt="no alt defined" src="http://mipagina.cantv.net/lem/images/icon_external_link.gif" height="13" width="14"></a>.
</p>

<p>
I started out with a simple and straightforward mission: Fetch an
Email from a POP mailbox and display it in a 7-bit, text-only capable
device. This article describes the different stages for a simple tool
that accomplishes this task, written in Perl with a lot of help from
CPAN modules. I hope this to be useful to other Perl folks who might
have a similar mission.   Let's discuss each part of this task
in turn, as we read through <tt>mfetch</tt>, the script I prepared as
an example. Keep in mind that TIMTOWTDI.</p>


<h2>Setting up the script</h2>

<p>

</p>

<p>
The first thing, as you know, is loading up all of the modules I
will be using. I'm sure you already know <tt>strict</tt> and
<tt>warnings</tt>. We'll see how do we use the rest of the modules a
bit later.</p>
<pre class="listing">    1: #!/usr/bin/perl<br>    2: <br>    3: # This script is (c) 2002 Luis E. Muñoz, All Rights Reserved<br>    4: # This code can be used under the same terms as Perl itself. It comes<br>    5: # with absolutely NO WARRANTY. Use at your own risk.<br>    6: <br>    7: use strict;<br>    8: use warnings;<br>    9: use IO::File;<br>   10: use Net::POP3;<br>   11: use NetAddr::IP;<br>   12: use Getopt::Std;<br>   13: use MIME::Parser;<br>   14: use HTML::Parser;<br>   15: use Unicode::Map8;<br>   16: use MIME::WordDecoder;<br>   17: <br>   18: use vars qw($opt_s $opt_u $opt_p $opt_m $wd $e $map);<br>   19: <br>   20: getopts('s:u:p:m:');<br>   21: <br>   22: usage_die("-s server is required\n") unless $opt_s;<br>   23: usage_die("-u username is required\n") unless $opt_u;<br>   24: usage_die("-p password is required\n") unless $opt_p;<br>   25: usage_die("-m message is required\n") unless $opt_m;<br>   26: <br>   27: $opt_s = NetAddr::IP-&gt;new($opt_s)<br>   28:     or die "Cannot make sense of given server\n";<br></pre>

<p>
 
</p>

<p>
Note the lines 27 and 28, where I use <a href="http://search.cpan.org/search?query=NetAddr%3A%3AIP&amp;mode=module">NetAddr::IP<img alt="no alt defined" src="http://mipagina.cantv.net/lem/images/icon_external_link.gif" height="13" width="14"></a> to convert whatever the user gave us through the
<tt>-s</tt> option into an IP address. This is a very common use of
this module, as its <tt>new()</tt> method will convert many common IP
notations into an object I can later extract an IP address from. It
will even perform a name resolution for us if required. So far,
everything should look familiar, as a lot of scripts start like this
one.
</p>

<p>
It is worth noting that the error handling in lines 22-25 is not a
brilliant example of good coding or documentation. It is much better
practice to write your scripts' documentation in POD, and use a module
such as <a href="http://search.cpan.org/search?query=Pod%3A%3AUsage&amp;mode=module">Pod::Usage<img alt="no alt defined" src="http://mipagina.cantv.net/lem/images/icon_external_link.gif" height="13" width="14"></a> to provide useful error messages to the user. At
the very least, try to provide an informative usage message. You can
see the <tt>usage_die()</tt> function if you <a href="http://mipagina.cantv.net/lem/perl/mfetch">download the complete script</a>.</p>


<h2>Fetching a message via POP3</h2>

<p>

</p>

<p>
 On to deeper waters. The first step in parsing a message, is getting
at the message itself. For this, I'll use <a href="http://search.cpan.org/search?query=Net%3A%3APOP3&amp;mode=module">Net::POP3<img alt="no alt defined" src="http://mipagina.cantv.net/lem/images/icon_external_link.gif" height="13" width="14"></a>, which implements the POP3 protocol described in
<a href="http://www.rfc-editor.org/rfc/rfc1939.txt">RFC-1939<img alt="no alt defined" src="http://mipagina.cantv.net/lem/images/icon_external_link.gif" height="13" width="14"></a>. This is all done in the code below.</p>
<pre class="listing">   30: my $pops = Net::POP3-&gt;new($opt_s-&gt;addr)<br>   31:     or die "Failed to connect to POP3 server: $!\n";<br>   32: <br>   33: $pops-&gt;login($opt_u, $opt_p)<br>   34:     or die "Authentication failed\n";<br>   35: <br>   36: my $fh = new_tmpfile IO::File<br>   37:     or die "Cannot create temporary file: $!\n";<br>   38: <br>   39: $pops-&gt;get($opt_m, $fh)<br>   40:     or die "No such message $opt_m\n";<br>   41: <br>   42: $pops-&gt;quit();<br>   43: $pops = undef;<br>   44: <br>   45: $fh-&gt;seek(0, SEEK_SET);<br></pre>

<p>
 
</p>

<p>
At line 30, a connection to the POP server is attempted. This is a
TCP connection, in this case to port 110. If this connection succeeds,
the <tt>USER</tt> and <tt>PASS</tt> commands are issued at line 33,
which are the simplest form of authentication supported by the POP
protocol. Your username and password are being sent here through the
network without the protection of cryptography, so a bit of caution is
in order.
</p>

<p>
<a href="http://search.cpan.org/search?query=Net%3A%3APOP3&amp;mode=module">Net::POP3<img alt="no alt defined" src="http://mipagina.cantv.net/lem/images/icon_external_link.gif" height="13" width="14"></a> supports many operations defined in the POP
protocol that allow for more complex actions, such as fetching the
list of messages, unseen messages, etc. It can also fetch messages for
us in a variety of ways. Since I want this script to be as lightweight
as possible (i.e., to burn as little memory as possible), I want to
fetch the message to a temporary on-disk file. The temporary file is
nicely provided by the <tt>new_tmpfile</tt> method of <a href="http://search.cpan.org/search?query=IO%3A%3AFile&amp;mode=module">IO::File<img alt="no alt defined" src="http://mipagina.cantv.net/lem/images/icon_external_link.gif" height="13" width="14"></a> in line 36, which returns a file handle to a
deleted file. I can work on this file, which will magically disappear
when the script is finished.
</p>

<p>
Later, I instruct the <tt>Net::POP3</tt> object to fetch the required
message from the mail server and write it to the supplied filehandle
using the <tt>get</tt> method, on line 39. After this, the connection
is terminated gracefully by invoking <tt>quit</tt> and destroying the
object. Destroying the object insures that the TCP connection with the
server is terminated, freeing the resources being held in the POP
server as soon as possible. This is a good programming practice for
network clients.
</p>

<p>
The interaction required by <tt>mfetch</tt> with the POP server is
really simple, so I'm not making justice to <tt>Net::POP3</tt>. It
provides a very complete implementation of the protocol, allowing for
much more sophisticated applications.
</p>

<p>
Note that in line 45, I <em>rewind</em> the file so that the fetched
message can be read back by the code that follows.
</p>

<p>
For this particular example, we could also have used
<tt>Net::POP3Client</tt>, which provides a somewhat similar
interface. The code would have looked more or less like the following
fragment.</p>
<pre class="listing">    1: my $pops = new Net::POP3Client(USER =&gt; $opt_u,<br>    2:                                PASSWORD =&gt; $opt_p,<br>    3:                                HOST =&gt; $opt_s-&gt;addr)<br>    4:     or die "Error connecting or logging in: $!\n";<br>    5: <br>    6: my $fh = new_tmpfile IO::File<br>    7:     or die "Cannot create temporary file: $!\n";<br>    8: <br>    9: $pops-&gt;HeadAndBodyToFile($fh, $opt_m)<br>   10:     or die "Cannot fetch message: $!\n";<br>   11: <br>   12: $pops-&gt;Close();<br></pre>

<h2>Parsing the MIME structure</h2>

<p>

</p>

<p>
Just as email travels inside a sort of envelope (the headers), complex
messages that include attachments and generally, HTML messages, travel
within a collection of MIME <em>entities</em>. You can think of these
entities as containers that can transfer any kind of binary
information through the Email infrastructure, which in general does
not know how to deal with 8-bit data. The code reproduced below, takes
care of parsing this MIME structure.</p>
<pre class="listing">   47: my $mp = new MIME::Parser;<br>   48: $mp-&gt;ignore_errors(1);<br>   49: $mp-&gt;extract_uuencode(1);<br>   50: <br>   51: eval { $e = $mp-&gt;parse($fh); };<br>   52: my $error = ($@ || $mp-&gt;last_error);<br>   53: <br>   54: if ($error)<br>   55: {<br>   56:     $mp-&gt;filer-&gt;purge;                # Get rid of the temp files<br>   57:     die "Error parsing the message: $error\n";<br>   58: }<br></pre>

<p>
 
</p>

<p>
Perl has a wonderful class that provides the ability to understand
this MIME encapsulation, returning a nice hierarchy of objects that
represent the message. You access this facilities through the <a href="http://search.cpan.org/search?query=MIME%3A%3AParser&amp;mode=module">MIME::Parser<img alt="no alt defined" src="http://mipagina.cantv.net/lem/images/icon_external_link.gif" height="13" width="14"></a> class, part of the <a href="http://search.cpan.org/search?query=MIME-Tools&amp;mode=dist">MIME-Tools<img alt="no alt defined" src="http://mipagina.cantv.net/lem/images/icon_external_link.gif" height="13" width="14"></a> bundle. <tt>MIME::Parser</tt> returns a hierarchy
of <tt>MIME::Entity</tt> objects representing your message. The parser
is so smart, that if you pass it a non-MIME email, it will be returned
to you as a <tt>text/plain</tt> entity.
</p>

<p>
<tt>MIME::Parser</tt> can be tweaked in many ways, as its
documentation will show you. One of the points where this toggling
might be important, is the decoding process. Remember that I need to
be as light in memory usage as possible. The default behavior of
<tt>MIME::Parser</tt> involves the use of temporary files for decoding
of the message. These temporary files can be spared and core memory
used instead by invoking <tt>output_to_core()</tt>. Before doing this,
note all the caveats cited in the module's documentation. The most
important one is that if a 100 MB file ends up in your inbox, this
whole thing needs to be slurped into RAM.
</p>

<p>
In line 47 I create the parser object. The call to
<tt>ignore_errors()</tt> in line 48 is an attempt to made this parser
as tolerant as possible. <tt>extract_uuencode()</tt> in line 49, takes
care of pieces of the email that are uu-encoded automatically,
translating them back into a more readable form.  The actual request
to parse the message, available through reading the <tt>$fh</tt>
filehandle, is in line 51. Note that it is enclosed in an
<tt>eval</tt> block. I have to do this as the parser might throw an
exception if certain errors are encountered. The <tt>eval</tt> allows
me to catch this exception and react in a way that is sensible to this
application. In this case, I want to be sure that any temporary file
created by the parsing process is cleared by a call to
<tt>purge()</tt>, as seen in lines 56 and 57.</p>


<h2>Setting up the HTML parser</h2>

<p>

</p>

<p>
Parsing HTML can be a tricky and tedious task. Thankfully, Perl has a
number of nice ways to help you do this job. A number of excellent
books such as <em>The Perl Cookbook</em> (<a href="http://www.oreilly.com/catalog/cookbook/">from O'Reilly &amp;
Associates<img alt="no alt defined" src="http://mipagina.cantv.net/lem/images/icon_external_link.gif" height="13" width="14"></a>) has a couple of recipes that came very close to what I
needed, especially recipe 20.5, <em>"Converting HTML to ASCII"</em>,
which I reproduce below.</p>
<pre class="listing">    1: use HTML::TreeBuilder;<br>    2: use HTML::FormatText;<br>    3: <br>    4: $html = HTML::TreeBuilder-&gt;new();<br>    5: $html-&gt;parse($document);<br>    6: <br>    7: $formatter = HTML::FormatText-&gt;new(leftmargin =&gt; 0, rightmargin =&gt; 50);<br>    8: <br>    9: $ascii = $formatter-&gt;format($html);<br></pre>

<p>

</p>

<p>
I did not want to use this recipe because of two reasons: I needed
fine-grained control in the HTML to ASCII conversion and I wanted to
have as little impact as possible in resources. I did a small <a href="http://mipagina.cantv.net/lem/perl/mbench">benchmark</a> that shows the kind of performance
difference among the two options while parsing a copy of one of my web
articles. The result below shows that the custom parser explained
later runs faster than the Cookbook's recipe. This does not mean that
the recipe or the modules it uses are bad. This result simply means
that the recipe is actually doing a lot of additional work, which just
happens to not be all that useful for this particular task.</p>
<pre class="console">bash-2.05a$ ./mbench<br>Benchmark: timing 100 iterations of Cookbook's, Custom...<br>Cookbook's: 73 wallclock secs (52.82 usr +  0.00 sys = 52.82 CPU) @  1.89/s (n=100)<br>    Custom:  1 wallclock secs ( 1.17 usr +  0.00 sys =  1.17 CPU) @ 85.47/s (n=100)<br>             Rate Cookbook's     Custom<br>Cookbook's 1.89/s         --       -98%<br>Custom     85.5/s      4415%         --</pre>

<p>

</p>

<p>
<a href="http://search.cpan.org/search?query=HTML%3A%3AFormatText&amp;mode=module">HTML::FormatText<img alt="no alt defined" src="http://mipagina.cantv.net/lem/images/icon_external_link.gif" height="13" width="14"></a> does an awesome job of converting the HTML
to plain text. Unfortunately I have a set of guidelines that I need to
follow in the conversion and that are not compatible with the output
of this module. Additionally, <a href="http://search.cpan.org/search?query=HTML%3A%3ATreeBuilder&amp;mode=module">HTML::TreeBuilder<img alt="no alt defined" src="http://mipagina.cantv.net/lem/images/icon_external_link.gif" height="13" width="14"></a> does an excellent job of parsing an HTML
document, but produces an intermediate structure - the parse tree -
that in my case, wastes resources.
</p>

<p>
However, Perl has an excellent HTML parser in the <a href="http://search.cpan.org/search?query=HTML%3A%3AParser&amp;mode=module">HTML::Parser<img alt="no alt defined" src="http://mipagina.cantv.net/lem/images/icon_external_link.gif" height="13" width="14"></a> module. In this case, I chose to use this class
to implement an event-driven parser, where tokens (syntactic elements)
in the source document cause the parser to call functions I
provide. This allowed me complete control on the translation while
sparing the intermediate data structure.
</p>

<p>
Converting HTML to text is a lossy transformation. This means that
what goes out of this transformation is not exactly equivalent to what
went in in the first place. Pictures, text layout, style and a few
other information elements are lost. My needs required that I noted
the existence of images as well as a reasonably accurate rendition of
the page's text, but nothing else. Remember that the target device can
only display 7-bit text, and this is within a very small and limited
display. This piece of code sets up the parser to do what I need.</p>
<pre class="listing">   62: my $parser = HTML::Parser-&gt;new<br>   63: (<br>   64:  api_version =&gt; 3,<br>   65:  default_h =&gt; [ "" ],<br>   66:  start_h   =&gt; [ sub { print "[IMG ", <br>   67:                       d($_[1]-&gt;{alt}) || $_[1]-&gt;{src},"]\n" <br>   68:                           if $_[0] eq 'img';<br>   69:                      }, "tagname, attr" ],<br>   70:  text_h    =&gt; [ sub { print d(shift); }, "dtext" ],<br>   71: ) or die "Cannot create HTML parser\n";<br>   72: <br>   73: $parser-&gt;ignore_elements(qw(script style));<br>   74: $parser-&gt;strict_comment(1);<br></pre>

<p>
 
</p>

<p>
Starting on line 71, I set up the <tt>HTML::Parser</tt>
object that will help me do this. First, I tell it I want to use the
latest (as of this writing) interface style, which provides more
flexibility than earlier interfaces. On line 65, I tell the object
that by default, no parse events should do anything. There are other
ways to say this, but the one shown is the most efficient.
</p>

<p>
Lines 66 through 69 define a handler for the <em>start</em>
events. This handler will be called each time an opening tag such as
<tt>&lt;a&gt;</tt> or <tt>&lt;img&gt;</tt> is recognized in the source
being parsed. Handlers are specified as a reference to an array whose
first element tells the parser what to do and its second element,
tells the parser what information to pass to the code. In this
example, I supply a function that for any <tt>img</tt> tag, will
output a hopefully descriptive text composed with either the
<tt>alt</tt> or the <tt>src</tt> attributes. I request this handler to
be called with the name of the tag as the first argument and the list
of attributes as further arguments, through the string <tt>"tagname,
attr"</tt> found in line 69. The <tt>d()</tt> function will be
explained a bit later, but it has to do with decoding its
argument.
</p>

<p>
The <em>text</em> event will be triggered by anything inside tags
in the input text. I've set up a simpler handler for this event that
merely prints out whatever is recognized. I also request that HTML
entities such as <tt>&amp;euro;</tt> or <tt>&amp;ntilde;</tt> be
decoded for me through the string <tt>"dtext"</tt> on line 70. HTML
entities are used to represent special characters outside the
traditional ASCII range. In the interest of document accuracy, you
should always use entities instead of directly placing 8-bit
characters in the text.
</p>

<p>
Some syntactic elements are used to enclose information that is not
important for this application, such as
<tt>&lt;style&gt;...&lt;/style&gt;</tt> and
<tt>&lt;script&gt;...&lt;/script&gt;</tt>. I ask the parser to ignore
those elements with the call to <tt>ignore_elements()</tt> at line
73. I also request the parser to follow strict comment syntax through
the call to <tt>strict_comment()</tt> on line 74.</p>


<h2>Setting up the Unicode mappings</h2>

<p>

</p>

<p>
MIME defines various ways to encode binary data depending on the
frequency of octets greater than 127. With relatively few high-bit
octets, <em>Quoted-Printable</em> encoding is used. When many high-bit
octets are present, <em>Base-64</em> encoding is used instead. The
reason is that Quoted-Printable is slightly more readable but very
inefficient in space while Base-64 is completely unreadable by
standard humans and adds much less overhead in the size of encoded
files. Often, message headers such as the sender's name are encoded
using Quoted-Printable when they contain characters such as a
'ñ'. These headers look like <tt>From:
=?ISO-8859-1?Q?Luis_Mu=F1oz?= &lt;some@body.org&gt;</tt> and should be
converted to <tt>From: Luis Muñoz &lt;some@body.org&gt;</tt>. In plain
english, Quoted-Printable encoding is being used to make the extended
ISO-8859-1 characters acceptable for any 7-bit transport such as
email. Many contemporary mail transport agents can properly handle
message bodies that contain high-bit octets but will choke on headers
with binary data, in case you were wondering why all this fuzz.
</p>

<p>
Lines 92 through 102 define <tt>setup_decoder()</tt>, which can use
the headers contained in a <a href="http://search.cpan.org/search?query=MIME%3A%3AHead&amp;mode=module">MIME::Head<img alt="no alt defined" src="http://mipagina.cantv.net/lem/images/icon_external_link.gif" height="13" width="14"></a> object to setup a suitable decoder based on the
<a href="http://search.cpan.org/search?query=MIME%3A%3AWordDecoder&amp;mode=module">MIME::WordDecoder<img alt="no alt defined" src="http://mipagina.cantv.net/lem/images/icon_external_link.gif" height="13" width="14"></a> class. This will translate instances of
Quoted-Printable text, to its high-bit equivalent. Note that I
selected ISO-8859-1 as the default in case no proper character set can
be identified. This was a sensible choice for me, as ISO-8859-1
encloses spanish characters, which happen to be my native language.</p>
<pre class="listing">   92: sub setup_decoder<br>   93: {<br>   94:     my $head = shift;<br>   95:     if ($head-&gt;get('Content-Type')<br>   96:         and $head-&gt;get('Content-Type') =~ m!charset="([^\"]+)"!)<br>   97:     {<br>   98:         $wd = supported MIME::WordDecoder uc $1;<br>   99:     }<br>  100:     $wd = supported MIME::WordDecoder "ISO-8859-1" unless $wd;<br>  101: }<br></pre>
<!--" This double quote is here because of the HTML mode in Emacs 
--> 

<p>

</p>

<p>
But this clever decoding is not enough. Getting at the original
high-bit characters is not enough. I must recode these high characters
into something usable by the 7-bit display device. So in line 76 I set
up a mapping based on <a href="http://search.cpan.org/search?query=Unicode%3A%3AMap8&amp;mode=module">Unicode::Map8<img alt="no alt defined" src="http://mipagina.cantv.net/lem/images/icon_external_link.gif" height="13" width="14"></a>. This module can convert 8-bit characters such
as ISO-8859-1 or ASCII into wider characters (<a href="http://www.unicode.org/">Unicode<img alt="no alt defined" src="http://mipagina.cantv.net/lem/images/icon_external_link.gif" height="13" width="14"></a>) and then back into
our chosen representation, ASCII, which only defines 7-bit
characters. This means that any character that cannot be properly
represented, will be lost, which for our application is acceptable.</p>
<pre class="listing">   76: $map = Unicode::Map8-&gt;new('ASCII')<br>   77:     or die "Cannot create character map\n";<br></pre>

<p>

</p>

<p>
The decoding and character mapping is then brought together at line
90, where I define the <tt>d()</tt> function, that simply invokes the
adequate MIME decoding method, transforms the resulting string into
Unicode via the <tt>to16()</tt> method and then, transforms it back
into <tt>ASCII</tt> using <tt>to8()</tt> to insure printable results
in our device. Since I am allergic to warnings related to
<tt>undef</tt> values, I make sure that <tt>decode()</tt> always get a
defined string to work with.</p>
<pre class="listing">   90: sub d { $map-&gt;to8($map-&gt;to16($wd-&gt;decode(shift||''))); }<br></pre>

<p>

</p>

<p>
 As you might notice if you try this code, the conversion is again
lossy because there are characters that does not exist in
<tt>ASCII</tt>. You can experiment with the <tt>addpair()</tt> method
to <tt>Unicode::Map8</tt> in order to add custom character
transformations (i.e., '€' might be 'E'). Another way to achieve
this, is through deriving a class from <tt>Unicode::Map8</tt> and
implementing the <tt>unmapped_to8</tt> method to supply your own
interpretation of the missing characters. Take a look at the module's
documentation for more information.</p>


<h2>Starting the decode process</h2>

<p>

</p>

<p>
With all the pieces in place, all that's left is to traverse the
hierarchy of entities that <tt>MIME::Parser</tt> provides after
parsing a message. I implemented a very simple recursive function
<tt>decode_entities</tt> starting at line 103. This is a recursive
function because recursion comes naturally as a way to handle trees
such as those produced by <tt>MIME::Parser</tt>. At
least to me.</p>
<pre class="listing">  103: sub decode_entities<br>  104: {<br>  105:     my $ent = shift;<br>  106: <br>  107:     if (my @parts = $ent-&gt;parts)<br>  108:     {<br>  109:         decode_entities($_) for @parts;<br>  110:     }<br>  111:     elsif (my $body = $ent-&gt;bodyhandle)<br>  112:     {<br>  113:         my $type = $ent-&gt;head-&gt;mime_type;<br>  114: <br>  115:         setup_decoder($ent-&gt;head);<br>  116: <br>  117:         if ($type eq 'text/plain')<br>  118:         { print d($body-&gt;as_string); }<br>  119:         elsif ($type eq 'text/html')<br>  120:         { $parser-&gt;parse($body-&gt;as_string); }<br>  121:         else<br>  122:         { print "[Unhandled part of type $type]"; }<br>  123:     }<br>  124: }<br></pre>

<p>

</p>

<p>
The condition at line 107 asks if this part or entity contains
other parts. If it does, it extracts them and invokes itself
recursively to process each sub-part at line 109.
</p>

<p>
If this part is a leaf, its body is processed. Line 111 gets it as a
<a href="http://search.cpan.org/search?query=MIME%3A%3ABody&amp;mode=module">MIME::Body<img alt="no alt defined" src="http://mipagina.cantv.net/lem/images/icon_external_link.gif" height="13" width="14"></a> object. On line 155 I setup a decoder for this
part's encoding and based on the type of this part, taken at line 113,
the code on lines 117 to 122 call the proper handlers.
</p>

<p>
In order to fire the decoding process, I call
<tt>decode_entities()</tt> with the result of the MIME decoding of the
message on line 86. This will invoke the HTML parser when needed and
in general, produce the output I look for in this example. After this
processing is done, I make sure to wipe temporary files created by
<tt>MIME::Parser</tt> on line 88. Note that if the message is not
actually encoded with MIME, <tt>MIME::Parser</tt> will arrange for you
to receive a single part of type <tt>text/plain</tt> that contains the
whole message text, which is perfect for our application.</p>
<pre class="listing">   86: decode_entities($e);<br>   87: <br>   88: $mp-&gt;filer-&gt;purge;<br></pre>

<h2>And that's about it</h2>

<p>

</p>

<p>
After these less than 130 lines of code, I can
easily fetch and decode a message, such as in the following
example:</p>
<pre class="console">bash-2.05a$ <strong>./mfetch -s pop.foo.bar -u myself \<br>            -p very_secure_password -m 5</strong><br>Date: Sat, 28 Dec 2002 20:14:37 -0400<br>From: root &lt;root@foo.bar&gt;<br>To: myself@foo.bar<br>Subject: This is the plain subject<br><br>This is a boring and plain message.</pre>

<p>

</p>

<p>
More complex MIME messages can also be decoded. Look at this
example where I dissect a dreaded piece of junk mail, but don't
worry. I used <tt>head</tt> to spare you pages and pages of worthless
image links:</p>
<pre class="console">bash-2.05a$ <strong>./mfetch -s pop.foo.bar -u myself \<br>            -p very_secure_password -m 2 | head -20</strong><br><br>Date: Sun, 22 Dec 2002 23:22:25 -0400<br>From: Luis Muoz &lt;lem@foo.bar&gt;<br>To: Myself &lt;myself@foo.bar&gt;<br>Subject: Fwd: Get $860 Free - Come, Play, Have Fun!<br><br><br><br>Begin forwarded message:<br><br>&gt; From: Cosmic Offers &lt;munged@migada.com.INVALID&gt;;<br>&gt; Date: Sun Dec 22, 2002  20:59:43 America/Caracas<br>&gt; To: spam@victim.net<br>&gt; Subject: Get $860 Free - Come, Play, Have Fun!<br>&gt;<br><br>&gt;<br>[IMG http://www.migada.com/email/Flc_600_550_liberty_mailer_.gif]<br>[IMG http://www.migada.com/email/Flc_600_550_liberty_mail-02.gif]<br>[IMG http://www.migada.com/email/Flc_600_550_liberty_mail-03.gif]<br>[IMG http://www.migada.com/email/Flc_600_550_liberty_mail-04.gif]</pre>

<p>

</p>

<p>
If you're curious, please <a href="http://mipagina.cantv.net/lem/perl/mfetch">download the complete
script</a> and play with it a bit. I hope this tutorial and its
related script to be as helpful for you as it has been for me</p></div>
<!-- $Id: footer,v 1.1.1.1 2005/01/01 12:02:45 lem Exp $ --><img src ="http://www.blogjava.net/pyguru/aggbug/1312.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/pyguru/" target="_blank">pyguru</a> 2005-02-19 00:33 <a href="http://www.blogjava.net/pyguru/archive/2005/02/19/1312.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CGI::Carp - CGI routines for writing to the HTTPD (or other) error log</title><link>http://www.blogjava.net/pyguru/archive/2005/02/18/1293.html</link><dc:creator>pyguru</dc:creator><author>pyguru</author><pubDate>Thu, 17 Feb 2005 21:27:00 GMT</pubDate><guid>http://www.blogjava.net/pyguru/archive/2005/02/18/1293.html</guid><wfw:comment>http://www.blogjava.net/pyguru/comments/1293.html</wfw:comment><comments>http://www.blogjava.net/pyguru/archive/2005/02/18/1293.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/pyguru/comments/commentRss/1293.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/pyguru/services/trackbacks/1293.html</trackback:ping><description><![CDATA[

<!-- INDEX BEGIN -->

<ul>
<li><a href="http://www.perl.com/doc/manual/html/lib/CGI/Carp.html#NAME">NAME</a>
	</li><li><a href="http://www.perl.com/doc/manual/html/lib/CGI/Carp.html#SYNOPSIS">SYNOPSIS</a>
	</li><li><a href="http://www.perl.com/doc/manual/html/lib/CGI/Carp.html#DESCRIPTION">DESCRIPTION</a>
	</li><li><a href="http://www.perl.com/doc/manual/html/lib/CGI/Carp.html#REDIRECTING_ERROR_MESSAGES">REDIRECTING ERROR MESSAGES</a>
	</li><li><a href="http://www.perl.com/doc/manual/html/lib/CGI/Carp.html#MAKING_PERL_ERRORS_APPEAR_IN_THE">MAKING PERL ERRORS APPEAR IN THE BROWSER WINDOW</a>
	<ul><li><a href="http://www.perl.com/doc/manual/html/lib/CGI/Carp.html#Changing_the_default_message">Changing the default message</a>
	</li></ul>

	</li><li><a href="http://www.perl.com/doc/manual/html/lib/CGI/Carp.html#CHANGE_LOG">CHANGE LOG</a>
	</li><li><a href="http://www.perl.com/doc/manual/html/lib/CGI/Carp.html#AUTHORS">AUTHORS</a>
	</li><li><a href="http://www.perl.com/doc/manual/html/lib/CGI/Carp.html#SEE_ALSO">SEE ALSO</a>
</li>
</ul>

<!-- INDEX END -->

<hr>
<p>
</p>
<h1><a name="NAME">NAME</a></h1>

<p>
<strong>CGI::Carp</strong> - 
<font size="-1">CGI</font> routines for writing to the 
<font size="-1">HTTPD</font> (or other) error log

</p>
<p>
</p>
<hr>
<h1><a name="SYNOPSIS">SYNOPSIS</a></h1>

<p>
</p>
<pre>    use CGI::Carp;<br></pre>

<p>
</p>
<pre>    croak "We're outta here!";<br>    confess "It was my fault: $!";<br>    carp "It was your fault!";   <br>    warn "I'm confused";<br>    die  "I'm dying.\n";<br></pre>

<p>
</p>
<hr>
<h1><a name="DESCRIPTION">DESCRIPTION</a></h1>

<p>

<font size="-1">CGI</font> scripts have a nasty habit of leaving warning
messages in the error logs that are neither time stamped nor fully
identified. Tracking down the script that caused the error is a pain. This
fixes that. Replace the usual

</p>
<p>
</p>
<pre>    use Carp;<br></pre>

<p>
with

</p>
<p>
</p>
<pre>    use CGI::Carp<br></pre>

<p>
And the standard 
<code>warn(),</code> die (), 
<code>croak(),</code> 
<code>confess()</code> and 
<code>carp()</code> calls will automagically be replaced with functions that write out nicely time-stamped messages to the 
<font size="-1">HTTP</font> server error log.

</p>
<p>
For example:

</p>
<p>
</p>
<pre>   [Fri Nov 17 21:40:43 1995] test.pl: I'm confused at test.pl line 3.<br>   [Fri Nov 17 21:40:43 1995] test.pl: Got an error message: Permission denied.<br>   [Fri Nov 17 21:40:43 1995] test.pl: I'm dying.<br></pre>

<p>
</p>
<hr>
<h1><a name="REDIRECTING_ERROR_MESSAGES">REDIRECTING ERROR MESSAGES</a></h1>

<p>
By default, error messages are sent to 
<font size="-1">STDERR.</font> Most 
<font size="-1">HTTPD</font> servers direct 
<font size="-1">STDERR</font> to the server's error log. Some
applications may wish to keep private error logs, distinct from the
server's error log, or they may wish to direct error messages to <font size="-1">STDOUT</font> so that the browser will receive them.

</p>
<p>
The <code>carpout()</code> function is provided for this purpose. Since 
<code>carpout()</code> is not exported by
default, you must import it explicitly by saying

</p>
<p>
</p>
<pre>   use CGI::Carp qw(carpout);<br></pre>

<p>
The 
<code>carpout()</code> function requires
one argument, which should be a reference to an open filehandle for writing
errors. It should be called in a <code>BEGIN</code> block at the top of the 
<font size="-1">CGI</font> application so that compiler errors will be
caught. Example:

</p>
<p>
</p>
<pre>   BEGIN {<br>     use CGI::Carp qw(carpout);<br>     open(LOG, "&gt;&gt;/usr/local/cgi-logs/mycgi-log") or<br>       die("Unable to open mycgi-log: $!\n");<br>     carpout(LOG);<br>   }<br></pre>

<p>

<code>carpout()</code> does not handle
file locking on the log for you at this point.

</p>
<p>
The real 
<font size="-1">STDERR</font> is not closed -- it is moved to 
<font size="-1">SAVEERR.</font> Some servers, when dealing with 
<font size="-1">CGI</font> scripts, close their connection to the browser when the script closes 
<font size="-1">STDOUT</font> and 
<font size="-1">STDERR.</font> 
<font size="-1">SAVEERR</font> is used to prevent this from happening prematurely.

</p>
<p>
You can pass filehandles to 
<code>carpout()</code> in a variety of ways. The ``correct'' way according to Tom Christiansen is to pass a reference to a filehandle 
<font size="-1">GLOB:</font>

</p>
<p>
</p>
<pre>    carpout(\*LOG);<br></pre>

<p>
This looks weird to mere mortals however, so the following syntaxes are
accepted as well:

</p>
<p>
</p>
<pre>    carpout(LOG);<br>    carpout(main::LOG);<br>    carpout(main'LOG);<br>    carpout(\LOG);<br>    carpout(\'main::LOG');<br></pre>

<p>
</p>
<pre>    ... and so on<br></pre>

<p>
FileHandle and other objects work as well.

</p>
<p>
Use of 
<code>carpout()</code> is not great for performance, so it is recommended for debugging purposes or for moderate-use applications. 
<font size="-1">A</font> future version of this module may delay redirecting 
<font size="-1">STDERR</font> until one of the CGI::Carp methods is called to prevent the performance hit.

</p>
<p>
</p>
<hr>
<h1><a name="MAKING_PERL_ERRORS_APPEAR_IN_THE">MAKING PERL ERRORS APPEAR IN THE BROWSER WINDOW</a></h1>

<p>
If you want to send fatal (die, confess) errors to the browser, ask to
import the special ``fatalsToBrowser'' subroutine:

</p>
<p>
</p>
<pre>    use CGI::Carp qw(fatalsToBrowser);<br>    die "Bad error here";<br></pre>

<p>
Fatal errors will now be echoed to the browser as well as to the log. CGI::Carp arranges to send a minimal 
<font size="-1">HTTP</font> header to the browser so that even errors
that occur in the early compile phase will be seen. Nonfatal errors
will still be directed to the log file only (unless redirected with
carpout).
</p>
<p>
</p>
<hr>
<!-- <A NAME="Changing_the_default_message">Changing the default message</A> -->
<p>By default, the software error message is followed by a note to
contact the Webmaster by e-mail with the time and date of the error. If
this message is not to your liking, you can change it using the <code>set_message()</code> routine. This is not imported by default; you should import it on the 
<code>use()</code> line:

</p>
<p>
</p>
<pre>    use CGI::Carp qw(fatalsToBrowser set_message);<br>    set_message("It's not a bug, it's a feature!");<br></pre>

<p>
You may also pass in a code reference in order to create a custom error
message. At run time, your code will be called with the text of the error
message that caused the script to die. Example:

</p>
<p>
</p>
<pre>    use CGI::Carp qw(fatalsToBrowser set_message);<br>    BEGIN {<br>       sub handle_errors {<br>          my $msg = shift;<br>          print "&lt;h1&gt;Oh gosh&lt;/h1&gt;";<br>          print "Got an error: $msg";<br>      }<br>      set_message(\&amp;handle_errors);<br>    }<br></pre>

<p>
In order to correctly intercept compile-time errors, you should call 
<code>set_message()</code> from within a 
<font size="-1">BEGIN{}</font> block.

</p>
<p>
</p>
<hr>
<h1><a name="CHANGE_LOG">CHANGE LOG</a></h1>

<p>
1.05 
<code>carpout()</code> added and minor
corrections by Marc Hedlund &lt;<a href="mailto:hedlund@best.com">hedlund@best.com</a>&gt; on 11/26/95.

</p>
<p>
1.06 
<code>fatalsToBrowser()</code> no longer aborts for fatal errors within 
<code>eval()</code> statements.

</p>
<p>
1.08 
<code>set_message()</code> added and 
<code>carpout()</code> expanded to allow for FileHandle objects.

</p>
<p>
1.09 
<code>set_message()</code> now allows users to pass a code 
<font size="-1">REFERENCE</font> for really custom error messages. croak and carp are now exported by default. Thanks to Gunther Birznieks for the patches.

</p>
<p>
1.10 Patch from Chris Dean (<a href="mailto:ctdean@cogit.com">ctdean@cogit.com</a>) to allow module to run
correctly under mod_perl.

</p>
<p>
</p>
<hr>
<h1><a name="AUTHORS">AUTHORS</a></h1>

<p>
Lincoln 
<font size="-1">D.</font> Stein &lt;<a href="mailto:lstein@genome.wi.mit.edu">lstein@genome.wi.mit.edu</a>&gt;
Feel free to redistribute this under the Perl Artistic License.

</p>
<p>
</p>
<hr>
<h1><a name="SEE_ALSO">SEE ALSO</a></h1>

<p>
Carp, CGI::Base, CGI::BasePlus, CGI::Request, CGI::MiniSvr, CGI::Form,
CGI::Response

</p>
<hr>
<h1>DISCLAIMER</h1>
 
We are painfully aware that these documents may contain incorrect links and
misformatted HTML.  Such bugs lie in the automatic translation process
that automatically created the hundreds and hundreds of separate documents that you find here.  Please <b>do
not report</b> link or formatting bugs, because we cannot fix
per-document problems.  The only bug reports that will help us are those
that supply working patches to the <i>installhtml</i> or <i>pod2html</i>
programs, or to the <tt>Pod::HTML</tt> module itself, for which I and the entire
Perl community will shower you with thanks and praises.  
<p>
If rather than formatting bugs, you encounter substantive content errors in these documents, such as mistakes in
the explanations or code, please use the <i>perlbug</i> utility included
with the Perl distribution.
</p>
<p>
</p>
<dl>
<dd>--Tom Christiansen, Perl Documentation Compiler and Editor</dd>
</dl>
 
<p>
</p>
<hr>
Return to the <a href="http://www.perl.com/doc/manual/html/index.html">Perl Documentation Index</a>.
<br>

Return to the <a href="http://www.perl.com/">Perl Home Page</a>.
<img src ="http://www.blogjava.net/pyguru/aggbug/1293.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/pyguru/" target="_blank">pyguru</a> 2005-02-18 05:27 <a href="http://www.blogjava.net/pyguru/archive/2005/02/18/1293.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Perl 5 by Example</title><link>http://www.blogjava.net/pyguru/archive/2005/02/18/1292.html</link><dc:creator>pyguru</dc:creator><author>pyguru</author><pubDate>Thu, 17 Feb 2005 19:56:00 GMT</pubDate><guid>http://www.blogjava.net/pyguru/archive/2005/02/18/1292.html</guid><wfw:comment>http://www.blogjava.net/pyguru/comments/1292.html</wfw:comment><comments>http://www.blogjava.net/pyguru/archive/2005/02/18/1292.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/pyguru/comments/commentRss/1292.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/pyguru/services/trackbacks/1292.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Perl 5by Exampleby David MedinetsC&nbsp;&nbsp;O&nbsp;&nbsp;N&nbsp;&nbsp;T&nbsp;&nbsp;E&nbsp;&nbsp;N&nbsp;&nbsp;T&nbsp;&nbsp;SChapter 1...&nbsp;&nbsp;<a href='http://www.blogjava.net/pyguru/archive/2005/02/18/1292.html'>阅读全文</a><img src ="http://www.blogjava.net/pyguru/aggbug/1292.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/pyguru/" target="_blank">pyguru</a> 2005-02-18 03:56 <a href="http://www.blogjava.net/pyguru/archive/2005/02/18/1292.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Perl: The Carp Module</title><link>http://www.blogjava.net/pyguru/archive/2005/02/18/1291.html</link><dc:creator>pyguru</dc:creator><author>pyguru</author><pubDate>Thu, 17 Feb 2005 19:49:00 GMT</pubDate><guid>http://www.blogjava.net/pyguru/archive/2005/02/18/1291.html</guid><wfw:comment>http://www.blogjava.net/pyguru/comments/1291.html</wfw:comment><comments>http://www.blogjava.net/pyguru/archive/2005/02/18/1291.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/pyguru/comments/commentRss/1291.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/pyguru/services/trackbacks/1291.html</trackback:ping><description><![CDATA[<h3><a name="ExampleTheTTFONTSIZEFACECourierCarpFONTTTFONTSIZEModuleFONT">

Example: The <tt>Carp</tt>

Module</a></h3>


<p>

This useful little module lets you do a better job of analyzing

runtime errors-like when your script can't open a file or when

an unexpected input value is found. It defines the <tt>carp()</tt>,

<tt>croak()</tt>, and <tt>confess()</tt>

fuNCtions. These are similar to <tt>warn()</tt>

and <tt>die()</tt>. However, instead

of reported in the exact script line where the error occurred,

the fuNCtions in this module will display the line number that

called the fuNCtion that generated the error. Confused? So was

I, until I did some experimenting. The results of that experimenting

can be found in Listing 15.6.

</p>
<p>



<img src="http://octopus.cdut.edu.cn/%7Eyf17/perl5ex/pseudo.gif" tppabs="http://202.113.16.101/%7eeb%7e/Perl%205%20By%20Example/pseudo.gif" align="right" border="1"></p>
<p>

</p>
<blockquote>

<i>Load the Carp module.<br>

Invoke the strict pragma.<br>

Start the Foo namespace.<br>

Define the </i><tt><i>foo()</i></tt><i>

fuNCtion.<br>

Call the </i><tt><i>carp()</i></tt><i>

fuNCtion.<br>

Call the </i><tt><i>croak()</i></tt><i>

fuNCtion.<br>

Switch to the main namespace.<br>

Call the </i><tt><i>foo()</i></tt><i>

fuNCtion.</i>

</blockquote>


<hr>

<blockquote>

<b>Listing 15.6&nbsp;&nbsp;15LST06.PL-Using the </b><tt><b><font face="Courier">carp()</font></b></tt><b>

and </b><tt><b><font face="Courier">croak()</font></b></tt><b>

from the </b><tt><b><font face="Courier">Carp Module<br>

</font></b></tt>

</blockquote>


<blockquote>

<pre>use Carp;<br>use strict;<br><br>package Foo;<br>    sub foo {<br>        main::carp("carp called at line " . __LINE__ .<br>            ",\n    but foo() was called");<br><br>        main::croak("croak called at line " . __LINE__ .<br>            ",\n    but foo() was called");<br>}<br><br>package main;<br>    foo::foo();<br><br></pre>

</blockquote>


<hr>

<p>

This program displays:

</p>
<blockquote>

<pre>carp called at line 9, <br><br>    but foo() was called at e.pl line 18<br><br>croak called at line 10, <br><br>    but foo() was called at e.pl line 18<br><br></pre>

</blockquote>


<p>

This example uses a compiler symbol, __LINE__, to iNCorporate

the current line number in the string passed to both <tt>carp()</tt>

and <tt>croak()</tt>. This technique

enables you to see both the line number where <tt>carp()</tt>

and <tt>croak()</tt> were called <i>and</i>

the line number where <tt>foo()</tt>

was called.

</p>
<p>

The <tt>Carp</tt> module also defines

a <tt>confess()</tt> fuNCtion which

is similar to <tt>croak()</tt> except

that a fuNCtion call history will also be displayed. Listing 15.7

shows how this fuNCtion can be used. The fuNCtion declarations

were placed after the <tt>foo()</tt>

fuNCtion call so that the program flow reads from top to bottom

with no jumping around.

</p>
<p>

<img src="http://octopus.cdut.edu.cn/%7Eyf17/perl5ex/pseudo.gif" tppabs="http://202.113.16.101/%7eeb%7e/Perl%205%20By%20Example/pseudo.gif" align="right" border="1"></p>
<p>

</p>
<blockquote>

<i>Load the Carp module.<br>

Invoke the strict pragma.<br>

Call </i><tt><i>foo()</i></tt><i>.

<br>

Define </i><tt><i>foo()</i></tt><i>.

<br>

Call </i><tt><i>bar()</i></tt><i>.

<br>

Define </i><tt><i>bar()</i></tt><i>.

<br>

Call </i><tt><i>baz()</i></tt><i>.

<br>

Define </i><tt><i>baz()</i></tt><i>.

<br>

Call </i><tt><i>Confess()</i></tt><i>.</i>

</blockquote>


<hr>

<blockquote>

<b>Listing 15.7&nbsp;&nbsp;15LST07.PL-Using </b><tt><b><font face="Courier">confess()</font></b></tt><b>

from the </b><tt><b><font face="Courier">Carp</font></b></tt><b>

Module<br>

</b>

</blockquote>


<blockquote>

<pre>use Carp;<br>use strict;<br><br>foo();<br><br>sub foo {<br>    bar();<br>}<br><br>sub bar {<br>    baz();<br>}<br><br>sub baz {<br>    confess("I give up!");<br>}<br></pre>

</blockquote>


<hr>

<p>

This program displays:

</p>
<blockquote>

<pre>I give up! at e.pl line 16<br><br>        main::baz called at e.pl line 12<br><br>        main::bar called at e.pl line 8<br><br>        main::foo called at e.pl line 5<br><br></pre>

</blockquote>


<p>

This daisy-chain of fuNCtion calls was done to show you how the

fuNCtion call history looks when displayed. The fuNCtion call

history is also called a <i>stack trace</i>. As each fuNCtion

is called, the address from which it is called gets placed on

a stack. When the <tt>confess()</tt>

fuNCtion is called, the stack is unwound or read. This lets Perl

print the fuNCtion call history.

</p>
<img src ="http://www.blogjava.net/pyguru/aggbug/1291.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/pyguru/" target="_blank">pyguru</a> 2005-02-18 03:49 <a href="http://www.blogjava.net/pyguru/archive/2005/02/18/1291.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>