File Coverage

blib/lib/Pod/PlainText.pm
Criterion Covered Total %
statement 178 243 73.2
branch 81 134 60.4
condition 14 24 58.3
subroutine 27 31 87.1
pod 1 26 3.8
total 301 458 65.7


line stmt bran cond sub pod time code
1             # Pod::PlainText -- Convert POD data to formatted ASCII text.
2             # $Id: Text.pm,v 2.1 1999/09/20 11:53:33 eagle Exp $
3             #
4             # Copyright 1999-2000 by Russ Allbery
5             #
6             # This program is free software; you can redistribute it and/or modify it
7             # under the same terms as Perl itself.
8             #
9             # This module is intended to be a replacement for Pod::Text, and attempts to
10             # match its output except for some specific circumstances where other
11             # decisions seemed to produce better output. It uses Pod::Parser and is
12             # designed to be very easy to subclass.
13              
14             ############################################################################
15             # Modules and declarations
16             ############################################################################
17              
18             package Pod::PlainText;
19 12     12   140132 use strict;
  12         109  
  12         377  
20              
21             require 5.005;
22              
23 12     12   56 use Carp qw(carp croak);
  12         19  
  12         510  
24 12     12   4935 use Pod::Select ();
  12         32  
  12         302  
25              
26 12     12   61 use vars qw(@ISA %ESCAPES $VERSION);
  12         19  
  12         920  
