File Coverage

blib/lib/HTML/WebMake/WmkFile.pm
Criterion Covered Total %
statement 15 343 4.3
branch 0 116 0.0
condition 0 29 0.0
subroutine 5 33 15.1
pod 0 28 0.0
total 20 549 3.6


line stmt bran cond sub pod time code
1             #
2              
3             package HTML::WebMake::WmkFile;
4              
5              
6 1     1   4 use HTML::WebMake::File;
  1         2  
  1         22  
7 1     1   4 use HTML::WebMake::MetaTable;
  1         1  
  1         18  
8 1     1   4 use Carp;
  1         1  
  1         52  
9 1     1   5 use strict;
  1         2  
  1         41  
10              
11 1         5111 use vars qw{
12             @ISA
13             $CGI_EDIT_AS_WMKFILE
14             $CGI_EDIT_AS_DIR
15             $CGI_EDIT_AS_TEXT
16             $CGI_NON_EDITABLE
17 1     1   5 };
  1         8  
18              
19             @ISA = qw(HTML::WebMake::File);
20              
21             $CGI_EDIT_AS_WMKFILE = 1;
22             $CGI_EDIT_AS_DIR = 2;
23             $CGI_EDIT_AS_TEXT = 3;
24             $CGI_NON_EDITABLE = 4;
25              
26             ###########################################################################
27              
28             sub new ($$$) {
29 0     0 0   my $class = shift;
30 0   0       $class = ref($class) || $class;
31 0           my ($main, $filename) = @_;
32 0           my $self = $class->SUPER::new ($main, $filename);
33              
34 0           $self->{cgi} = {
35             'fulltext' => undef,
36             'items' => [ ],
37             };
38              
39 0           bless ($self, $class);
40 0           $self;
41             }
42              
43             # -------------------------------------------------------------------------
44              
45 0     0 0   sub dbg { HTML::WebMake::Main::dbg (@_); }
46 0     0 0   sub dbg2 { HTML::WebMake::Main::dbg2 (@_); }
47              
48             # -------------------------------------------------------------------------
49              
50             sub parse {
51 0     0 0   my ($self, $str) = @_;
52 0           local ($_) = $str;
53              
54 0 0         if (!defined $self->{main}) { carp "no main defined in WmkFile::parse"; }
  0            
55              
56 0 0         if ($self->{parse_for_cgi}) {
57 0           $self->{cgi}->{fulltext} = $_;
58             }
59              
60             # We don't use a proper XML parser, because:
61             # (a) content blocks etc. can contain HTML tags which will not be
62             # scoped correctly;
63             # (b) we use <{perl }> blocks which are invalid XML;
64             # (c) we allow attributes without "quotes".
65             # So kludge it where required. We're probably faster this way
66             # anyway ;)
67              
68             # trim off text before/after chunk
69 0           s/^.*?]*?>//gis;
70 0           s/<\/\s*webmake\s*>.*$//gis;
71              
72             # handle scoped tags. Since we don't use a proper XML parser, we have to
73             # rewrite them here. We convert them to single-character markers (\001 or
74             # \002) indicating a start tag or end tag, then loop until all appearances of
75             # the tag have been converted. We then convert them back to text, with a
76             # scope number attached. Until Perl can do a regexp like this:
77             #
78             # /]*>[^/
79             #
80             # we're probably stuck doing it this way. Hey, don't knock it, it works ;)
81              
82 0           s/\001/<<001>>/gs;
83 0           s/\002/<<002>>/gs;
84 0           $self->{scopings} = { };
85 0           for my $tag (qw(for metadefault attrdefault)) {
86 0 0         if (!/<\/$tag>/) {
87 0           $self->{scopings}->{$tag} = 0; next;
  0            
88             }
89              
90 0           s/<$tag(\b[^>]*[^\/]>)/\001$1/gs;
91 0           s/<\/$tag>/\002/gs;
92              
93 0           my $count = 0;
94 0           while (s{\001([^>]+)>([^\001\002]+)\002}
95             {<$tag$count$1>$2<\/$tag$count>}gis)
96             {
97 0           $count++;
98             }
99 0           $self->{scopings}->{$tag} = $count;
100             }
101 0           s/<<001>>/\001/gs;
102 0           s/<<002>>/\002/gs;
103              
104 0           my $util = $self->{main}->{util};
105 0 0         if (!defined $util) { carp "no util defined in WmkFile::parse"; }
  0            
