File Coverage

blib/lib/Pod/Hlp.pm
Criterion Covered Total %
statement 7 199 3.5
branch 0 86 0.0
condition 0 18 0.0
subroutine 3 15 20.0
pod 0 12 0.0
total 10 330 3.0


line stmt bran cond sub pod time code
1             package Pod::Hlp;
2              
3             $VERSION = '1.02';
4              
5             # based on Tom C's:
6             #package Pod::Text;
7             # Version 1.01
8              
9             =head1 NAME
10              
11             Pod::Hlp - convert POD data to formatted VMS HLP Help module text.
12              
13             =head1 SYNOPSIS
14              
15             use Pod::Hlp;
16              
17             pod2hlp("perlfunc.pod",$top_help_level,*Filehandle);
18              
19             Also:
20              
21             pod2hlp < input.pod
22              
23             Also:
24              
25             perl pod2hlb
26              
27             =head1 DESCRIPTION
28              
29             Pod::Hlp is a module that can convert documentation in the POD format
30             (such as can be found throughout the Perl distribution) into formatted
31             VMS C<*.HLP> files. Such files can be inserted into an .HLB library
32             through the C system call, or via the use of the
33             C script supplied with the kit. A separate F program
34             is included that is primarily a wrapper for Pod::Hlp.
35              
36             The single function C can take one, two, or three arguments.
37             The first should be the name of a file to read the pod from, or "<&STDIN"
38             to read from STDIN. A second argument, if provided, should be an
39             integer indicating the help header level of the file as a whole where
40             C<'1'> is the default. A third argument, if provided, should be a
41             filehandle glob where output should be sent.
42              
43             =head1 AUTHOR
44              
45             Peter Prymmer Epvhp@best.comE
46              
47             based heavily on Pod::Text by:
48              
49             Tom Christiansen Etchrist@mox.perl.comE
50              
51             =head1 TODO
52              
53             Cleanup work. VT escapes should be substituted for the
54             Term::Cap ones. The input and output locations need to be more
55             flexible.
56              
57             =cut
58              
59             require Exporter;
60             @ISA = Exporter;
61             #@EXPORT = qw(pod2text);
62              
63             $UNDL = "\x1b[4m";
64             $INV = "\x1b[7m";
65             $BOLD = "\x1b[1m";
66             $NORM = "\x1b[0m";
67              
68             @head1_freq_patterns # =head1 patterns which need not be index'ed
69             = ("AUTHOR","BUGS","DATE","DESCRIPTION","DIAGNOSTICS",
70             "ENVIRONMENT","EXAMPLES","FILES","INTRODUCTION","NAME","NOTE",
71             "SEE ALSO","SYNOPSIS","WARNING");
72              
73             sub pod2hlp {
74 0     0 0   local($file,$hlp_level,*OUTPUT) = @_;
75 0 0         $hlp_level = '1' if @_<2;
76 0           $head1_level = $hlp_level + 1;
77 0           $head2_level = $head1_level + 1;
78 0           $last_cmd = $hlp_level;
79 0 0         *OUTPUT = *STDOUT if @_<3;
80              
81 0           $SCREEN = 72;
82              
83 0           $/ = "";
84              
85 0           $FANCY = 0;
86              
87 0           $cutting = 1;
88 0           $DEF_INDENT = 4;
89 0           $indent = $DEF_INDENT;
90 0           $needspace = 0;
91              
92 0 0         open(IN, $file) || die "Couldn't open $file: $!";
93              
94 0           POD_DIRECTIVE: while () {
95 0 0         if ($cutting) {
96 0 0         next unless /^=/;
97 0           $cutting = 0;
98             }
99 0           1 while s{^(.*?)(\t+)(.*)$}{
100 0           $1
101             . (' ' x (length($2) * 8 - length($1) % 8))
102             . $3
103             }me;
104             # Translate verbatim paragraph
105 0 0         if (/^\s/) {
106 0           $needspace = 1;
107 0           output($_);
108 0           next;
109             }
110              
111             sub prepare_for_output {
112              
113 0     0 0   s/\s*$/\n/;
114 0           &init_noremap;
115              
116             # need to hide E<> first; they're processed in clear_noremap
117 0           s/(E<[^<>]+>)/noremap($1)/ge;
  0            
118 0           $maxnest = 10;
119 0   0       while ($maxnest-- && /[A-Z]
120 0 0         unless ($FANCY) {
121 0           s/C<(.*?)>/`$1'/g;
122             } else {
123 0           s/C<(.*?)>/noremap("E${1}E")/ge;
  0            
124             }
125             # s/[IF]<(.*?)>/italic($1)/ge;
126 0           s/I<(.*?)>/*$1*/g;
127             # s/[CB]<(.*?)>/bold($1)/ge;
128 0           s/X<.*?>//g;
129             # LREF: a manpage(3f)
130 0           m:L<([a-zA-Z][^\s\/]+)(\([^\)]+\))?>:;
131 0 0         if (defined($2)) {
132 0           s:L<([a-zA-Z][^\s\/]+)(\([^\)]+\))?>:the $1$2 help page:g;
133             }
134             else {
135 0           s:L<([a-zA-Z][^\s\/]+)(\([^\)]+\))?>:the $1 help page:g;
136             }
137             # LREF: an =item on another manpage
138 0           s{
139             L<
140             ([^/]+)
141             /
142             (
143             [:\w]+
144             (\(\))?
145             )
146             >
147             } {the "$2" entry in the $1 help page}gx;
148              
149             # LREF: an =item on this manpage
150 0           s{
151             ((?:
152             L<
153             /
154             (
155             [:\w]+
156             (\(\))?
157             )
158             >
159             (,?\s+(and\s+)?)?
160             )+)
161 0           } { internal_lrefs($1) }gex;
162              
163             # LREF: a =head2 (head1?), maybe on a manpage, maybe right here
164             # the "func" can disambiguate
165 0           s{
166             L<
167             (?:
168             ([a-zA-Z]\S+?) /
169             )?
170             "?(.*?)"?
171             >
172             }{
173 0           do {
174 0 0         $1 # if no $1, assume it means on this page.
175             ? "the section on \"$2\" in the $1 help page"
176             : "the section on \"$2\""
177             }
178             }gex;
179              
180 0           s/[A-Z]<(.*?)>/$1/g;
181             }
182 0           clear_noremap(1);
183             }
184              
185 0           &prepare_for_output;
186              
187 0 0         if (s/^=//) {
188             # $needspace = 0; # Assume this.
189             # s/\n/ /g;
190 0           ($Cmd, $_) = split(' ', $_, 2);
191             # clear_noremap(1);
192 0 0         if ($Cmd eq 'cut') {
    0          
    0          
    0          
    0          
    0          
193 0           $cutting = 1;
194             }
195             elsif ($Cmd eq 'head1') {
196 0           makespace();
197              
198             # Is this ugly or what?
199 0 0         if ($last_cmd > $head1_level) {
200 0           $last_cmd = $head1_level;
201 0           goto make_head1_anyway;
202             }
203 0           for $pat (@head1_freq_patterns) {
204 0 0         if (/^$pat/i) { goto freqpatt; }
  0            
205             }
206             make_head1_anyway:
207             # VMS librarian does not like to make n+2 jumps:
208 0 0         if (($head1_level - $last_cmd)<=1) {
209 0           $last_cmd = $head1_level;
210             }
211             else {
212 0           $last_cmd = $last_cmd + 1;
213             }
214              
215 0           $hlp_line = $_;
216             # The key names for help topics and subtopics can include any
217             # printable ASCII characters except those used by LIBRARIAN
218             # as either delimiters (space, horizontal tab, and comma) or
219             # comments (exclamation point).
220 0 0         if ($hlp_line =~ s/[\ \t\r\f]+/'_'/eg) { #\s would match \n
  0            
221 0           $hlp_line =~ s/^[_]//; #trim lead
222 0           $hlp_line =~ s/_$//; #trim trail
223             }
224 0           chomp($hlp_line);
225 0           $hlp_line = "$last_cmd $hlp_line\n";
226 0           print OUTPUT "$hlp_line";
227 0           freqpatt:
228             print OUTPUT;
229             # print OUTPUT uc($_);
230             }
231             elsif ($Cmd eq 'head2') {
232 0           makespace();
233 0 0         s/(\w)/\xA7 $1/ if $FANCY;
234 0           $hlp_line = $_;
235 0 0         if ($hlp_line =~ s/[\ \t\r\f]+/'_'/eg) { #\s would match \n
  0            
236 0           $hlp_line =~ s/^[_]//; #trim lead
237 0           $hlp_line =~ s/_$//; #trim trail
238             }
239 0           chomp($hlp_line);
240              
241             # perlpod.pod only allows for =head1 and =head2 (N.B. relaxed
242             # with more recent pod specs), nevertheless
243             # VMS librarian does not like to make n+2 jumps, which
244             # could still occur if the file began with =head2 e.g.:
245 0 0         if (($head2_level - $last_cmd)<=1) {
246 0           $last_cmd = $head2_level;
247             } else {
248 0           $last_cmd = $last_cmd + 1;
249             }
250              
251 0           $hlp_line = "$last_cmd $hlp_line\n";
252 0           print OUTPUT "$hlp_line";
253 0           print OUTPUT ' ' x ($DEF_INDENT/2), $_, "\n";
254             }
255             elsif ($Cmd eq 'over') {
256 0           push(@indent,$indent);
257 0   0       $indent += ($_ + 0) || $DEF_INDENT;
258             }
259             elsif ($Cmd eq 'back') {
260 0           $indent = pop(@indent);
261 0 0         warn "Unmatched =back\n" unless defined $indent;
262 0           $needspace = 1;
263             }
264             elsif ($Cmd eq 'item') {
265 0           makespace();
266             # s/\A(\s*)\*/$1\xb7/ if $FANCY;
267             # s/^(\s*\*\s+)/$1 /;
268             {
269 0 0         if (length() + 3 < $indent) {
  0            
270 0           my $paratag = $_;
271 0           $_ = ;
272 0 0         if (/^=/) { # tricked!
273 0   0       local($indent) = $indent[$#index - 1] || $DEF_INDENT;
274 0           output($paratag);
275 0           redo POD_DIRECTIVE;
276             }
277 0           &prepare_for_output;
278 0           IP_output($paratag, $_);
279             } else {
280 0   0       local($indent) = $indent[$#index - 1] || $DEF_INDENT;
281 0           output($_);
282             }
283             }
284             }
285             else {
286 0           warn "Unrecognized directive: $Cmd\n";
287             }
288             }
289             else {
290             # clear_noremap(1);
291 0           makespace();
292 0           output($_, 1);
293             }
294             }
295              
296 0           close(IN);
297              
298             }
299              
300             #########################################################################
301              
302             sub makespace {
303 0 0   0 0   if ($needspace) {
304 0           print OUTPUT "\n";
305 0           $needspace = 0;
306             }
307             }
308              
309             sub bold {
310 0     0 0   my $line = shift;
311 0 0         return $line if $use_format;
312 0           $line =~ s/(.)/$1\b$1/g;
313 0           return $line;
314             }
315              
316             sub italic {
317 0     0 0   my $line = shift;
318 0 0         return $line if $use_format;
319 0           $line =~ s/(.)/$1\b_/g;
320 0           return $line;
321             }
322              
323             # Fill a paragraph including underlined and overstricken chars.
324             # It's not perfect for words longer than the margin, and it's probably
325             # slow, but it works.
326             sub fill {
327 0     0 0   local $_ = shift;
328 0           my $par = "";
329 0           my $indent_space = " " x $indent;
330 0           my $marg = $SCREEN-$indent;
331 0           my $line = $indent_space;
332 0           my $line_length;
333 0           foreach (split) {
334 0           my $word_length = length;
335 0           $word_length -= 2 while /\010/g; # Subtract backspaces
336              
337 0 0         if ($line_length + $word_length > $marg) {
338 0           $par .= $line . "\n";
339 0           $line= $indent_space . $_;
340 0           $line_length = $word_length;
341             }
342             else {
343 0 0         if ($line_length) {
344 0           $line_length++;
345 0           $line .= " ";
346             }
347 0           $line_length += $word_length;
348 0           $line .= $_;
349             }
350             }
351 0 0         $par .= "$line\n" if $line;
352 0           $par .= "\n";
353 0           return $par;
354             }
355              
356             sub IP_output {
357 0     0 0   local($tag, $_) = @_;
358 0   0       local($tag_indent) = $indent[$#index - 1] || $DEF_INDENT;
359 0           $tag_cols = $SCREEN - $tag_indent;
360 0           $cols = $SCREEN - $indent;
361 0           $tag =~ s/\s*$//;
362 0           s/\s+/ /g;
363 0           s/^ //;
364 1     1   2857 no strict;
  1         2  
  1         194  
365 0           $str = "format OUTPUT = \n"
366             . (" " x ($tag_indent))
367             . '@' . ('<' x ($indent - $tag_indent - 1))
368             . "^" . ("<" x ($cols - 1)) . "\n"
369             . '$tag, $_'
370             . "\n~~"
371             . (" " x ($indent-2))
372             . "^" . ("<" x ($cols - 5)) . "\n"
373             . '$_' . "\n\n.\n1";
374             #warn $str; warn "tag is $tag, _ is $_";
375             {
376             # Avoid "redefined OUTPUT format" warnings.
377             # perldiag in 5.6.1 recommends no warnings pragma but this works
378             # with 5.005_03
379 0           local $^W = 0;
  0            
380 0 0         eval $str || die;
381             }
382 0           write OUTPUT;
383             }
384              
385             sub output {
386 0     0 0   local($_, $reformat) = @_;
387 1     1   6 no strict;
  1         1  
  1         945  
388 0 0         if ($reformat) {
389 0           $cols = $SCREEN - $indent;
390 0           s/\s+/ /g;
391 0           s/^ //;
392 0           $str = "format OUTPUT = \n~~"
393             . (" " x ($indent-2))
394             . "^" . ("<" x ($cols - 5)) . "\n"
395             . '$_' . "\n\n.\n1";
396             {
397             # Avoid "redefined OUTPUT format" warnings.
398             # perldiag in 5.6.1 recommends no warnings pragma but this works
399             # with 5.005_03
400 0           local $^W = 0;
  0            
401 0 0         eval $str || die;
402             }
403 0           write OUTPUT;
404             } else {
405 0           s/^/' ' x $indent/gem;
  0            
406 0           s/^\s+\n$/\n/gm;
407 0           print OUTPUT;
408             }
409             }
410              
411             sub noremap {
412 0     0 0   local($thing_to_hide) = shift;
413 0           $thing_to_hide =~ tr/\000-\177/\200-\377/;
414 0           return $thing_to_hide;
415             }
416              
417             sub init_noremap {
418 0 0   0 0   die "unmatched init" if $mapready++;
419 0 0         if ( /[\200-\377]/ ) {
420 0           warn "hi bit char in input stream";
421             }
422             }
423              
424             sub clear_noremap {
425 0     0 0   my $ready_to_print = $_[0];
426 0 0         die "unmatched clear" unless $mapready--;
427 0           tr/\200-\377/\000-\177/;
428             # now for the E<>s, which have been hidden until now
429             # otherwise the interative \w<> processing would have
430             # been hosed by the E
431 0 0         s {
432             E<
433             ( [A-Za-z]+ )
434             >
435             } {
436 0           do {
437             defined $HTML_Escapes{$1}
438 0           ? do { $HTML_Escapes{$1} }
439 0 0         : do {
440 0           warn "Unknown escape: $& in $_";
441 0           "E<$1>";
442             }
443             }
444             }egx if $ready_to_print;
445             }
446              
447             sub internal_lrefs {
448 0     0 0   local($_) = shift;
449 0           s{L]+)>}{$1}g;
450 0           my(@items) = split( /(?:,?\s+(?:and\s+)?)/ );
451 0           my $retstr = "the ";
452 0           my $i;
453 0           for ($i = 0; $i <= $#items; $i++) {
454 0           $retstr .= "C<$items[$i]>";
455 0 0 0       $retstr .= ", " if @items > 2 && $i != $#items;
456 0 0         $retstr .= " and " if $i+2 == @items;
457             }
458              
459 0 0         $retstr .= " entr" . ( @items > 1 ? "ies" : "y" )
460             . " elsewhere in this document ";
461              
462 0           return $retstr;
463              
464             }
465              
466             BEGIN {
467              
468 1     1   79 %HTML_Escapes = (
469             'amp' => '&', # ampersand
470             'lt' => '<', # left chevron, less-than
471             'gt' => '>', # right chevron, greater-than
472             'quot' => '"', # double quote
473             'sol' => '/', # solidus or forward slash
474             'verbar' => '|', # vertical bar or pipe
475              
476             "Aacute" => "\xC1", # capital A, acute accent
477             "aacute" => "\xE1", # small a, acute accent
478             "Acirc" => "\xC2", # capital A, circumflex accent
479             "acirc" => "\xE2", # small a, circumflex accent
480             "AElig" => "\xC6", # capital AE diphthong (ligature)
481             "aelig" => "\xE6", # small ae diphthong (ligature)
482             "Agrave" => "\xC0", # capital A, grave accent
483             "agrave" => "\xE0", # small a, grave accent
484             "Aring" => "\xC5", # capital A, ring
485             "aring" => "\xE5", # small a, ring
486             "Atilde" => "\xC3", # capital A, tilde
487             "atilde" => "\xE3", # small a, tilde
488             "Auml" => "\xC4", # capital A, dieresis or umlaut mark
489             "auml" => "\xE4", # small a, dieresis or umlaut mark
490             "Ccedil" => "\xC7", # capital C, cedilla
491             "ccedil" => "\xE7", # small c, cedilla
492             "Eacute" => "\xC9", # capital E, acute accent
493             "eacute" => "\xE9", # small e, acute accent
494             "Ecirc" => "\xCA", # capital E, circumflex accent
495             "ecirc" => "\xEA", # small e, circumflex accent
496             "Egrave" => "\xC8", # capital E, grave accent
497             "egrave" => "\xE8", # small e, grave accent
498             "ETH" => "\xD0", # capital Eth, Icelandic
499             "eth" => "\xF0", # small eth, Icelandic
500             "Euml" => "\xCB", # capital E, dieresis or umlaut mark
501             "euml" => "\xEB", # small e, dieresis or umlaut mark
502             "Iacute" => "\xCD", # capital I, acute accent
503             "iacute" => "\xED", # small i, acute accent
504             "Icirc" => "\xCE", # capital I, circumflex accent
505             "icirc" => "\xEE", # small i, circumflex accent
506             "Igrave" => "\xCD", # capital I, grave accent
507             "igrave" => "\xED", # small i, grave accent
508             "Iuml" => "\xCF", # capital I, dieresis or umlaut mark
509             "iuml" => "\xEF", # small i, dieresis or umlaut mark
510             "Ntilde" => "\xD1", # capital N, tilde
511             "ntilde" => "\xF1", # small n, tilde
512             "Oacute" => "\xD3", # capital O, acute accent
513             "oacute" => "\xF3", # small o, acute accent
514             "Ocirc" => "\xD4", # capital O, circumflex accent
515             "ocirc" => "\xF4", # small o, circumflex accent
516             "Ograve" => "\xD2", # capital O, grave accent
517             "ograve" => "\xF2", # small o, grave accent
518             "Oslash" => "\xD8", # capital O, slash
519             "oslash" => "\xF8", # small o, slash
520             "Otilde" => "\xD5", # capital O, tilde
521             "otilde" => "\xF5", # small o, tilde
522             "Ouml" => "\xD6", # capital O, dieresis or umlaut mark
523             "ouml" => "\xF6", # small o, dieresis or umlaut mark
524             "szlig" => "\xDF", # small sharp s, German (sz ligature)
525             "THORN" => "\xDE", # capital THORN, Icelandic
526             "thorn" => "\xFE", # small thorn, Icelandic
527             "Uacute" => "\xDA", # capital U, acute accent
528             "uacute" => "\xFA", # small u, acute accent
529             "Ucirc" => "\xDB", # capital U, circumflex accent
530             "ucirc" => "\xFB", # small u, circumflex accent
531             "Ugrave" => "\xD9", # capital U, grave accent
532             "ugrave" => "\xF9", # small u, grave accent
533             "Uuml" => "\xDC", # capital U, dieresis or umlaut mark
534             "uuml" => "\xFC", # small u, dieresis or umlaut mark
535             "Yacute" => "\xDD", # capital Y, acute accent
536             "yacute" => "\xFD", # small y, acute accent
537             "yuml" => "\xFF", # small y, dieresis or umlaut mark
538              
539             "lchevron" => "\xAB", # left chevron (double less than)
540             "rchevron" => "\xBB", # right chevron (double greater than)
541             );
542             }
543              
544             1;