27              
28             # We inherit from Pod::Select instead of Pod::Parser so that we can be used
29             # by Pod::Usage.
30             @ISA = qw(Pod::Select);
31              
32             $VERSION = '2.07';
33              
34             BEGIN {
35 12 50   12   39743 if ($] < 5.006) {
36 0         0 require Symbol;
37 0         0 import Symbol;
38             }
39             }
40              
41             ############################################################################
42             # Table of supported E<> escapes
43             ############################################################################
44              
45             # This table is taken near verbatim from Pod::PlainText in Pod::Parser,
46             # which got it near verbatim from the original Pod::Text. It is therefore
47             # credited to Tom Christiansen, and I'm glad I didn't have to write it. :)
48             %ESCAPES = (
49             'amp' => '&', # ampersand
50             'lt' => '<', # left chevron, less-than
51             'gt' => '>', # right chevron, greater-than
52             'quot' => '"', # double quote
53              
54             "Aacute" => "\xC1", # capital A, acute accent
55             "aacute" => "\xE1", # small a, acute accent
56             "Acirc" => "\xC2", # capital A, circumflex accent
57             "acirc" => "\xE2", # small a, circumflex accent
58             "AElig" => "\xC6", # capital AE diphthong (ligature)
59             "aelig" => "\xE6", # small ae diphthong (ligature)
60             "Agrave" => "\xC0", # capital A, grave accent
61             "agrave" => "\xE0", # small a, grave accent
62             "Aring" => "\xC5", # capital A, ring
63             "aring" => "\xE5", # small a, ring
64             "Atilde" => "\xC3", # capital A, tilde
65             "atilde" => "\xE3", # small a, tilde
66             "Auml" => "\xC4", # capital A, dieresis or umlaut mark
67             "auml" => "\xE4", # small a, dieresis or umlaut mark
68             "Ccedil" => "\xC7", # capital C, cedilla
69             "ccedil" => "\xE7", # small c, cedilla
70             "Eacute" => "\xC9", # capital E, acute accent
71             "eacute" => "\xE9", # small e, acute accent
72             "Ecirc" => "\xCA", # capital E, circumflex accent
73             "ecirc" => "\xEA", # small e, circumflex accent
74             "Egrave" => "\xC8", # capital E, grave accent
75             "egrave" => "\xE8", # small e, grave accent
76             "ETH" => "\xD0", # capital Eth, Icelandic
77             "eth" => "\xF0", # small eth, Icelandic
78             "Euml" => "\xCB", # capital E, dieresis or umlaut mark
79             "euml" => "\xEB", # small e, dieresis or umlaut mark
80             "Iacute" => "\xCD", # capital I, acute accent
81             "iacute" => "\xED", # small i, acute accent
82             "Icirc" => "\xCE", # capital I, circumflex accent
83             "icirc" => "\xEE", # small i, circumflex accent
84             "Igrave" => "\xCD", # capital I, grave accent
85             "igrave" => "\xED", # small i, grave accent
86             "Iuml" => "\xCF", # capital I, dieresis or umlaut mark
87             "iuml" => "\xEF", # small i, dieresis or umlaut mark
88             "Ntilde" => "\xD1", # capital N, tilde
89             "ntilde" => "\xF1", # small n, tilde
90             "Oacute" => "\xD3", # capital O, acute accent
91             "oacute" => "\xF3", # small o, acute accent
92             "Ocirc" => "\xD4", # capital O, circumflex accent
93             "ocirc" => "\xF4", # small o, circumflex accent
94             "Ograve" => "\xD2", # capital O, grave accent
95             "ograve" => "\xF2", # small o, grave accent
96             "Oslash" => "\xD8", # capital O, slash
97             "oslash" => "\xF8", # small o, slash
98             "Otilde" => "\xD5", # capital O, tilde
99             "otilde" => "\xF5", # small o, tilde
100             "Ouml" => "\xD6", # capital O, dieresis or umlaut mark
101             "ouml" => "\xF6", # small o, dieresis or umlaut mark
102             "szlig" => "\xDF", # small sharp s, German (sz ligature)
103             "THORN" => "\xDE", # capital THORN, Icelandic
104             "thorn" => "\xFE", # small thorn, Icelandic
105             "Uacute" => "\xDA", # capital U, acute accent
106             "uacute" => "\xFA", # small u, acute accent
107             "Ucirc" => "\xDB", # capital U, circumflex accent
108             "ucirc" => "\xFB", # small u, circumflex accent
109             "Ugrave" => "\xD9", # capital U, grave accent
110             "ugrave" => "\xF9", # small u, grave accent
111             "Uuml" => "\xDC", # capital U, dieresis or umlaut mark
112             "uuml" => "\xFC", # small u, dieresis or umlaut mark
113             "Yacute" => "\xDD", # capital Y, acute accent
114             "yacute" => "\xFD", # small y, acute accent
115             "yuml" => "\xFF", # small y, dieresis or umlaut mark
116              
117             "lchevron" => "\xAB", # left chevron (double less than)
118             "rchevron" => "\xBB", # right chevron (double greater than)
119             );
120              
121              
122             ############################################################################
123             # Initialization
124             ############################################################################
125              
126             # Initialize the object. Must be sure to call our parent initializer.
127             sub initialize {
128 12     12 0 2809 my $self = shift;
129              
130 12 50       144 $$self{alt} = 0 unless defined $$self{alt};
131 12 50       46 $$self{indent} = 4 unless defined $$self{indent};
132 12 50       45 $$self{loose} = 0 unless defined $$self{loose};
133 12 50       41 $$self{sentence} = 0 unless defined $$self{sentence};
134 12 50       52 $$self{width} = 76 unless defined $$self{width};
135              
136 12         28 $$self{INDENTS} = []; # Stack of indentations.
137 12         28 $$self{MARGIN} = $$self{indent}; # Current left margin in spaces.
138              
139 12         107 return $self->SUPER::initialize;
140             }
141              
142              
143             ############################################################################
144             # Core overrides
145             ############################################################################
146              
147             # Called for each command paragraph. Gets the command, the associated
148             # paragraph, the line number, and a Pod::Paragraph object. Just dispatches
149             # the command to a method named the same as the command. =cut is handled
150             # internally by Pod::Parser.
151             sub command {
152 61     61 0 722 my $self = shift;
153 61         81 my $command = shift;
154 61 100       349 return if $command eq 'pod';
155 52 50 66     113 return if ($$self{EXCLUDE} && $command ne 'end');
156 52 100       85 if (defined $$self{ITEM}) {
157 2         9 $self->item ("\n");
158 2         5 local $_ = "\n";
159 2 50       6 $self->output($_) if($command eq 'back');
160             }
161 52         91 $command = 'cmd_' . $command;
162 52         206 return $self->$command (@_);
163             }
164              
165             # Called for a verbatim paragraph. Gets the paragraph, the line number, and
166             # a Pod::Paragraph object. Just output it verbatim, but with tabs converted
167             # to spaces.
168             sub verbatim {
169 6     6 0 15 my $self = shift;
170 6 50       22 return if $$self{EXCLUDE};
171 6 100       32 $self->item if defined $$self{ITEM};
172 6         13 local $_ = shift;
173 6 100       216 return if /^\s*$/;
174 3         22 s/^(\s*\S+)/(' ' x $$self{MARGIN}) . $1/gme;
  7         35  
175 3         16 return $self->output($_);
176             }
177              
178             # Called for a regular text block. Gets the paragraph, the line number, and
179             # a Pod::Paragraph object. Perform interpolation and output the results.
180             sub textblock {
181 81     81 0 134 my $self = shift;
182 81 100       188 return if $$self{EXCLUDE};
183 78 100       139 if($$self{VERBATIM}) {
184 4         9 $self->output($_[0]);
185 4         51 return;
186             }
187 74         98 local $_ = shift;
188 74         90 my $line = shift;
189              
190             # Perform a little magic to collapse multiple L<> references. This is
191             # here mostly for backwards-compatibility. We'll just rewrite the whole
192             # thing into actual text at this part, bypassing the whole internal
193             # sequence parsing thing.
194 74         120 s{
195             (
196             L< # A link of the form L.
197             /
198             (
199             [:\w]+ # The item has to be a simple word...
200             (\(\))? # ...or simple function.
201             )
202             >
203             (
204             ,?\s+(and\s+)? # Allow lots of them, conjuncted.
205             L<
206             /
207             (
208             [:\w]+
209             (\(\))?
210             )
211             >
212             )+
213             )
214             } {
215 0         0 local $_ = $1;
216 0         0 s%L]+)>%$1%g;
217 0         0 my @items = split /(?:,?\s+(?:and\s+)?)/;
218 0         0 my $string = "the ";
219 0         0 my $i;
220 0         0 for ($i = 0; $i < @items; $i++) {
221 0         0 $string .= $items[$i];
222 0 0 0     0 $string .= ", " if @items > 2 && $i != $#items;
223 0 0       0 $string .= " and " if ($i == $#items - 1);
224             }
225 0         0 $string .= " entries elsewhere in this document";
226 0         0 $string;
227             }gex;
228              
229             # Now actually interpolate and output the paragraph.
230 74         4552 $_ = $self->interpolate ($_, $line);
231 74         821 s/\s*$/\n/s;
232 74 100       184 if (defined $$self{ITEM}) {
233 13         54 $self->item ($_ . "\n");
234             } else {
235 61         265 $self->output ($self->reformat ($_ . "\n"));
236             }
237             }
238              
239             # Called for an interior sequence. Gets the command, argument, and a
240             # Pod::InteriorSequence object and is expected to return the resulting text.
241             # Calls code, bold, italic, file, and link to handle those types of
242             # sequences, and handles S<>, E<>, X<>, and Z<> directly.
243             sub interior_sequence {
244 107     107 0 228 my $self = shift;
245 107         121 my $command = shift;
246 107         137 local $_ = shift;
247 107 50 33     356 return '' if ($command eq 'X' || $command eq 'Z');
248              
249             # Expand escapes into the actual character now, carping if invalid.
250 107 100       166 if ($command eq 'E') {
251 10 50       342 return $ESCAPES{$_} if defined $ESCAPES{$_};
252 0         0 carp "Unknown escape: E<$_>";
253 0         0 return "E<$_>";
254             }
255              
256             # For all the other sequences, empty content produces no output.
257 97 100       211 return if $_ eq '';
258              
259             # For S<>, compress all internal whitespace and then map spaces to \01.
260             # When we output the text, we'll map this back.
261 96 100       163 if ($command eq 'S') {
262 3         4 s/\s{2,}/ /g;
263 3         3 tr/ /\01/;
264 3         99 return $_;
265             }
266              
267             # Anything else needs to get dispatched to another method.
268 93 100       220 if ($command eq 'B') { return $self->seq_b ($_) }
  28 100       77  
    100          
    100          
    50          
269 26         93 elsif ($command eq 'C') { return $self->seq_c ($_) }
270 2         23 elsif ($command eq 'F') { return $self->seq_f ($_) }
271 14         49 elsif ($command eq 'I') { return $self->seq_i ($_) }
272 23         58 elsif ($command eq 'L') { return $self->seq_l ($_) }
273 0         0 else { carp "Unknown sequence $command<$_>" }
274             }
275              
276             # Called for each paragraph that's actually part of the POD. We take
277             # advantage of this opportunity to untabify the input.
278             sub preprocess_paragraph {
279 158     158 0 5745 my $self = shift;
280 158         240 local $_ = shift;
281 158         408 1 while s/^(.*?)(\t+)/$1 . ' ' x (length ($2) * 8 - length ($1) % 8)/me;
  0         0  
282 158         3785 return $_;
283             }
284              
285              
286             ############################################################################
287             # Command paragraphs
288             ############################################################################
289              
290             # All command paragraphs take the paragraph and the line number.
291              
292             # First level heading.
293             sub cmd_head1 {
294 16     16 0 24 my $self = shift;
295 16         28 local $_ = shift;
296 16         62 s/\s+$//s;
297 16         967 $_ = $self->interpolate ($_, shift);
298 16 50       58 if ($$self{alt}) {
299 0         0 $self->output ("\n==== $_ ====\n\n");
300             } else {
301 16 50       41 $_ .= "\n" if $$self{loose};
302 16         68 $self->output ($_ . "\n");
303             }
304             }
305              
306             # Second level heading.
307             sub cmd_head2 {
308 0     0 0 0 my $self = shift;
309 0         0 local $_ = shift;
310 0         0 s/\s+$//s;
311 0         0 $_ = $self->interpolate ($_, shift);
312 0 0       0 if ($$self{alt}) {
313 0         0 $self->output ("\n== $_ ==\n\n");
314             } else {
315 0 0       0 $_ .= "\n" if $$self{loose};
316 0         0 $self->output (' ' x ($$self{indent} / 2) . $_ . "\n");
317             }
318             }
319              
320             # third level heading - not strictly perlpodspec compliant
321             sub cmd_head3 {
322 0     0 0 0 my $self = shift;
323 0         0 local $_ = shift;
324 0         0 s/\s+$//s;
325 0         0 $_ = $self->interpolate ($_, shift);
326 0 0       0 if ($$self{alt}) {
327 0         0 $self->output ("\n= $_ =\n");
328             } else {
329 0 0       0 $_ .= "\n" if $$self{loose};
330 0         0 $self->output (' ' x ($$self{indent}) . $_ . "\n");
331             }
332             }
333              
334             # fourth level heading - not strictly perlpodspec compliant
335             # just like head3
336             *cmd_head4 = \&cmd_head3;
337              
338             # Start a list.
339             sub cmd_over {
340 7     7 0 15 my $self = shift;
341 7         15 local $_ = shift;
342 7 50       44 unless (/^[-+]?\d+\s+$/) { $_ = $$self{indent} }
  0         0  
343 7         22 push (@{ $$self{INDENTS} }, $$self{MARGIN});
  7         34  
344 7         145 $$self{MARGIN} += ($_ + 0);
345             }
346              
347             # End a list.
348             sub cmd_back {
349 7     7 0 21 my $self = shift;
350 7         13 $$self{MARGIN} = pop @{ $$self{INDENTS} };
  7         23  
351 7 50       138 unless (defined $$self{MARGIN}) {
352 0         0 carp 'Unmatched =back';
353 0         0 $$self{MARGIN} = $$self{indent};
354             }
355             }
356              
357             # An individual list item.
358             sub cmd_item {
359 16     16 0 29 my $self = shift;
360 16 50       44 if (defined $$self{ITEM}) { $self->item }
  0         0  
361 16         24 local $_ = shift;
362 16         56 s/\s+$//s;
363 16         847 $$self{ITEM} = $self->interpolate ($_);
364             }
365              
366             # Begin a block for a particular translator. Setting VERBATIM triggers
367             # special handling in textblock().
368             sub cmd_begin {
369 2     2 0 3 my $self = shift;
370 2         3 local $_ = shift;
371 2 50       18 my ($kind) = /^(\S+)/ or return;
372 2 100       6 if ($kind eq 'text') {
373 1         20 $$self{VERBATIM} = 1;
374             } else {
375 1         14 $$self{EXCLUDE} = 1;
376             }
377             }
378              
379             # End a block for a particular translator. We assume that all =begin/=end
380             # pairs are properly closed.
381             sub cmd_end {
382 2     2 0 5 my $self = shift;
383 2         3 $$self{EXCLUDE} = 0;
384 2         30 $$self{VERBATIM} = 0;
385             }
386              
387             # One paragraph for a particular translator. Ignore it unless it's intended
388             # for text, in which case we treat it as a verbatim text block.
389             sub cmd_for {
390 2     2 0 3 my $self = shift;
391 2         2 local $_ = shift;
392 2         4 my $line = shift;
393 2 100       28 return unless s/^text\b[ \t]*\r?\n?//;
394 1         6 $self->verbatim ($_, $line);
395             }
396              
397             # just a dummy method for the time being
398             sub cmd_encoding {
399 0     0 0 0 return;
400             }
401              
402             ############################################################################
403             # Interior sequences
404             ############################################################################
405              
406             # The simple formatting ones. These are here mostly so that subclasses can
407             # override them and do more complicated things.
408 28 50   28 0 1200 sub seq_b { return $_[0]{alt} ? "``$_[1]''" : $_[1] }
409 26 50   26 0 1152 sub seq_c { return $_[0]{alt} ? "``$_[1]''" : "`$_[1]'" }
410 2 50   2 0 57 sub seq_f { return $_[0]{alt} ? "\"$_[1]\"" : $_[1] }
411 14     14 0 605 sub seq_i { return '*' . $_[1] . '*' }
412              
413             # The complicated one. Handle links. Since this is plain text, we can't
414             # actually make any real links, so this is all to figure out what text we
415             # print out.
416             sub seq_l {
417 23     23 0 33 my $self = shift;
418 23         25 local $_ = shift;
419              
420             # Smash whitespace in case we were split across multiple lines.
421 23         81 s/\s+/ /g;
422              
423             # If we were given any explicit text, just output it.
424 23 100       64 if (/^([^|]+)\|/) { return $1 }
  9         211  
425              
426             # Okay, leading and trailing whitespace isn't important; get rid of it.
427 14         34 s/^\s+//;
428 14         23 s/\s+$//;
429              
430             # Default to using the whole content of the link entry as a section
431             # name. Note that L forces a manpage interpretation, as does
432             # something looking like L. The latter is an
433             # enhancement over the original Pod::Text.
434 14         27 my ($manpage, $section) = ('', $_);
435 14 100       90 if (/^(?:https?|ftp|news):/) {
    100          
    100          
    50          
436             # a URL
437 1         24 return $_;
438             } elsif (/^"\s*(.*?)\s*"$/) {
439 1         4 $section = '"' . $1 . '"';
440             } elsif (m/^[-:.\w]+(?:\(\S+\))?$/) {
441 2         53 ($manpage, $section) = ($_, '');
442             } elsif (m{/}) {
443 10         50 ($manpage, $section) = split (/\s*\/\s*/, $_, 2);
444             }
445              
446 13         21 my $text = '';
447             # Now build the actual output text.
448 13 100       48 if (!length $section) {
    100          
449 2 50       8 $text = "the $manpage manpage" if length $manpage;
450             } elsif ($section =~ /^[:\w]+(?:\(\))?/) {
451 8         20 $text .= 'the ' . $section . ' entry';
452 8 50       20 $text .= (length $manpage) ? " in the $manpage manpage"
453             : ' elsewhere in this document';
454             } else {
455 3         12 $section =~ s/^\"\s*//;
456 3         12 $section =~ s/\s*\"$//;
457 3         9 $text .= 'the section on "' . $section . '"';
458 3 100       11 $text .= " in the $manpage manpage" if length $manpage;
459             }
460 13         378 return $text;
461             }
462              
463              
464             ############################################################################
465             # List handling
466             ############################################################################
467              
468             # This method is called whenever an =item command is complete (in other
469             # words, we've seen its associated paragraph or know for certain that it
470             # doesn't have one). It gets the paragraph associated with the item as an
471             # argument. If that argument is empty, just output the item tag; if it
472             # contains a newline, output the item tag followed by the newline.
473             # Otherwise, see if there's enough room for us to output the item tag in the
474             # margin of the text or if we have to put it on a separate line.
475             sub item {
476 16     16 1 28 my $self = shift;
477 16         71 local $_ = shift;
478 16         23 my $tag = $$self{ITEM};
479 16 50       38 unless (defined $tag) {
480 0         0 carp 'item called without tag';
481 0         0 return;
482             }
483 16         27 undef $$self{ITEM};
484 16         41 my $indent = $$self{INDENTS}[-1];
485 16 50       39 unless (defined $indent) { $indent = $$self{indent} }
  0         0  
486 16         34 my $space = ' ' x $indent;
487 16 50       40 $space =~ s/^ /:/ if $$self{alt};
488 16 100 100     110 if (!$_ || /^\s+$/ || ($$self{MARGIN} - $indent < length ($tag) + 1)) {
      100        
489 6         15 my $margin = $$self{MARGIN};
490 6         10 $$self{MARGIN} = $indent;
491 6         30 my $output = $self->reformat ($tag);
492 6         33 $output =~ s/[\r\n]*$/\n/;
493 6         24 $self->output ($output);
494 6         10 $$self{MARGIN} = $margin;
495 6 100 100     60 $self->output ($self->reformat ($_)) if defined && /\S/;
496             } else {
497 10         41 $_ = $self->reformat ($_);
498 10 50 33     23 s/^ /:/ if ($$self{alt} && $indent > 0);
499 10         17 my $tagspace = ' ' x length $tag;
500 10 50       152 s/^($space)$tagspace/$1$tag/ or carp 'Bizarre space in item';
501 10         34 $self->output ($_);
502             }
503             }
504              
505              
506             ############################################################################
507             # Output formatting
508             ############################################################################
509              
510             # Wrap a line, indenting by the current left margin. We can't use
511             # Text::Wrap because it plays games with tabs. We can't use formline, even
512             # though we'd really like to, because it screws up non-printing characters.
513             # So we have to do the wrapping ourselves.
514             sub wrap {
515 80     80 0 101 my $self = shift;
516 80         103 local $_ = shift;
517 80         102 my $output = '';
518 80         159 my $spaces = ' ' x $$self{MARGIN};
519 80         120 my $width = $$self{width} - $$self{MARGIN};
520 80         185 while (length > $width) {
521 49 50 33     751 if (s/^([^\r\n]{0,$width})\s+// || s/^([^\r\n]{$width})//) {
522 49         200 $output .= $spaces . $1 . "\n";
523             } else {
524 0         0 last;
525             }
526             }
527 80         165 $output .= $spaces . $_;
528 80         361 $output =~ s/\s+$/\n\n/;
529 80         275 return $output;
530             }
531              
532             # Reformat a paragraph of text for the current margin. Takes the text to
533             # reformat and returns the formatted text.
534             sub reformat {
535 80     80 0 119 my $self = shift;
536 80         105 local $_ = shift;
537              
538             # If we're trying to preserve two spaces after sentences, do some
539             # munging to support that. Otherwise, smash all repeated whitespace.
540 80 50       144 if ($$self{sentence}) {
541 0         0 s/ +$//mg;
542 0         0 s/\.\r?\n/. \n/g;
543 0         0 s/[\r\n]+/ /g;
544 0         0 s/ +/ /g;
545             } else {
546 80         557 s/\s+/ /g;
547             }
548 80         223 return $self->wrap($_);
549             }
550              
551             # Output text to the output device.
552 103     103 0 183 sub output { $_[1] =~ tr/\01/ /; print { $_[0]->output_handle } $_[1] }
  103         134  
  103         2940  
