File Coverage

perllib/Arch/FileHighlighter.pm
Criterion Covered Total %
statement 84 142 59.1
branch 31 56 55.3
condition 6 17 35.2
subroutine 11 14 78.5
pod 3 5 60.0
total 135 234 57.6


line stmt bran cond sub pod time code
1             # Arch Perl library, Copyright (C) 2004-2005 Mikhael Goikhman
2             #
3             # This program is free software; you can redistribute it and/or modify
4             # it under the terms of the GNU General Public License as published by
5             # the Free Software Foundation; either version 2 of the License, or
6             # (at your option) any later version.
7             #
8             # This program is distributed in the hope that it will be useful,
9             # but WITHOUT ANY WARRANTY; without even the implied warranty of
10             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11             # GNU General Public License for more details.
12             #
13             # You should have received a copy of the GNU General Public License
14             # along with this program; if not, write to the Free Software
15             # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16              
17 2     2   37 use 5.005;
  2         6  
  2         68  
18 2     2   10 use strict;
  2         4  
  2         82  
19              
20             package Arch::FileHighlighter;
21              
22 2     2   494 use Arch::Util qw(run_cmd load_file save_file);
  2         5  
  2         269  
23              
24             sub new ($;$) {
25 3     3 1 8 my $class = shift;
26 3         31 my $filters = shift;
27 3 0 50     12 $filters ||= [ (-x '/usr/bin/enscript'? 'enscript': ()), 'internal' ];
28              
29 3         10 my $self = {
30             filters => $filters,
31             };
32 3         10 bless $self, $class;
33              
34 2     2   12 no strict 'refs';
  2         2  
  2         193  
35 3         5 ${"${class}::global_instance"} = $self;
  3         15  
36 3         97 return $self;
37             }
38              
39             sub instance ($;$) {
40 0     0 1 0 my $class = shift;
41              
42 2     2   9 no strict 'refs';
  2         4  
  2         4847  
43 0   0     0 return ${"${class}::global_instance"} || $class->new(@_);
44             }
45              
46             sub htmlize ($) {
47 6     6 0 12 my $str = shift;
48 6 50       13 die "No content to htmlize" unless defined $str;
49              
50 6         76 $str =~ s/&/&/sg;
51 6         125 $str =~ s/\"/"/sg;
52 6         101 $str =~ s/
53 6         78 $str =~ s/>/>/sg;
54 6         57 return $str;
55             }
56              
57             sub dehtmlize ($) {
58 0     0 0 0 my $str = shift;
59 0 0       0 die "No content to dehtmlize" unless defined $str;
60              
61 0         0 $str =~ s/&/&/sg;
62 0         0 $str =~ s/"/\"/sg;
63 0         0 $str =~ s/</
64 0         0 $str =~ s/>/>/sg;
65 0         0 return $str;
66             }
67              
68             sub highlight ($$;$) {
69 6     6 1 13 my $self = shift;
70 6         12 my $file_name = shift;
71 6         8 my $content = shift;
72              
73 6 100       29 load_file($file_name, \$content) unless defined $content;
74 6 100       23 my $content_ref = ref($content) eq 'SCALAR'? $content: \$content;
75              
76 6 50       689 return undef if -B $file_name;
77              
78 6         13 foreach (@{$self->{filters}}) {
  6         27  
79             # make sure we actually copy $_ and not work in-place
80 8         12 my $filter = $_;
81 8         17 my %args = ();
82 8 100       38 if ($filter =~ /(.*)\((.*)\)/) {
83 4         15 $filter = $1;
84 4         8 my $args = $2;
85 4 50       23 %args = map { /^(.+?)=(.*)$/? ($1 => $2): ($_ => 1) }
  8         37  
86             split(/[^:\w=]+/, $args);
87             }
88 8         21 my $method = "_highlight_$filter";
89 8 50       41 unless ($self->can($method)) {
90 0         0 warn qq(Arch::FileHighlighter: unknown filter "$filter"\n);
91 0         0 next;
92             }
93 8         31 my $html_ref = $self->$method($file_name, $content_ref, %args);
94 8 100       46 return $html_ref if $html_ref;
95             }
96 0         0 $self->_highlight_none($file_name, $content_ref);
97             }
98              
99             sub _highlight_enscript ($$$%) {
100 0     0   0 my $self = shift;
101 0         0 my $file_name = shift;
102 0         0 my $content_ref = shift;
103 0         0 my %args = @_;
104              
105 0         0 my $tmp;
106 0 0       0 if ($content_ref) {
107 0         0 require Arch::TempFiles;
108 0         0 $tmp = Arch::TempFiles->new;
109 0 0       0 $file_name =~ m!^(.*/|^)([^/]+)$! || die "Invalid file name ($file_name)\n";
110 0         0 $file_name = $tmp->dir("highlight") . "/$2";
111 0         0 save_file($file_name, $content_ref);
112             }
113              
114 0         0 my @enscript_args = qw(enscript --output - --quiet --pretty-print);
115 0 0       0 push @enscript_args, "--color" unless $args{"mono"};
116 0         0 push @enscript_args, "--language", "html", $file_name;
117 0         0 my $html = eval { run_cmd(@enscript_args) };
  0         0  
118 0 0       0 return undef unless $html;
119              
120 0         0 $html =~ s!^.*
\n?!!s; $html =~ s!
.*$!!s;
  0         0  
121 0 0 0     0 return undef unless $args{"asis"} || $html =~ /
122              
123 0         0 for (1 .. 3) {
124 0 0       0 my $dot = $_ == 3? ".": "[^<]";
125 0         0 $html =~ s!($dot*?)!$1!sg;
126 0         0 $html =~ s!($dot*?)!$1!sg;
127 0         0 $html =~ s!($dot*?)!$1!sg;
128 0         0 $html =~ s!($dot*?)!$1!sg;
129 0         0 $html =~ s!($dot*?)!$1!sg;
130 0         0 $html =~ s!($dot*?)!$1!sg;
131 0         0 $html =~ s!($dot*?)!$1!sg;
132 0         0 $html =~ s!($dot*?)!$1!sg;
133 0         0 $html =~ s!($dot*?)!$1!sg;
134 0         0 $html =~ s!($dot*?)!$1!sg;
135             }
136 0         0 $html =~ s!(.*?)!$1!sg;
137 0         0 $html =~ s!(.*?)!$1!sg;
138 0         0 $html =~ s!!!sg; # enscript bug with perl highlightling
139 0         0 $html =~ s!(\r?\n)((?:)+)!$2$1!g;
140 0         0 return \$html;
141             }
142              
143             sub _match_file_extension ($$) {
144 4     4   8 my $file_name = shift;
145 4         9 my $args = shift;
146              
147 4         19 while (my ($ext, $value) = each %$args) {
148 8 100 66     185 return 1 if $value && $file_name =~ /\.$ext(\.in)?$/i;
149             }
150 2         13 return 0;
151             }
152              
153             sub _highlight_internal ($$$%) {
154 5     5   7 my $self = shift;
155 5         10 my $file_name = shift;
156 5         6 my $content_ref = shift;
157 5         12 my %args = @_;
158              
159 5         15 my @xml_extensions = qw(html htm shtml sgml xml wml rss glade);
160 5         16 my $xml_extension_regexp = join('|', @xml_extensions);
161              
162 5 100       17 if (%args) {
163 2 50       7 if (exists $args{':xml'}) {
164 0         0 my $value = delete $args{':xml'};
165 0         0 $args{$_} = $value foreach @xml_extensions;
166             }
167 2 100       6 return undef unless _match_file_extension($file_name, \%args);
168             }
169              
170 4 50 33     19 print STDERR "internal highlighting for $file_name\n"
171             if $ENV{DEBUG} && ("$ENV{DEBUG}" & "\1") ne "\0";
172 4         14 my $html = htmlize($$content_ref);
173 4         9 $file_name =~ s/\.in$//;
174 4         8 $file_name = lc($file_name);
175              
176 4 100 66     34 if ($file_name =~ /\.(ac|am|conf|m4|pl|pm|po|py|rb|sh|sql)$/ || $html =~ /^#!/) {
177 2         124 $html =~ s!^([ \t]*)(#.*)!$1$2!mg;
178             }
179 4 50       12 if ($file_name =~ /\.(lisp|lsp|scm|scheme)$/) {
180 0         0 $html =~ s!^([ \t]*)(;.*)!$1$2!mg;
181             }
182 4 100       20 if ($file_name =~ /\.(c|cc|cpp|cxx|c\+\+|h|hpp|idl|php|xpm|l|y)$/) {
183 2         7 $html =~ s!(^|[^\\:])(//.*)!$1$2<\/span>!g;
184 2         35 $html =~ s!(^|[^\\])(/\*.*?\*/)!$1$2<\/span>!sg;
185             }
186 4 50       21 if ($file_name =~ /(^configure(\.ac)?|\.m4)$/) {
187 0         0 $html =~ s!(\bdnl\b.*)!$1<\/span>!g;
188 0         0 $html =~ s!\b(m4_\w+)\b!$1<\/span>!g;
189 0         0 $html =~ s!\b(if|then|else|fi)\b!$1<\/span>!g;
190             }
191 4 50       79 if ($file_name =~ /\.($xml_extension_regexp)$/) {
192 0         0 $html =~ s!(<\!--.*?-->)!$1<\/span>!sg;
193 0         0 $html =~ s!(</?\w+.*?>)!$1<\/span>!sg;
194 0         0 while ($html =~ s!(>(?:<[\w-]+)?\s+)([\w-]+)(=)("[^"]*"|'[^']'|[^\s]*)!$1$2<\/span>$3$4<\/span>!sg) {}
195             }
196 4         17 return \$html;
197             }
198              
199             sub _highlight_none ($$$%) {
200 3     3   7 my $self = shift;
201 3         258 my $file_name = shift;
202 3         7 my $content_ref = shift;
203 3         9 my %args = @_;
204              
205 3 100       10 if (%args) {
206 2 100       8 return undef unless _match_file_extension($file_name, \%args);
207             }
208              
209 2         9 my $html = htmlize($$content_ref);
210 2         7 return \$html;
211             }
212              
213             1;
214              
215             __END__