106              
107 0           $util->set_filename ($self->{filename});
108              
109             # if we are parsing for the CGI scripts, make sure that the XML
110             # parser also notes regular expressions which match each item, so that the
111             # CGI code can rewrite the file easily later.
112 0 0         if ($self->{parse_for_cgi}) {
113 0           $util->{generate_tag_regexps} = 1;
114             }
115              
116 0           my $prevpass;
117 0           my ($lasttag, $lasteval);
118 0           for (my $evalpass = 0; 1; $evalpass++) {
119 0 0 0       last if (defined $prevpass && $_ eq $prevpass);
120 0           $prevpass = $_;
121              
122 0           s/^\s+//gs;
123 0 0         last if ($_ !~ /^
124              
125 0           1 while s/<\{!--.*?--\}>//gs; # WebMake comments.
126 0           1 while s/^//gs; # XML-style comments.
127              
128             # Preprocessing.
129 0           $util->strip_first_lone_tag (\$_, "include",
130             $self, \&tag_include, qw(file));
131 0           $util->strip_first_lone_tag (\$_, "use",
132             $self, \&tag_use, qw(plugin));
133              
134 0 0         if (!$self->{parse_for_cgi}) {
135 0           $self->{main}->eval_code_at_parse (\$_);
136             } else {
137 0           1 while s/^<{.*?}>//gs; # trim code, CGI mode doesn't need it
138             }
139              
140 0           $self->{main}->getusertags()->subst_wmk_tags
141             ($self->{filename}, \$_);
142            
143             {
144             # if we got some eval code, store the text for error messages
145 0           my $text = $self->{main}->{last_perl_code_text};
  0            
146 0 0         if (defined $text) { $lasteval = $text; $lasttag = undef; }
  0            
  0            
147             }
148              
149             # Declarations.
150 0           $util->strip_first_tag_block (\$_, "content",
151             $self, \&tag_content, qw(name));
152 0           $util->strip_first_lone_tag (\$_, "contents",
153             $self, \&tag_contents, qw(src name));
154 0           $util->strip_first_tag_block (\$_, "template",
155             $self, \&tag_template, qw(name));
156 0           $util->strip_first_lone_tag (\$_, "templates",
157             $self, \&tag_templates, qw(src name));
158 0           $util->strip_first_tag_block (\$_, "contenttable",
159             $self, \&tag_contenttable, qw());
160 0           $util->strip_first_lone_tag (\$_, "media",
161             $self, \&tag_media, qw(src name));
162              
163 0 0         if (/^
164 0           $util->strip_first_lone_tag (\$_, "metadefault",
165             $self, \&tag_metadefault, qw(name));
166 0           my $i;
167 0           for ($i = 0; $i < $self->{scopings}->{"metadefault"}; $i++) {
168 0           $util->strip_first_tag_block (\$_, "metadefault".$i,
169             $self, \&tag_metadefault, qw(name));
170             }
171             }
172 0 0         if (/^
173 0           $util->strip_first_lone_tag (\$_, "attrdefault",
174             $self, \&tag_attrdefault, qw(name));
175 0           my $i;
176 0           for ($i = 0; $i < $self->{scopings}->{"attrdefault"}; $i++) {
177 0           $util->strip_first_tag_block (\$_, "attrdefault".$i,
178             $self, \&tag_attrdefault, qw(name));
179             }
180             }
181              
182 0           $util->strip_first_tag (\$_, "metatable",
183             $self, \&tag_metatable, qw());
184 0           $util->strip_first_tag (\$_, "sitemap",
185             $self, \&tag_sitemap, qw(name node leaf));
186 0           $util->strip_first_tag (\$_, "navlinks",
187             $self, \&tag_navlinks,
188             qw(name map up prev next));
189 0           $util->strip_first_lone_tag (\$_, "breadcrumbs",
190             $self, \&tag_breadcrumbs,
191             qw(name map level));
192              
193             # Loops
194 0 0         if (/^
195 0           my $i;
196 0           for ($i = 0; $i < $self->{scopings}->{"for"}; $i++) {
197 0           $util->strip_first_tag_block (\$_, "for".$i,
198             $self, \&tag_for, qw(name values));
199             }
200             }
201              
202             # Outputs.
203 0           $util->strip_first_tag_block (\$_, "out",
204             $self, \&tag_out, qw(file));
205              
206             # Misc.
207 0           $util->strip_first_lone_tag (\$_, "cache",
208             $self, \&tag_cache, qw(dir));
209 0           $util->strip_first_lone_tag (\$_, "option",
210             $self, \&tag_option, qw(name value));
211              
212             # CGIs and hrefs
213 0           $util->strip_first_lone_tag (\$_, "editcgi",
214             $self, \&tag_editcgi, qw(href));
215 0           $util->strip_first_lone_tag (\$_, "viewcgi",
216             $self, \&tag_viewcgi, qw(href));
217 0           $util->strip_first_lone_tag (\$_, "site",
218             $self, \&tag_site, qw(href));
219              
220             # if we got some tags, store the text for error messages
221 0           my $text = $util->{last_tag_text};
222 0 0         if (defined $text) { $lasttag = $text; $lasteval = undef; }
  0            
  0            
223             }
224              
225             # if there's any text left in the file that we couldn't parse,
226             # it's an error, so warn about it.
227             #
228 0 0         if (/\S/) {
229 0           my $failuretext = $lasttag;
230              
231 0 0         if (defined $lasteval) {
232 0 0         if ($_ !~ /^
233             # easy to spot; the Perl code returned '1' or something.
234             # flag it clearly.
235              
236 0           s/\n.*$//gs;
237 0           $self->{main}->fail ("Perl code didn't return valid WebMake code:\n".
238             "\t$lasteval\n\t=> \"$_\"\n");
239 0           return 0;
240             }
241 0           $failuretext = $lasteval;
242             }
243              
244 0           /^(.*?>.{40,40})/s; $_ = $1; $_ =~ s/\s+/ /gs;
  0            
  0            
245 0   0       $lasttag ||= '';
246 0           $self->{main}->fail ("WMK file contains unparseable data at or after:\n".
247             "\t$lasttag\n\t$_ ...\"\n");
248 0           return 0;
249             }
250              
251 0           return 1;
252             }
253              
254             # -------------------------------------------------------------------------
255              
256             sub subst_attrs {
257 0     0 0   my ($self, $tagname, $attrs) = @_;
258 0 0         return if ($self->{parse_for_cgi});
259              
260 0 0         if (defined ($attrs->{name})) {
261 0           $tagname .= " \"".$attrs->{name}."\""; # for errors
262             }
263              
264 0           my ($k, $v);
265 0           while (($k, $v) = each %{$attrs}) {
  0            
266 0 0 0       next unless (defined $k && defined $v);
267 0           $attrs->{$k} = $self->{main}->fileless_subst ($tagname, $v);
268             }
269             }
270              
271             # -------------------------------------------------------------------------
272              
273             sub tag_include {
274 0     0 0   my ($self, $tag, $attrs, $text) = @_;
275              
276 0 0         $self->cgi_add ($tag, $CGI_EDIT_AS_WMKFILE, $attrs->{file}, $attrs) and return '';
277 0           $self->subst_attrs ("", $attrs);
278              
279 0           my $file = $attrs->{file};
280              
281 0 0         if (!open (INC, "< $file")) {
282 0           die "Cannot open include file: $file\n";
283             }
284 0           my @s = stat INC;
285 0           my $inc = join ('', );
286 0           close INC;
287              
288 0           dbg ("included file: \"$file\"");
289 0           $self->{main}->set_file_modtime ($file, $s[9]);
290 0           $self->add_dep ($file);
291 0           $inc;
292             }
293              
294             # -------------------------------------------------------------------------
295              
296             sub tag_use {
297 0     0 0   my ($self, $tag, $attrs, $text) = @_;
298              
299 0           $self->subst_attrs ("", $attrs);
300              
301 0           my $plugin = $attrs->{plugin};
302 0           my $file;
303             my @s;
304              
305 0           $file = '~/.webmake/plugins/'.$plugin.'.wmk';
306 0           $file = $self->{main}->sed_fname ($file);
307 0           @s = stat $file;
308              
309 0 0         if (!defined $s[9]) {
310 0           $file = '%l/'.$plugin.'.wmk';
311 0           $file = $self->{main}->sed_fname ($file);
312 0           @s = stat $file;
313             }
314              
315 0 0         if (!defined $s[9]) {
316 0           die "Cannot open 'use' plugin: $plugin\n";
317             }
318              
319             foundit:
320              
321 0 0         if (!open (INC, "<$file")) {
322 0           die "Cannot open 'use' file: $file\n";
323             }
324 0           my $inc = join ('', );
325 0           close INC;
326              
327 0           dbg ("used file: \"$file\"");
328 0           $self->{main}->set_file_modtime ($file, $s[9]);
329 0           $self->add_dep ($file);
330 0           $inc;
331             }
332              
333             # -------------------------------------------------------------------------
334              
335             sub tag_cache {
336 0     0 0   my ($self, $tag, $attrs, $text) = @_;
337              
338 0           $self->subst_attrs ("", $attrs);
339 0           my $dir = $attrs->{dir};
340 0           $self->{main}->setcachefile ($dir);
341 0           "";
342             }
343              
344             # -------------------------------------------------------------------------
345              
346             sub tag_option {
347 0     0 0   my ($self, $tag, $attrs, $text) = @_;
348              
349 0           $self->subst_attrs ("
350 0           $self->{main}->set_option ($attrs->{name}, $attrs->{value});
351 0           "";
352             }
353              
354             # -------------------------------------------------------------------------
355              
356             sub tag_editcgi {
357 0     0 0   my ($self, $tag, $attrs, $text) = @_;
358              
359 0           $self->subst_attrs ("", $attrs);
360 0           $self->{main}->add_url ("WebMake.EditCGI", $attrs->{href});
361 0           "";
362             }
363              
364             # -------------------------------------------------------------------------
365              
366             sub tag_viewcgi {
367 0     0 0   my ($self, $tag, $attrs, $text) = @_;
368              
369 0           $self->subst_attrs ("", $attrs);
370 0           $self->{main}->add_url ("WebMake.ViewCGI", $attrs->{href});
371 0           "";
372             }
373              
374             # -------------------------------------------------------------------------
375              
376             sub tag_site {
377 0     0 0   my ($self, $tag, $attrs, $text) = @_;
378              
379 0           $self->subst_attrs ("", $attrs);
380 0           $self->{main}->add_url ("WebMake.SiteHref", $attrs->{href});
381 0           "";
382             }
383              
384             # -------------------------------------------------------------------------
385              
386             sub tag_content {
387 0     0 0   my ($self, $tag, $attrs, $text) = @_;
388              
389 0 0         $self->cgi_add ($tag, $CGI_EDIT_AS_TEXT, $text, $attrs) and return '';
390 0           $self->subst_attrs ("", $attrs);
391 0           my $name = $attrs->{name};
392 0 0         if (!defined $name) {
393 0           carp ("Unnamed content found in ".$self->{filename}.": $text\n");
394 0           return;
395             }
396              
397 0 0         if (defined $attrs->{root}) {
398 0           warn "warning: \${$name}: 'root' attribute is deprecated, ".
399             "use 'isroot' instead\n";
400 0           $attrs->{isroot} = $attrs->{root}; # backwards compat
401             }
402              
403 0           $self->{main}->add_content ($name, $self, $attrs, $text);
404 0           "";
405             }
406              
407             sub tag_contents {
408 0     0 0   my ($self, $tag, $attrs, $text) = @_;
409              
410 0 0         $self->cgi_add_datasource ($tag, $attrs) and return '';
411 0           $self->subst_attrs ("", $attrs);
412 0           my $lister = new HTML::WebMake::Contents ($self->{main},
413             $attrs->{src}, $attrs->{name}, $attrs);
414 0           $lister->add();
415 0           "";
416             }
417              
418             sub tag_template {
419 0     0 0   my ($self, $tag, $attrs, $text) = @_;
420              
421 0 0         $self->cgi_add ($tag, $CGI_EDIT_AS_TEXT, $text, $attrs) and return '';
422 0           $self->subst_attrs ("