553              
554              
555             ############################################################################
556             # Backwards compatibility
557             ############################################################################
558              
559             # The old Pod::Text module did everything in a pod2text() function. This
560             # tries to provide the same interface for legacy applications.
561             sub pod2text {
562 0     0 0   my @args;
563              
564             # This is really ugly; I hate doing option parsing in the middle of a
565             # module. But the old Pod::Text module supported passing flags to its
566             # entry function, so handle -a and -.
567 0           while ($_[0] =~ /^-/) {
568 0           my $flag = shift;
569 0 0         if ($flag eq '-a') { push (@args, alt => 1) }
  0 0          
570 0           elsif ($flag =~ /^-(\d+)$/) { push (@args, width => $1) }
571             else {
572 0           unshift (@_, $flag);
573 0           last;
574             }
575             }
576              
577             # Now that we know what arguments we're using, create the parser.
578 0           my $parser = Pod::PlainText->new (@args);
579              
580             # If two arguments were given, the second argument is going to be a file
581             # handle. That means we want to call parse_from_filehandle(), which
582             # means we need to turn the first argument into a file handle. Magic
583             # open will handle the <&STDIN case automagically.
584 0 0         if (defined $_[1]) {
585 0           my $infh;
586 0 0         if ($] < 5.006) {
587 0           $infh = gensym();
588             }
589 0 0         unless (open ($infh, $_[0])) {
590 0           croak ("Can't open $_[0] for reading: $!\n");
591             }
592 0           $_[0] = $infh;
593 0           return $parser->parse_from_filehandle (@_);
594             } else {
595 0           return $parser->parse_from_file (@_);
596             }
597             }
598              
599              
600             ############################################################################
601             # Module return value and documentation
602             ############################################################################
603              
604             1;
605             __END__