File Coverage

blib/lib/SGML/DTDParse/DTD.pm
Criterion Covered Total %
statement 27 869 3.1
branch 0 336 0.0
condition 0 59 0.0
subroutine 9 70 12.8
pod 0 27 0.0
total 36 1361 2.6


line stmt bran cond sub pod time code
1             # -*- Perl -*-
2              
3             package SGML::DTDParse::DTD;
4              
5 1     1   26553 use strict;
  1         2  
  1         39  
6 1     1   5 use vars qw($VERSION $CVS);
  1         3  
  1         5676  
7              
8             $VERSION = do { my @r=(q$Revision: 2.2 $=~/\d+/g); sprintf "%d."."%03d"x$#r,@r };
9             $CVS = '$Id: DTD.pm,v 2.2 2005/07/16 03:21:35 ehood Exp $ ';
10              
11 1     1   1272 use Text::DelimMatch;
  1         7432  
  1         57  
12 1     1   965 use SGML::DTDParse;
  1         3  
  1         40  
13 1     1   605 use SGML::DTDParse::Catalog;
  1         3  
  1         50  
14 1     1   893 use SGML::DTDParse::Tokenizer;
  1         4  
  1         34  
15 1     1   601 use SGML::DTDParse::ContentModel;
  1         3  
  1         37  
16 1     1   711 use SGML::DTDParse::Util qw(entify);
  1         3  
  1         3971  
17              
18             my $DTDVERSION = "1.0";
19             my $DTDPUBID = "-//Norman Walsh//DTD DTDParse V2.0//EN";
20             my $DTDSYSID = "dtd.dtd";
21             my $debug = 0;
22              
23             {
24             package SGML::DTDParse::DTD::ENTITY;
25              
26             sub new {
27 0     0     my($type, $dtd, $entity, $etype, $pub, $sys, $text) = @_;
28 0   0       my $class = ref($type) || $type;
29 0           my $self = {};
30              
31 0           $text = $dtd->fix_entityrefs($text);
32              
33 0 0 0       if ($dtd->{'XML'} && ($pub && !$sys)) {
      0        
34 0           $dtd->status("External entity declaration without system "
35             . "identifer found in XML DTD. "
36             . "This isn't an XML DTD.", 1);
37 0           $dtd->{'XML'} = 0;
38             }
39              
40 0           $self->{'DTD'} = $dtd;
41 0           $self->{'NAME'} = $entity;
42 0           $self->{'TYPE'} = $etype;
43 0           $self->{'NOTATION'} = "";
44 0           $self->{'PUBLIC'} = $pub;
45 0           $self->{'SYSTEM'} = $sys;
46 0           $self->{'TEXT'} = $text;
47              
48 0 0         if ($etype =~ /^ndata (\S+)$/i) {
49 0           $self->{'TYPE'} = 'ndata';
50 0           $self->{'NOTATION'} = $1;
51             }
52              
53 0 0         if ($etype =~ /^cdata (\S+)$/i) {
54 0           $self->{'TYPE'} = 'cdata';
55 0           $self->{'NOTATION'} = $1;
56             }
57              
58 0           bless $self, $class;
59             }
60              
61             sub name {
62 0     0     my $self = shift;
63 0           my $value = shift;
64 0 0         $self->{'NAME'} = $value if defined($value);
65 0           return $self->{'NAME'};
66             }
67              
68             sub type {
69 0     0     my $self = shift;
70 0           my $value = shift;
71 0 0         $self->{'TYPE'} = $value if defined($value);
72 0           return $self->{'TYPE'};
73             }
74              
75             sub notation {
76 0     0     my $self = shift;
77 0           my $value = shift;
78 0 0         $self->{'NOTATION'} = $value if defined($value);
79 0           return $self->{'NOTATION'};
80             }
81              
82             sub public {
83 0     0     my $self = shift;
84 0           my $value = shift;
85 0 0         $self->{'PUBLIC'} = $value if defined($value);
86 0           return $self->{'PUBLIC'};
87             }
88              
89             sub system {
90 0     0     my $self = shift;
91 0           my $value = shift;
92 0 0         $self->{'SYSTEM'} = $value if defined($value);
93 0           return $self->{'SYSTEM'};
94             }
95              
96             sub text {
97 0     0     my $self = shift;
98 0           my $value = shift;
99 0 0         $self->{'TEXT'} = $value if defined($value);
100 0           return $self->{'TEXT'};
101             }
102              
103             sub xml {
104 0     0     my $self = shift;
105 0           my $xml = "";
106              
107 0           $xml .= "name() . "\"\n";
108 0           $xml .= " type=\"" . $self->type() . "\"\n";
109 0 0         $xml .= " notation=\"" . $self->notation() . "\"\n"
110             if $self->notation();
111              
112 0 0 0       if ($self->public() || $self->system()) {
113 0 0         $xml .= " public=\"" . $self->public() . "\"\n"
114             if $self->public();
115 0 0         $xml .= " system=\"" . $self->system() . "\"\n"
116             if $self->system();
117 0           $xml .= "/>\n";
118             } else {
119 0           my $text = $self->{'DTD'}->expand_entities($self->text());
120 0           $text =~ s/\&/\&/sg;
121              
122 0           $xml .= ">\n";
123 0           $xml .= "$text\n";
124              
125 0 0         if ($self->{'DTD'}->{'UNEXPANDED_CONTENT'}) {
126 0           $text = $self->text();
127 0           $text =~ s/\&/\&/sg;
128 0           $xml .= "$text\n";
129             }
130              
131 0           $xml .= "\n";
132             }
133              
134 0           return $xml;
135             }
136             }
137              
138             {
139             package SGML::DTDParse::DTD::ELEMENT;
140              
141             sub new {
142 0     0     my($type, $dtd, $element, $stagm, $etagm, $cm, $incl, $excl) = @_;
143 0   0       my $class = ref($type) || $type;
144 0           my $self = {};
145              
146 0           $cm = $dtd->fix_entityrefs($cm);
147 0           $incl = $dtd->fix_entityrefs($incl);
148 0           $excl = $dtd->fix_entityrefs($excl);
149              
150 0 0 0       if ($dtd->{'XML'} && ($cm eq 'CDATA')) {
151 0           $dtd->status("CDATA declared element content found in XML DTD. "
152             . "This isn't an XML DTD.", 1);
153 0           $dtd->{'XML'} = 0;
154             }
155              
156 0 0 0       if ($dtd->{'XML'} && ($stagm || $etagm)) {
      0        
157 0           $dtd->status("Tag minimization found in XML DTD. "
158             . "This isn't an XML DTD.", 1);
159 0           $dtd->{'XML'} = 0;
160             }
161              
162 0           $self->{'DTD'} = $dtd;
163 0           $self->{'NAME'} = $element;
164 0           $self->{'STAGM'} = $stagm;
165 0           $self->{'ETAGM'} = $etagm;
166 0           $self->{'CONMDL'} = $cm;
167 0           $self->{'INCL'} = $incl;
168 0           $self->{'EXCL'} = $excl;
169              
170 0           bless $self, $class;
171             }
172              
173             sub name {
174 0     0     my $self = shift;
175 0           my $value = shift;
176 0 0         $self->{'NAME'} = $value if defined($value);
177 0           return $self->{'NAME'};
178             }
179              
180             sub type {
181 0     0     return "element";
182             }
183              
184             sub starttag_min {
185 0     0     my $self = shift;
186 0           my $value = shift;
187 0 0         $self->{'STAGM'} = $value if defined($value);
188 0           return $self->{'STAGM'};
189             }
190              
191             sub endtag_min {
192 0     0     my $self = shift;
193 0           my $value = shift;
194 0 0         $self->{'ETAGM'} = $value if defined($value);
195 0           return $self->{'ETAGM'};
196             }
197              
198             sub content_model {
199 0     0     my $self = shift;
200 0           my $value = shift;
201 0 0         $self->{'CONMDL'} = $value if defined($value);
202 0           return $self->{'CONMDL'};
203             }
204              
205             sub inclusions {
206 0     0     my $self = shift;
207 0           my $value = shift;
208 0 0         $self->{'INCL'} = $value if defined($value);
209 0           return $self->{'INCL'};
210             }
211              
212             sub exclusions {
213 0     0     my $self = shift;
214 0           my $value = shift;
215 0 0         $self->{'EXCL'} = $value if defined($value);
216 0           return $self->{'EXCL'};
217             }
218              
219             sub xml_content_model {
220 0     0     my $self = shift;
221 0           my $wrapper = shift;
222 0           my $model = shift;
223 0           my $expand = shift;
224 0           my $xml = "";
225 0           my ($text, $cmtok, $cm);
226              
227             # $text = $model;
228             # $text =~ s/\%/\&/sg;
229             # $xml = "<$wrapper text=\"$text\">\n";
230 0           $xml = "<$wrapper>\n";
231              
232 0 0         $text = $expand ? $self->{'DTD'}->expand_entities($model) : $model;
233 0           $cmtok = new SGML::DTDParse::Tokenizer $text;
234 0           $cm = new SGML::DTDParse::ContentModel $cmtok;
235              
236 0           $xml .= $cm->xml();
237              
238 0           $xml .= "\n";
239              
240 0           return $xml;
241             }
242              
243             sub xml {
244 0     0     my $self = shift;
245 0           my $xml = "";
246 0           my($text, $cmtok, $cm, $type);
247              
248 0           $text = $self->content_model();
249 0           $text = $self->{'DTD'}->expand_entities($text);
250 0           $cmtok = new SGML::DTDParse::Tokenizer $text;
251 0           $cm = new SGML::DTDParse::ContentModel $cmtok;
252              
253 0           $type = $cm->type();
254              
255 0           $xml .= "name() . "\"";
256 0 0         $xml .= " stagm=\"" . $self->starttag_min() . "\""
257             if $self->starttag_min();
258 0 0         $xml .= " etagm=\"" . $self->endtag_min() . "\""
259             if $self->endtag_min();
260 0           $xml .= "\n";
261 0           $xml .= " content-type=\"$type\"";
262 0           $xml .= ">\n";
263              
264 0           $xml .= $self->xml_content_model('content-model-expanded',
265             $self->content_model(), 1);
266              
267 0 0         if ($self->{'DTD'}->{'UNEXPANDED_CONTENT'}) {
268 0           $xml .= $self->xml_content_model('content-model',
269             $self->content_model(), 0);
270             }
271              
272 0 0         if ($self->inclusions()) {
273 0           $xml .= $self->xml_content_model('inclusions',
274             $self->inclusions(), 1);
275             }
276              
277 0 0         if ($self->exclusions()) {
278 0           $xml .= $self->xml_content_model('exclusions',
279             $self->exclusions(), 1);
280             }
281              
282 0           $xml .= "\n";
283              
284 0           return $xml;
285             }
286             }
287              
288             {
289             package SGML::DTDParse::DTD::ATTLIST;
290              
291             sub new {
292 0     0     my $type = shift;
293 0           my $dtd = shift;
294 0           my $attlist = shift;
295 0           my $attdecl = shift;
296 0           my(@attrs) = @_;
297 0   0       my $class = ref($type) || $type;
298 0           my $self = {};
299              
300 0           $self->{'DTD'} = $dtd;
301 0           $self->{'NAME'} = $attlist;
302 0           $self->{'TYPE'} = {};
303 0           $self->{'VALS'} = {};
304 0           $self->{'DEFV'} = {};
305 0           $self->{'DECL'} = $attdecl;
306              
307 0           while (@attrs) {
308 0           my $name = shift @attrs;
309 0           my $values = shift @attrs;
310 0           my $attrtype = shift @attrs;
311 0           my $defval = shift @attrs;
312              
313 0           $self->{'TYPE'}->{$name} = $attrtype;
314 0           $self->{'VALS'}->{$name} = $values;
315 0           $self->{'DEFV'}->{$name} = $defval;
316             }
317              
318 0           bless $self, $class;
319             }
320              
321             sub append {
322 0     0     my $self = shift;
323 0           my $dtd = shift;
324 0           my $attlist = shift;
325 0           my $attdecl = shift;
326 0           my(@attrs) = @_;
327              
328 0           while (@attrs) {
329 0           my $name = shift @attrs;
330 0           my $values = shift @attrs;
331 0           my $attrtype = shift @attrs;
332 0           my $defval = shift @attrs;
333              
334 0           $self->{'TYPE'}->{$name} = $attrtype;
335 0           $self->{'VALS'}->{$name} = $values;
336 0           $self->{'DEFV'}->{$name} = $defval;
337             }
338             }
339              
340             sub name {
341 0     0     my $self = shift;
342 0           my $value = shift;
343 0 0         $self->{'NAME'} = $value if defined($value);
344 0           return $self->{'NAME'};
345             }
346              
347             sub type {
348 0     0     return "attlist";
349             }
350              
351             sub text {
352 0     0     my $self = shift;
353 0           return $self->{'DECL'};
354             }
355              
356             sub attribute_list {
357 0     0     my $self = shift;
358 0           my(@attr) = keys %{$self->{'TYPE'}};
  0            
359 0           return @attr;
360             }
361              
362             sub attribute_type {
363 0     0     my $self = shift;
364 0           my $attr = shift;
365 0           my $value = shift;
366 0 0         $self->{'TYPE'}->{$attr} = $value if defined($value);
367 0           return $self->{'TYPE'}->{$attr};
368             }
369              
370             sub attribute_values {
371 0     0     my $self = shift;
372 0           my $attr = shift;
373 0           my $value = shift;
374 0 0         $self->{'VALS'}->{$attr} = $value if defined($value);
375 0           return $self->{'VALS'}->{$attr};
376             }
377              
378             sub attribute_default {
379 0     0     my $self = shift;
380 0           my $attr = shift;
381 0           my $value = shift;
382 0 0         $self->{'DEFV'}->{$attr} = $value if defined($value);
383 0           return $self->{'DEFV'}->{$attr};
384             }
385              
386             sub xml {
387 0     0     my $self = shift;
388 0           my $xml = "";
389 0           my(@attr) = $self->attribute_list();
390 0           my($attr, $text);
391              
392 0           $xml .= "name() . "\">\n";
393              
394 0           my $cdata = $self->{'DECL'};
395 0           $cdata =~ s/&/&/sg;
396 0           $cdata =~ s/
397              
398 0           $xml .= "$cdata\n";
399              
400 0           foreach $attr (@attr) {
401 0           $xml .= "
402              
403 0           $text = $self->attribute_type($attr);
404             # $text =~ s/\%/\&/sg;
405 0           $xml .= " type=\"$text\"\n";
406              
407 0           $text = $self->attribute_values($attr);
408             # $text =~ s/\%/\&/sg;
409              
410 0           my $enumtype = undef;
411 0 0         if ($text =~ /^NOTATION \(/) {
412 0           $enumtype = "notation";
413 0           $text = "(" . $'; # '
414             }
415              
416 0 0         if ($text =~ /^\(/) {
417 0 0         $enumtype = "yes" if !defined($enumtype);
418 0           $xml .= " enumeration=\"$enumtype\"\n";
419 0           $text =~ s/[\(\)\|]/ /g;
420 0           $text =~ s/\s+/ /g;
421 0           $text =~ s/^\s*//;
422 0           $text =~ s/\s*$//;
423             }
424              
425 0           $xml .= " value=\"$text\"\n";
426              
427 0           $text = $self->attribute_default($attr);
428             # $text =~ s/\%/\&/sg;
429 0           $xml .= " default=\"$text\"/>\n";
430             }
431              
432 0           $xml .= "\n";
433              
434 0           return $xml;
435             }
436             }
437              
438             {
439             package SGML::DTDParse::DTD::NOTATION;
440              
441             sub new {
442 0     0     my($type, $dtd, $notation, $pub, $sys, $text) = @_;
443 0   0       my $class = ref($type) || $type;
444 0           my $self = {};
445              
446 0           $self->{'DTD'} = $dtd;
447 0           $self->{'NAME'} = $notation;
448 0           $self->{'PUBLIC'} = $pub;
449 0           $self->{'SYSTEM'} = $sys;
450              
451 0           bless $self, $class;
452             }
453              
454             sub name {
455 0     0     my $self = shift;
456 0           my $value = shift;
457 0 0         $self->{'NAME'} = $value if defined($value);
458 0           return $self->{'NAME'};
459             }
460              
461             sub type {
462 0     0     return "notation";
463             }
464              
465             sub public {
466 0     0     my $self = shift;
467 0           my $value = shift;
468 0 0         $self->{'PUBLIC'} = $value if defined($value);
469 0           return $self->{'PUBLIC'};
470             }
471              
472             sub system {
473 0     0     my $self = shift;
474 0           my $value = shift;
475 0 0         $self->{'SYSTEM'} = $value if defined($value);
476 0           return $self->{'SYSTEM'};
477             }
478              
479             sub xml {
480 0     0     my $self = shift;
481 0           my $xml = "";
482              
483 0           $xml .= "name() . "\"\n";
484              
485 0 0         $xml .= " public=\"" . $self->public() . "\"\n"
486             if $self->public();
487              
488 0 0 0       if (!$self->public() || $self->system()) {
489 0           $xml .= " system=\"" . $self->system() . "\"\n";
490             }
491              
492 0           $xml .= "/>\n";
493              
494 0           return $xml;
495             }
496             }
497              
498             sub new {
499 0     0 0   my $type = shift;
500 0           my %param = @_;
501 0   0       my $class = ref($type) || $type;
502 0           my $self = bless {}, $class;
503 0           my $cat = new SGML::DTDParse::Catalog (%param);
504              
505 0           $self->{'LASTMSGLEN'} = 0;
506 0           $self->{'NEWLINE'} = 0;
507 0           $self->{'CAT'} = $cat;
508 0           $self->{'PENT'} = {};
509 0           $self->{'DECLS'} = [];
510 0           $self->{'DECLS'}->[0] = 0;
511 0           $self->{'PENTDECL'} = [];
512 0           $self->{'PENTDECL'}->[0] = 0;
513 0           $self->{'GENT'} = {};
514 0           $self->{'GENTDECL'} = [];
515 0           $self->{'GENTDECL'}->[0] = 0;
516 0           $self->{'ELEM'} = {};
517 0           $self->{'ATTR'} = {};
518 0           $self->{'NOTN'} = {};
519 0   0       $self->{'VERBOSE'} = $param{'Verbose'} || $param{'Debug'};
520 0           $self->debug($param{'Debug'});
521 0           $self->{'TITLE'} = $param{'Title'};
522 0 0         $self->{'UNEXPANDED_CONTENT'}
523             = $param{'UnexpandedContent'} ? 1 : 0;
524 0           $self->{'SOURCE_DTD'} = $param{'SourceDtd'};
525 0           $self->{'PUBLIC_ID'} = $param{'PublicId'};
526 0           $self->{'SYSTEM_ID'} = $param{'SystemId'};
527 0           $self->{'DECLARATION'} = $param{'Declaration'};
528 0           $self->{'XML'} = $param{'Xml'};
529 0           $self->{'NAMECASE_GEN'} = $param{'NamecaseGeneral'};
530 0           $self->{'NAMECASE_ENT'} = $param{'NamecaseEntity'};
531              
532             # There's a deficiency in the way this code is written. The entity
533             # boundaries are lost as entities are loaded, so there's no way to
534             # keep track of the correct "current directory" for resolving
535             # relative system identifiers. To work around this problem, the list
536             # of all directories accessed is kept in a path, and that path is
537             # searched for relative system identifiers. This could produce the
538             # wrong results, but it doesn't seem very likely. A proper solution
539             # may be implemented in the future.
540 0           $self->{'SEARCHPATH'} = ();
541              
542 0           delete($self->{'DTD'}); # This isn't supposed to exist yet.
543              
544 0           return $self;
545             }
546              
547             sub parse {
548 0     0 0   my $self = shift;
549 0           my $dtd = shift;
550 0           my $dtd_fh = \*STDIN;
551 0           local $_;
552              
553 0 0         die "Error: Already parsed " . $self->{'DTD'} . "\n" if $self->{'DTD'};
554              
555 0 0         if (!$dtd) {
556 0 0         if ($self->{'SYSTEM_ID'}) {
    0          
557 0           $dtd = $self->{'CAT'}->system_map($self->{'SYSTEM_ID'});
558             } elsif ($self->{'PUBLIC_ID'}) {
559 0           $dtd = $self->{'CAT'}->public_map($self->{'PUBLIC_ID'});
560             }
561             }
562              
563 0 0         if (!$dtd) {
564 0           $self->status('Reading DTD from stdin...', 1);
565 0           $self->{'DTD'} = '0';
566             } else {
567 0           $self->{'DTD'} = $dtd;
568             }
569 0 0         if (!$self->{'SYSTEM_ID'}) {
570 0           $self->{'SYSTEM_ID'} = $self->{'DTD'};
571             }
572              
573 0           my $decl = $self->{'DECLARATION'};
574              
575 0 0         if (!$decl) {
576 0 0         if ($self->{'PUBLIC_ID'}) {
577 0           $decl = $self->{'CAT'}->declaration($self->{'PUBLIC_ID'});
578             } else {
579 0           my $pubid = $self->{'CAT'}->reverse_public_map($dtd);
580 0           $decl = $self->{'CAT'}->declaration($pubid);
581             }
582             }
583              
584 0 0         if ($self->{'PUBLIC_ID'}) {
585 0           $self->status('Public ID: ' . $self->{'PUBLIC_ID'}, 1);
586             } else {
587 0           $self->status('Public ID: unknown', 1);
588             }
589              
590 0           $self->status('System ID: ' . $self->{'SYSTEM_ID'}, 1);
591              
592 0 0         if ($decl) {
593 0           $self->{'DECLARATION'} = $decl;
594 0           $self->status("SGML declaration: $decl", 1);
595 0           my($xml, $namecase, $entitycase) = $self->parse_decl($decl);
596 0           $self->{'XML'} = $xml;
597 0           $self->{'NAMECASE_GEN'} = $namecase;
598 0           $self->{'NAMECASE_ENT'} = $entitycase;
599             } else {
600 0           $self->status("SGML declaration: unknown, using defaults for xml and namecase", 1);
601             }
602              
603 0 0         if ($dtd) {
604 1     1   1133 use Symbol;
  1         1505  
  1         24232  
605 0           $dtd_fh = gensym;
606 0 0         open($dtd_fh, $dtd) || die qq{Error: Unable to open "$dtd": $!\n};
607             }
608             {
609             # slurp up entire file
610 0           local $/;
  0            
611 0           $_ = <$dtd_fh>;
612             }
613 0 0         close ($dtd_fh) if $dtd;
614              
615 0   0       $self->add_to_searchpath($dtd || '.');
616              
617 0           my ($tok, $rest) = $self->next_token($_);
618 0           while ($tok) {
619 0 0         if ($tok =~ /
    0          
    0          
    0          
    0          
620 0           $rest = $self->parse_entity($rest);
621             } elsif ($tok =~ /
622 0           $rest = $self->parse_element($rest);
623             } elsif ($tok =~ /
624 0           $rest = $self->parse_attlist($rest);
625             } elsif ($tok =~ /
626 0           $rest = $self->parse_notation($rest);
627             } elsif ($tok =~ /
628 0           $rest = $self->parse_markedsection($rest);
629             } else {
630 0           die "Error: Unexpected declaration: $tok\n";
631             }
632              
633 0           ($tok, $rest) = $self->next_token($rest);
634             }
635              
636 0           $self->status("Parse complete.\n");
637              
638 0           return $self;
639             }
640              
641             sub parseCatalog {
642 0     0 0   my $self = shift;
643 0           my $catalog = shift;
644              
645 0           $self->{'CAT'}->parse($catalog);
646             }
647              
648             sub verbose {
649 0     0 0   my $self = shift;
650 0           my $val = shift;
651 0           my $verb = $self->{'VERBOSE'};
652              
653 0 0         $self->{'VERBOSE'} = $val if defined($val);
654              
655 0           return $verb;
656             }
657              
658             sub debug {
659 0     0 0   my $self = shift;
660 0           my $val = shift;
661 0           my $dbg = $debug;
662            
663 0 0         if (defined($val)) {
664 0           $debug = $val;
665 0 0         if (ref($self)) {
666 0           $self->{'DEBUG'} = $debug;
667             }
668             }
669 0           return $dbg;
670             }
671              
672             # ======================================================================
673              
674             sub add_entity {
675 0     0 0   my($self, $name, $type, $public, $system, $text) = @_;
676 0           my $entity = new SGML::DTDParse::DTD::ENTITY $self, $name, $type, $public, $system, $text;
677 0           my $count;
678              
679 0 0         if ($type eq 'param') {
680 0 0         return if exists($self->{'PENT'}->{$name});
681 0           $count = $self->{'PENTDECL'}->[0] + 1;
682 0           $self->{'PENT'}->{$name} = $count;
683 0           $self->{'PENTDECL'}->[0] = $count;
684 0           $self->{'PENTDECL'}->[$count] = $entity;
685              
686 0           $count = $self->{'DECLS'}->[0] + 1;
687 0           $self->{'DECLS'}->[0] = $count;
688 0           $self->{'DECLS'}->[$count] = $entity;
689             } else {
690 0 0         return if exists($self->{'GENT'}->{$name});
691 0           $count = $self->{'GENTDECL'}->[0] + 1;
692 0           $self->{'GENT'}->{$name} = $count;
693 0           $self->{'GENTDECL'}->[0] = $count;
694 0           $self->{'GENTDECL'}->[$count] = $entity;
695              
696 0           $count = $self->{'DECLS'}->[0] + 1;
697 0           $self->{'DECLS'}->[0] = $count;
698 0           $self->{'DECLS'}->[$count] = $entity;
699             }
700             }
701              
702             sub pent {
703 0     0 0   my $self = shift;
704 0           my $name = shift;
705 0           my $count = $self->{'PENT'}->{$name};
706              
707 0 0         return undef if !$count;
708              
709 0           return $self->{'PENTDECL'}->[$count];
710             }
711              
712             sub gent {
713 0     0 0   my $self = shift;
714 0           my $name = shift;
715 0           my $count = $self->{'GENT'}->{$name};
716              
717 0 0         return undef if !$count;
718              
719 0           return $self->{'GENTDECL'}->[$count];
720             }
721              
722             sub declaration_count {
723 0     0 0   my $self = shift;
724 0           return $self->{'DECLS'}->[0];
725             }
726              
727             sub declarations {
728 0     0 0   my $self = shift;
729 0           my @decls = @{$self->{'DECLS'}};
  0            
730 0           shift @decls;
731 0           return @decls;
732             }
733              
734             # ======================================================================
735              
736             sub xml_elements {
737 0     0 0   my $self = shift;
738 0           my $fh = shift;
739 0           my %output = ();
740              
741 0           foreach $_ (keys %{$self->{'NOTN'}}) {
  0            
742 0           print $fh $self->{'NOTN'}->{$_}->xml(), "\n";
743             }
744              
745 0           foreach $_ (keys %{$self->{'PENT'}}) {
  0            
746 0           print $fh $self->pent($_)->xml(), "\n";
747             }
748              
749 0           foreach $_ (keys %{$self->{'GENT'}}) {
  0            
750 0           print $fh $self->gent($_)->xml(), "\n";
751             }
752              
753 0           foreach $_ (keys %{$self->{'ELEM'}}) {
  0            
754 0           print $fh $self->{'ELEM'}->{$_}->xml(), "\n";
755 0 0         print $fh $self->{'ATTR'}->{$_}->xml(), "\n"
756             if exists ($self->{'ATTR'}->{$_});
757 0           $output{$_} = 1;
758             }
759              
760 0           foreach $_ (keys %{$self->{'ATTR'}}) {
  0            
761 0 0         print $fh $self->{'ATTR'}->{$_}->xml(), "\n" if !$output{$_};
762             }
763             }
764              
765             sub xml {
766 0     0 0   my $self = shift;
767 0           my $fh = shift;
768 0           my $count;
769              
770 0           print $fh "
771 0           print $fh " \"$DTDSYSID\" [\n";
772              
773             # for ($count = 1; $count <= $self->{'PENTDECL'}->[0]; $count++) {
774             # my($pent) = $self->{'PENTDECL'}->[$count];
775             # next if $pent->system() || $pent->public();
776             # print $fh "name(), " \"%", $pent->name(), ";\">\n";
777             # }
778              
779 0           for ($count = 1; $count <= $self->{'GENTDECL'}->[0]; $count++) {
780 0           my $gent = $self->{'GENTDECL'}->[$count];
781              
782 0 0         if ($gent->type() ne 'sdata') {
    0          
783 0           my $name = $gent->name();
784 0           my $text = $gent->text();
785              
786 0 0         $text = "&#38;" if $text eq '&';
787 0 0         $text = "&#60;" if $text eq '<';
788              
789 0           print $fh "\n";
790             } elsif ($gent->type() ne 'pi') {
791 0           my $name = $gent->name();
792 0           my $text = $gent->text();
793              
794 0 0         $text = "&#38;" if $text eq '&';
795 0 0         $text = "&#60;" if $text eq '<';
796              
797 0           print $fh "\n";
798             }
799             }
800              
801 0           print $fh "]>\n";
802 0           print $fh "
803 0           print $fh " unexpanded='", $self->{'UNEXPANDED_CONTENT'}, "'\n";
804 0           print $fh " title=\"", entify($self->{'TITLE'}), "\"\n";
805 0           print $fh " namecase-general=\"", $self->{'NAMECASE_GEN'}, "\"\n";
806 0           print $fh " namecase-entity=\"", $self->{'NAMECASE_ENT'}, "\"\n";
807 0           print $fh " xml=\"", $self->{'XML'}, "\"\n";
808 0           print $fh " system-id=\"", entify($self->{'SYSTEM_ID'}), "\"\n";
809 0           print $fh " public-id=\"", entify($self->{'PUBLIC_ID'}), "\"\n";
810 0           print $fh " declaration=\"", $self->{'DECLARATION'}, "\"\n";
811 0           print $fh " created-by=\"DTDParse V$SGML::DTDParse::VERSION\"\n";
812 0           print $fh " created-on=\"", scalar(localtime()), "\"\n";
813 0           print $fh ">\n";
814              
815 0           $self->xml_elements($fh);
816 0           print $fh "\n";
817             }
818              
819             # ======================================================================
820              
821             sub parse_entity {
822 0     0 0   my $self = shift;
823 0           my $dtd = shift;
824 0           my($type, $name) = ('gen', undef);
825 0           my($public, $system, $text) = ("", "", "");
826 0           my($tok);
827              
828 0           ($tok, $dtd) = $self->next_token($dtd);
829              
830 0 0         if ($tok eq '%') {
831 0           $type = 'param';
832 0           ($tok, $dtd) = $self->next_token($dtd);
833             }
834              
835 0           $name = $tok;
836              
837 0           $tok = $self->peek_token($dtd);
838              
839 0 0         if ($tok =~ /^[\"\']/) {
840             # we're looking at text...
841 0           ($text, $dtd) = $self->next_token($dtd);
842 0           $text = $self->trim_quotes($text);
843             } else {
844 0           ($tok, $dtd) = $self->next_token($dtd);
845              
846 0 0         if ($tok =~ /public/i) {
    0          
    0          
    0          
    0          
847 0           ($public, $dtd) = $self->next_token($dtd);
848 0           $public = $self->trim_quotes($public);
849 0           $tok = $self->peek_token($dtd);
850 0 0         if ($tok ne '>') {
851 0           ($system, $dtd) = $self->next_token($dtd);
852 0           $system = $self->trim_quotes($system);
853             }
854             } elsif ($tok =~ /system/i) {
855 0           ($system, $dtd) = $self->next_token($dtd);
856 0           $system = $self->trim_quotes($system);
857             } elsif ($tok =~ /^sdata$/i) {
858 0           $type = 'sdata';
859 0           ($text, $dtd) = $self->next_token($dtd);
860 0           $text = $self->trim_quotes($text);
861             } elsif ($tok =~ /^pi$/i) {
862 0           $type = 'pi';
863 0           ($text, $dtd) = $self->next_token($dtd);
864 0           $text = $self->trim_quotes($text);
865             } elsif ($tok =~ /^cdata$/i) {
866 0           $type = 'cdata';
867 0           ($text, $dtd) = $self->next_token($dtd);
868 0           $text = $self->trim_quotes($text);
869             } else {
870 0           die "Error: Unexpected declared entity type ($name): $tok\n";
871             }
872             }
873              
874 0           ($tok, $dtd) = $self->next_token($dtd);
875              
876 0 0         if ($tok =~ /ndata/i) {
    0          
877 0           ($tok, $dtd) = $self->next_token($dtd);
878             # now $tok contains the notation name
879 0           $type = "ndata $tok";
880 0           ($tok, $dtd) = $self->next_token($dtd);
881             # now $tok should contain the token after the notation
882             } elsif ($tok =~ /cdata/i) {
883 0           ($tok, $dtd) = $self->next_token($dtd);
884             # now $tok contains the notation name
885 0           $type = "cdata $tok";
886 0           ($tok, $dtd) = $self->next_token($dtd);
887             # now $tok should contain the token after the notation
888             }
889              
890 0 0         if ($tok ne '>') {
891 0           print "[[", substr($dtd, 0, 100), "]]\n";
892 0           die "Error: Unexpected token in ENTITY declaration: $tok\n";
893             }
894              
895 0 0         print STDERR "ENT: $type $name (P: $public) (S: $system) [$text]\n" if $debug>1;
896              
897 0           $self->status("Entity $name");
898              
899 0           $self->add_entity($name, $type, $public, $system, $text);
900              
901 0           return $dtd;
902             }
903              
904             sub parse_element {
905 0     0 0   my $self = shift;
906 0           my $dtd = shift;
907 0           my(@names) = ();
908 0           my($stagm, $etagm) = ('', '');
909 0           my $mc = new Text::DelimMatch '\(', '\)[\?\+\*\,]*';
910 0           my($tok, $cm, $expand, $rest);
911 0           my($incl, $excl, $name);
912              
913 0           ($tok, $dtd) = $self->next_token($dtd);
914              
915 0 0         if ($tok =~ /^\(/) {
916 0           my($pre, $namegrp, $ntok, $rest);
917 0           ($pre, $namegrp, $dtd) = $mc->match($tok . $dtd);
918              
919 0           ($ntok, $rest) = $self->next_token($namegrp);
920 0           while ($ntok) {
921 0 0         if ($ntok =~ /[\|\(\)]/) {
922             # nop
923             } else {
924 0           push (@names, $ntok);
925             }
926 0           ($ntok, $rest) = $self->next_token($rest);
927             }
928             } else {
929 0           push (@names, $tok);
930             }
931              
932             # we need to look ahead a little bit here so that we can handle
933             # the case where the start/end tag minimization flags are in
934             # a parameter entity without accidentally expanding parameter
935             # entities in the content model...
936              
937 0           ($tok, $dtd) = $self->next_token($dtd, 1);
938              
939 0 0         if ($tok =~ /^\%/) {
    0          
940             # check to see what this is...
941 0           ($expand, $rest) = $self->next_token($tok);
942              
943 0 0         if ($expand =~ /^[\-o]/is) {
944 0           $stagm = $expand;
945 0           $dtd = $rest . $dtd;
946 0           ($etagm, $dtd) = $self->next_token($dtd);
947             } else {
948 0 0         $dtd = $tok . $dtd if $expand =~ /\S/;
949             }
950             } elsif ($tok =~ /^[\-o]/is) {
951 0           $stagm = $tok;
952 0           ($etagm, $dtd) = $self->next_token($dtd);
953             } else {
954 0           $dtd = $tok . $dtd;
955             }
956              
957             # ok, now $dtd begins with the content model...
958 0           ($tok, $dtd) = $self->next_token($dtd, 1);
959              
960 0 0         if ($tok eq '(') {
961 0           my($pre, $match);
962 0           ($pre, $match, $dtd) = $mc->match($tok . $dtd);
963 0           $cm = $match;
964             } else {
965 0           $cm = $tok;
966             }
967              
968 0           ($tok, $dtd) = $self->next_token($dtd);
969              
970 0 0         if ($tok eq '-') {
971 0           my($pre, $match);
972 0           ($pre, $match, $dtd) = $mc->match($tok . $dtd);
973 0           $excl = $match;
974 0           ($tok, $dtd) = $self->next_token($dtd);
975             }
976              
977 0 0         if ($tok eq '+') {
978 0           my($pre, $match);
979 0           ($pre, $match, $dtd) = $mc->match($tok . $dtd);
980 0           $incl = $match;
981 0           ($tok, $dtd) = $self->next_token($dtd);
982             }
983              
984 0 0         if ($tok ne '>') {
985 0           die "Error: Unexpected token in ELEMENT declaration: $tok\n";
986             }
987              
988 0           foreach $name (@names) {
989 0           $self->status("Element $name");
990              
991 0 0         if (exists($self->{'ELEM'}->{$name})) {
992 0           warn "Warning: Duplicate element declaration for $name ignored.\n";
993             } else {
994 0           my $elem = new SGML::DTDParse::DTD::ELEMENT $self, $name, $stagm,$etagm, $cm, $incl, $excl;
995              
996 0           $self->{'ELEM'}->{$name} = $elem;
997              
998 0           my $count = $self->{'DECLS'}->[0] + 1;
999 0           $self->{'DECLS'}->[0] = $count;
1000 0           $self->{'DECLS'}->[$count] = $elem;
1001             }
1002              
1003 0 0         print STDERR "ELEM: $name = $cm -($excl) +($incl)\n" if $debug>1;
1004             }
1005              
1006 0           return $dtd;
1007             }
1008              
1009             sub parse_attlist {
1010 0     0 0   my $self = shift;
1011 0           my $dtd = shift;
1012 0           my(@names) = ();
1013 0           my $mc = new Text::DelimMatch '\(', '\)[\?\+\*\,]*';
1014 0           my(@attr) = ();
1015 0           my($name, $values, $defval, $type, $tok, $notation_hack);
1016              
1017             # name is name
1018             # values is CDATA or an enumeration (for example)
1019             # defval is a default value
1020             # type is #IMPLIED, #FIXED, #REQUIRED, etc.
1021              
1022 0           ($tok, $dtd) = $self->next_token($dtd);
1023              
1024 0 0         if ($tok =~ /^\(/) {
1025 0           my($pre, $namegrp, $ntok, $rest);
1026 0           ($pre, $namegrp, $dtd) = $mc->match($tok . $dtd);
1027              
1028 0           ($ntok, $rest) = $self->next_token($namegrp);
1029 0           while ($ntok) {
1030 0 0         if ($ntok =~ /[\|\(\)]/) {
1031             # nop
1032             } else {
1033 0           push (@names, $ntok);
1034             }
1035 0           ($ntok, $rest) = $self->next_token($rest);
1036             }
1037             } else {
1038 0           push (@names, $tok);
1039             }
1040              
1041 0 0         print STDERR "\nATTLIST ", join(" ", @names), "\n" if $debug > 2;
1042              
1043             # now we're looking at the attribute declarations...
1044              
1045             # first grab the whole darn thing, unexpanded...
1046             # this is a tad iffy, perhaps, but I think it always works...
1047 0           $dtd =~ /^(.*?)>/is;
1048 0           my $attdecl = $1;
1049              
1050             # then we can look at the expanded thing...
1051 0           ($tok, $dtd) = $self->next_token($dtd);
1052 0           while ($tok ne '>') {
1053 0           $name = $tok;
1054 0           ($values, $dtd) = $self->next_token($dtd);
1055              
1056 0           $defval = "";
1057 0           $type = "";
1058              
1059 0 0         print STDERR "$name\n" if $debug > 2;
1060              
1061 0           $notation_hack = "";
1062 0 0         if ($values =~ /^notation$/i) {
1063 0 0         if ($self->peek_token($dtd)) {
1064 0           $notation_hack = "NOTATION ";
1065 0           ($values, $dtd) = $self->next_token($dtd);
1066             }
1067             }
1068              
1069 0 0         if ($values eq '(') {
1070 0           my(@enum) = ();
1071 0           my($pre, $enum, $ntok, $rest);
1072              
1073 0           ($pre, $enum, $dtd) = $mc->match($values . $dtd);
1074 0           ($ntok, $rest) = $self->next_token($enum);
1075 0 0         print STDERR "\$rest = $rest\n" if $debug>4;
1076 0           while ($ntok ne '') {
1077 0 0         print STDERR "\$ntok = $ntok\n" if $debug>4;
1078 0 0         if ($ntok =~ /[,\|\(\)]/) {
1079             # nop
1080             } else {
1081 0 0         print STDERR "Adding to \@enum: $ntok\n" if $debug>4;
1082 0           push (@enum, $ntok);
1083             }
1084 0           ($ntok, $rest) = $self->next_token($rest);
1085             }
1086              
1087 0           $values = $notation_hack . '(' . join("|", @enum) . ')';
1088             }
1089              
1090 0 0         print STDERR "\t$values\n" if $debug > 2;
1091              
1092 0           ($type, $dtd) = $self->next_token($dtd);
1093              
1094 0 0         print STDERR "\t$type\n" if $debug > 2;
1095              
1096 0 0         if ($type =~ /\#FIXED/i) {
    0          
1097 0           ($defval, $dtd) = $self->next_token($dtd);
1098 0 0         $defval = $self->trim_quotes($defval) if $defval =~ /^[\"\']/;
1099             } elsif ($type !~ /^\#/) {
1100 0           $defval = $type;
1101 0 0         $defval = $self->trim_quotes($defval) if $defval =~ /^[\"\']/;
1102 0           $type = "";
1103             }
1104              
1105 0 0         print STDERR "\t$defval\n" if $debug > 2;
1106              
1107 0           push (@attr, $name, $values, $type, $defval);
1108              
1109 0           ($tok, $dtd) = $self->next_token($dtd);
1110             }
1111              
1112 0           foreach $name (@names) {
1113 0           $self->status("Attlist $name");
1114              
1115 0 0         if (exists($self->{'ATTR'}->{$name})) {
1116 0           my $attlist = $self->{'ATTR'}->{$name};
1117 0           $attlist->append($self, $name, $attdecl, @attr);
1118 0           warn ": duplicate attlist declaration for $name appended.\n";
1119             } else {
1120 0           my $attlist = new SGML::DTDParse::DTD::ATTLIST $self, $name, $attdecl, @attr;
1121 0           $self->{'ATTR'}->{$name} = $attlist;
1122              
1123 0           my $count = $self->{'DECLS'}->[0] + 1;
1124 0           $self->{'DECLS'}->[0] = $count;
1125 0           $self->{'DECLS'}->[$count] = $attlist;
1126             }
1127             }
1128              
1129 0           return $dtd;
1130             }
1131              
1132             sub parse_notation {
1133 0     0 0   my $self = shift;
1134 0           my $dtd = shift;
1135 0           my $name = undef;
1136 0           my($public, $system, $text) = ("", "", "");
1137 0           my($tok);
1138              
1139 0           ($name, $dtd) = $self->next_token($dtd);
1140 0           ($tok, $dtd) = $self->next_token($dtd);
1141              
1142 0 0         if ($tok =~ /public/i) {
    0          
1143 0           ($public, $dtd) = $self->next_token($dtd);
1144 0           $public = $self->trim_quotes($public);
1145              
1146 0           $tok = $self->peek_token($dtd);
1147 0 0         if ($tok ne '>') {
1148 0           ($system, $dtd) = $self->next_token($dtd);
1149 0           $system = $self->trim_quotes($system);
1150             }
1151             } elsif ($tok =~ /system/i) {
1152 0           $tok = $self->peek_token($dtd);
1153 0 0         if ($tok eq '>') {
1154 0           $system = "";
1155             } else {
1156 0           ($system, $dtd) = $self->next_token($dtd);
1157 0           $system = $self->trim_quotes($system);
1158             }
1159             } else {
1160 0           $text = $self->trim_quotes($tok);
1161             }
1162              
1163 0           ($tok, $dtd) = $self->next_token($dtd);
1164              
1165 0 0         if ($tok ne '>') {
1166 0           die "Error: Unexpected token in NOTATION declaration: $tok\n";
1167             }
1168              
1169 0 0         print STDERR "NOT: $name (P: $public) (S: $system) [$text]\n" if $debug > 1;
1170              
1171 0           $self->status("Notation $name");
1172              
1173 0 0         if (exists($self->{'NOTN'}->{$name})) {
1174 0           warn "Warning: Duplicate notation declaration for $name ignored.\n";
1175             } else {
1176 0           my $notation = new SGML::DTDParse::DTD::NOTATION $self, $name, $public, $system, $text;
1177              
1178 0           $self->{'NOTN'}->{$name} = $notation;
1179              
1180 0           my $count = $self->{'DECLS'}->[0] + 1;
1181 0           $self->{'DECLS'}->[0] = $count;
1182 0           $self->{'DECLS'}->[$count] = $notation;
1183             }
1184              
1185 0           return $dtd;
1186             }
1187              
1188             sub parse_markedsection {
1189 0     0 0   my $self = shift;
1190 0           my $dtd = shift;
1191 0           my $mc = new Text::DelimMatch '';
1192 0           my($tok, $pre, $match, $ms);
1193              
1194 0           ($tok, $dtd) = $self->next_token($dtd);
1195              
1196 0           ($pre, $ms, $dtd) = $mc->match("
1197              
1198 0 0         if ($tok =~ /^include$/i) {
1199 0           $ms =~ /^$/s;
1200 0           $dtd = $1 . $dtd;
1201             }
1202              
1203 0           return $dtd;
1204             }
1205              
1206             sub peek_token {
1207 0     0 0   my $self = shift;
1208 0           my $dtd = shift;
1209 0           my $return_peref = shift;
1210 0           my $tok;
1211              
1212 0           ($tok, $dtd) = $self->next_token($dtd, $return_peref);
1213              
1214 0           return $tok;
1215             }
1216              
1217             sub next_token {
1218 0     0 0   my $self = shift;
1219 0           my $dtd = shift;
1220 0           my $return_peref = shift;
1221              
1222 0           $dtd =~ s/^\s*//sg;
1223              
1224 0 0         if ($dtd =~ /^/s) {
1225             # comment declaration
1226 0           return $self->next_token($'); # '
1227             }
1228              
1229 0 0         if ($dtd =~ /^--.*?--/s) {
1230             # comment
1231 0           return $self->next_token($'); # '
1232             }
1233              
1234 0 0         if ($dtd =~ /^<\?.*?>/s) {
1235             # processing instruction
1236 0           return $self->next_token($'); # '
1237             }
1238              
1239 0 0         if ($dtd =~ /^
1240             # beginning of a marked section
1241 0 0         print STDERR "TOK: [$&]\n" if $debug > 3;
1242 0           return ($&, $'); # '
1243             }
1244              
1245 0 0         if ($dtd =~ /^[\(\)\-\+\|\&\,\>]/) {
1246             # beginning of a model group, or incl., or excl., or end decl
1247 0 0         print STDERR "TOK: [$&]\n" if $debug > 3;
1248 0           return ($&, $'); # '
1249             }
1250              
1251 0 0         if ($dtd =~ /^[\"\']/) {
1252             # quoted string
1253 0           $dtd =~ /^(([\"\'])(.*?)\2)/s;
1254 0 0         print STDERR "TOK: [$1]\n" if $debug > 3;
1255 0           return ($&, $'); # '
1256             }
1257              
1258 0 0         if ($dtd =~ /^\%([a-zA-Z0-9\_\-\.]+);?/) {
1259             # peref
1260 0 0         print STDERR "TOK: [$1]\n" if $debug > 3;
1261 0 0         if ($return_peref) {
1262 0           return ("%$1;", $'); # '
1263             } else {
1264 0           my $repltext = $self->entity_repl($1);
1265 0           $dtd = $repltext . $'; # '
1266 0           return $self->next_token($dtd);
1267             }
1268             }
1269              
1270 0 0         if ($dtd =~ /^([^\s\|\&\,\(\)\[\]\>\%]+)/s) {
1271             # next non-space sequence
1272 0 0         print STDERR "TOK: [$1]\n" if $debug > 3;
1273 0           return ($1, $'); # '
1274             }
1275              
1276 0 0         if ($dtd =~ /^(\%)/s) {
1277             # lone % (for param entity declarations)
1278 0 0         print STDERR "TOK: [$1]\n" if $debug > 3;
1279 0           return ($1, $');
1280             }
1281              
1282 0 0         print STDERR "TOK: <>\n" if $debug > 3;
1283 0           return (undef, $dtd);
1284             }
1285              
1286             sub entity_repl {
1287 0     0 0   my $self = shift;
1288 0           my $name = shift;
1289 0           my $entity = $self->pent($name);
1290 0           local(*F, $_);
1291              
1292 0 0         die "Error: %$name; undeclared.\n" if !$entity;
1293              
1294 0 0 0       if ($entity->{'PUBLIC'} || $entity->{'SYSTEM'}) {
1295 0           my $id = "";
1296 0           my $filename = "";
1297              
1298 0 0         if ($entity->{'PUBLIC'}) {
1299 0           $id = $entity->{'PUBLIC'};
1300 0           $filename = $self->{'CAT'}->public_map($id);
1301             }
1302              
1303 0 0 0       if (!$filename && $entity->{'SYSTEM'}) {
1304 0           $id = $entity->{'SYSTEM'};
1305 0           $filename = $self->{'CAT'}->system_map($id);
1306             }
1307              
1308 0 0         if (!defined($filename)) {
1309 0           die "%Error: $name; ($id): not found in catalog.\n";
1310             }
1311              
1312 0 0         if ($self->debug()) {
1313 0           $self->status("Loading $id\n\t($filename)", 1);
1314             } else {
1315 0           $self->status("Loading $id", 1);
1316             }
1317              
1318 0           $filename = $self->resolve_relativesystem($filename);
1319              
1320 0           $self->add_to_searchpath($filename);
1321              
1322 0 0         open (F, $filename) ||
1323             die qq{\n%Error: $name;: Unable to open "$filename": $! \n};
1324             {
1325 0           local $/;
  0            
1326 0           $_ = ;
1327             }
1328 0           close (F);
1329 0           return $_;
1330             } else {
1331 0           return $entity->{'TEXT'};
1332             }
1333             }
1334              
1335             sub trim_quotes {
1336 0     0 0   my $self = shift;
1337 0           my $text = shift;
1338              
1339 0 0         if ($text =~ /^\"(.*)\"$/s) {
    0          
1340 0           $text = $1;
1341             } elsif ($text =~ /^\'(.*)\'$/s) {
1342 0           $text = $1;
1343             } else {
1344 0           die "Error: Unexpected text: $text\n";
1345             }
1346              
1347 0           return $text;
1348             }
1349              
1350             sub fix_entityrefs {
1351 0     0 0   my $self = shift;
1352 0           my $text = shift;
1353              
1354 0 0         if ($text ne "") {
1355 0           my $value = "";
1356              
1357             # make sure all entity references end in semi-colons
1358 0           while ($text =~ /^(.*?)([\&\%]\#?[-.:_a-z0-9]+;?)(.*)$/si) {
1359 0           my $entref = $2;
1360 0           $value .= $1;
1361 0           $text = $3;
1362              
1363 0 0         if ($entref =~ /\;$/s) {
1364 0           $value .= $entref;
1365             } else {
1366 0           $value .= $entref . ";";
1367             }
1368             }
1369              
1370 0           $text = $value . $text;
1371             }
1372              
1373 0           return $text;
1374             }
1375              
1376             sub expand_entities {
1377 0     0 0   my $self = shift;
1378 0           my $text = shift;
1379              
1380 0           while ($text =~ /\%(.*?);/) {
1381 0           my $pre = $`;
1382 0           my $pename = $1;
1383 0           my $post = $'; # '
1384              
1385 0           $text = $pre . $self->entity_repl($pename) . $post;
1386             }
1387              
1388 0           return $text;
1389             }
1390              
1391             sub parse_decl {
1392 0     0 0   my $self = shift;
1393 0           my $decl = shift;
1394 0           local (*F, $_);
1395 0           my $xml = 0;
1396 0           my $namecase_gen = 1;
1397 0           my $namecase_ent = 0;
1398              
1399 0 0         if (!open (F, $decl)) {
1400 0           $self->status(qq{Warning: Failed to load declaration "$decl": $!}, 1);
1401 0           return ($xml, $namecase_gen, $namecase_ent);
1402             }
1403              
1404             {
1405 0           local $/;
  0            
1406 0           $_ = ;
1407             }
1408 0           close (F);
1409              
1410             #
1411             # "ISO 8879:1986 (WWW)"
1412              
1413 0           s/--.*?--//gs; # get rid of comments
1414 0 0         if (!/
1415 0           return ($xml, $namecase_gen, $namecase_ent);
1416             }
1417              
1418 0 0         if (/
1419             # this is XML
1420 0           return (1, 0, 0);
1421             }
1422              
1423 0 0         if (/namecase\s+/is) {
1424 0           $_ = $'; # '
1425 0           my @words = split(/\s+/is, $_);
1426 0           my $done = 0;
1427              
1428 0           while (!$done) {
1429 0           my $word = shift @words;
1430              
1431 0 0         if ($word =~ /^general$/i) {
    0          
1432 0           $word = shift @words;
1433 0           $namecase_gen = ($word =~ /^yes$/i);
1434             } elsif ($word =~ /^entity$/i) {
1435 0           $word = shift @words;
1436 0           $namecase_ent = ($word =~ /^yes$/i);
1437             } else {
1438 0           $done = 1;
1439             }
1440             }
1441             } else {
1442 0           print "No namecase declaration???\n";
1443             }
1444              
1445 0           return ($xml, $namecase_gen, $namecase_ent);
1446             }
1447              
1448             sub add_to_searchpath {
1449 0     0 0   my $self = shift;
1450 0           my $file = shift;
1451 0           my $searchpath = ".";
1452 0           my $found = 0;
1453              
1454 0           $file =~ s/\\/\//sg;
1455 0 0         $searchpath = $1 if $file =~ /^(.*)\/[^\/]+$/;
1456              
1457 0           foreach my $path (@{$self->{'SEARCHPATH'}}) {
  0            
1458 0 0         $found = 1 if $path eq $searchpath;
1459             }
1460              
1461 0 0 0       push (@{$self->{'SEARCHPATH'}}, $searchpath)
  0            
1462             if !$found && $searchpath;
1463             }
1464              
1465             sub resolve_relativesystem {
1466 0     0 0   my $self = shift;
1467 0           my $system = shift;
1468 0           my $found = 0;
1469 0           my $resolved = $system;
1470              
1471 0 0 0       return $system if ($system =~ /^\//) || ($system =~ /^[a-z]:[\\\/]/);
1472              
1473 0           foreach my $path (@{$self->{'SEARCHPATH'}}) {
  0            
1474 0 0         if (-f "$path/$system") {
1475 0           $found = 1;
1476 0           $resolved = "$path/$system";
1477 0           last;
1478             }
1479             }
1480              
1481 0 0         if ($found) {
1482 0           $self->add_to_searchpath($resolved);
1483             } else {
1484 0           $self->status("Could not resolve relative path: $system", 1);
1485             }
1486              
1487 0           return $resolved;
1488             }
1489              
1490             sub status {
1491 0     0 0   my $self = shift;
1492 0           my $msg = shift;
1493 0           my $persist = shift;
1494              
1495 0 0         return if !$self->verbose();
1496              
1497 0 0 0       if ($self->debug() || $self->{'NEWLINE'}) {
1498 0           print STDERR "\n";
1499             } else {
1500 0           print STDERR "\r";
1501 0           print STDERR " " x $self->{'LASTMSGLEN'};
1502 0           print STDERR "\r";
1503             }
1504              
1505 0           print STDERR $msg;
1506              
1507 0           $self->{'LASTMSGLEN'} = length($msg);
1508 0   0       $self->{'NEWLINE'} = $persist || (length($msg) > 79);
1509             }
1510              
1511             1;
1512              
1513             __END__