<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">package SGMLS;
use Carp;

$version = '$Revision: 1.14 $';

=head1 NAME

SGMLS - class for postprocessing the output from the B&lt;sgmls&gt; and
B&lt;nsgmls&gt; parsers.

=head1 SYNOPSIS

  use SGMLS;

  my $parse = new SGMLS(STDIN);

  my $event = $parse-&gt;next_event;
  while ($event) {

    SWITCH: {

      ($event-&gt;type eq 'start_element') &amp;&amp; do {
        my $element = $event-&gt;data;    # An object of class SGMLS_Element
        [[your code for the beginning of an element]]
        last SWITCH;
      };

      ($event-&gt;type eq 'end_element') &amp;&amp; do {
        my $element = $event-&gt;data;    # An object of class SGMLS_Element
        [[your code for the end of an element]]
        last SWITCH;
      };

      ($event-&gt;type eq 'cdata') &amp;&amp; do {
        my $cdata = $event-&gt;data;      # A string
        [[your code for character data]]
        last SWITCH;
      };

      ($event-&gt;type eq 'sdata') &amp;&amp; do {
        my $sdata = $event-&gt;data;      # A string
        [[your code for system data]]
        last SWITCH;
      };

      ($event-&gt;type eq 're') &amp;&amp; do {
        [[your code for a record end]]
        last SWITCH;
      };

      ($event-&gt;type eq 'pi') &amp;&amp; do {
        my $pi = $event-&gt;data;         # A string
        [[your code for a processing instruction]]
        last SWITCH;
      };

      ($event-&gt;type eq 'entity') &amp;&amp; do {
        my $entity = $event-&gt;data;     # An object of class SGMLS_Entity
        [[your code for an external entity]]
        last SWITCH;
      };

      ($event-&gt;type eq 'start_subdoc') &amp;&amp; do {
        my $entity = $event-&gt;data;     # An object of class SGMLS_Entity
        [[your code for the beginning of a subdoc entity]]
        last SWITCH;
      };

      ($event-&gt;type eq 'end_subdoc') &amp;&amp; do {
        my $entity = $event-&gt;data;     # An object of class SGMLS_Entity
        [[your code for the end of a subdoc entity]]
        last SWITCH;
      };

      ($event-&gt;type eq 'conforming') &amp;&amp; do {
        [[your code for a conforming document]]
        last SWITCH;
      };

      die "Internal error: unknown event type " . $event-&gt;type . "\n";
    }

    $event = $parse-&gt;next_event;
  }

=head1 DESCRIPTION

The B&lt;SGMLS&gt; package consists of several related classes: see
L&lt;"SGMLS"&gt;, L&lt;"SGMLS_Event"&gt;, L&lt;"SGMLS_Element"&gt;,
L&lt;"SGMLS_Attribute"&gt;, L&lt;"SGMLS_Notation"&gt;, and L&lt;"SGMLS_Entity"&gt;.  All
of these classes are available when you specify

  use SGMLS;

Generally, the only object which you will create explicitly will
belong to the C&lt;SGMLS&gt; class; all of the others will then be created
automatically for you over the course of the parse.  Much fuller
documentation is available in the C&lt;.sgml&gt; files in the C&lt;DOC/&gt;
directory of the C&lt;SGMLS.pm&gt; distribution.

=head2 The C&lt;SGMLS&gt; class

This class holds a single parse.  When you create an instance of it,
you specify a file handle as an argument (if you are reading the
output of B&lt;sgmls&gt; or B&lt;nsgmls&gt; from a pipe, the file handle will
ordinarily be C&lt;STDIN&gt;):

  my $parse = new SGMLS(STDIN);

The most important method for this class is C&lt;next_event&gt;, which reads
and returns the next major event from the input stream.  It is
important to note that the C&lt;SGMLS&gt; class deals with most B&lt;ESIS&gt;
events itself: attributes and entity definitions, for example, are
collected and stored automatically and invisibly to the user.  The
following list contains all of the methods for the C&lt;SGMLS&gt; class:

=item C&lt;next_event()&gt;: Return an C&lt;SGMLS_Event&gt; object containing the
next major event from the SGML parse.

=item C&lt;element()&gt;: Return an C&lt;SGMLS_Element&gt; object containing the
current element in the document.

=item C&lt;file()&gt;: Return a string containing the name of the current
SGML source file (this will work only if the C&lt;-l&gt; option was given to
B&lt;sgmls&gt; or B&lt;nsgmls&gt;).

=item C&lt;line()&gt;: Return a string containing the current line number
from the source file (this will work only if the C&lt;-l&gt; option was
given to B&lt;sgmls&gt; or B&lt;nsgmls&gt;).

=item C&lt;appinfo()&gt;: Return a string containing the C&lt;APPINFO&gt;
parameter (if any) from the SGML declaration.

=item C&lt;notation(NNAME)&gt;: Return an C&lt;SGMLS_Notation&gt; object
representing the notation named C&lt;NNAME&gt;.  With newer versions of
B&lt;nsgmls&gt;, all notations are available; otherwise, only the notations
which are actually used will be available.

=item C&lt;entity(ENAME)&gt;: Return an C&lt;SGMLS_Entity&gt; object representing
the entity named C&lt;ENAME&gt;.  With newer versions of B&lt;nsgmls&gt;, all
entities are available; otherwise, only external data entities and
internal entities used as attribute values will be available.

=item C&lt;ext()&gt;: Return a reference to an associative array for
user-defined extensions.


=head2 The C&lt;SGMLS_Event&gt; class

This class holds a single major event, as generated by the
C&lt;next_event&gt; method in the C&lt;SGMLS&gt; class.  It uses the following
methods:

=item C&lt;type()&gt;: Return a string describing the type of event:
"start_element", "end_element", "cdata", "sdata", "re", "pi",
"entity", "start_subdoc", "end_subdoc", and "conforming".  See
L&lt;"SYNOPSIS"&gt;, above, for the values associated with each of these.

=item C&lt;data()&gt;: Return the data associated with the current event (if
any).  For "start_element" and "end_element", returns an
C&lt;SGMLS_ELement&gt; object; for "entity", "start_subdoc", and
"end_subdoc", returns an C&lt;SGMLS_Entity&gt; object; for "cdata", "sdata",
and "pi", returns a string; and for "re" and "conforming", returns the
empty string.  See L&lt;"SYNOPSIS"&gt;, above, for an example of this
method's use.

=item C&lt;key()&gt;: Return a string key to the event, such as an element
or entity name (otherwise, the same as C&lt;data()&gt;).

=item C&lt;file()&gt;: Return the current file name, as in the C&lt;SGMLS&gt;
class.

=item C&lt;line()&gt;: Return the current line number, as in the C&lt;SGMLS&gt;
class.

=item C&lt;element()&gt;: Return the current element, as in the C&lt;SGMLS&gt;
class.

=item C&lt;parse()&gt;: Return the C&lt;SGMLS&gt; object which generated the
event.

=item C&lt;entity(ENAME)&gt;: Look up an entity, as in the C&lt;SGMLS&gt; class.

=item C&lt;notation(ENAME)&gt;: Look up a notation, as in the C&lt;SGMLS&gt;
class.

=item C&lt;ext()&gt;: Return a reference to an associative array for
user-defined extensions.


=head2 The C&lt;SGMLS_Element&gt; class

This class is used for elements, and contains all associated
information (such as the element's attributes).  It recognises the
following methods:

=item C&lt;name()&gt;: Return a string containing the name, or Generic
Identifier, of the element, in upper case.

=item C&lt;parent()&gt;: Return the C&lt;SGMLS_Element&gt; object for the
element's parent (if any).

=item C&lt;parse()&gt;: Return the C&lt;SGMLS&gt; object for the current parse.

=item C&lt;attributes()&gt;: Return a reference to an associative array of
attribute names and C&lt;SGMLS_Attribute&gt; structures.  Attribute names
will be all in upper case.

=item C&lt;attribute_names()&gt;: Return an array of strings containing the
names of all attributes defined for the current element, in upper
case.

=item C&lt;attribute(ANAME)&gt;: Return the C&lt;SGMLS_Attribute&gt; structure for
the attribute C&lt;ANAME&gt;.

=item C&lt;set_attribute(ATTRIB)&gt;: Add the C&lt;SGMLS_Attribute&gt; object
C&lt;ATTRIB&gt; to the current element, replacing any other attribute
structure with the same name.

=item C&lt;in(GI)&gt;: Return C&lt;true&gt; (ie. 1) if the string C&lt;GI&gt; is the
name of the current element's parent, or C&lt;false&gt; (ie. 0) if it is
not.

=item C&lt;within(GI)&gt;: Return C&lt;true&gt; (ie. 1) if the string C&lt;GI&gt; is the
name of any of the ancestors of the current element, or C&lt;false&gt;
(ie. 0) if it is not.

=item C&lt;ext()&gt;: Return a reference to an associative array for
user-defined extensions.


=head2 The C&lt;SGMLS_Attribute&gt; class

Each instance of an attribute for each C&lt;SGMLS_Element&gt; is an object
belonging to this class, which recognises the following methods:

=item C&lt;name()&gt;: Return a string containing the name of the current
attribute, all in upper case.

=item C&lt;type()&gt;: Return a string containing the type of the current
attribute, all in upper case.  Available types are "IMPLIED", "CDATA",
"NOTATION", "ENTITY", and "TOKEN".

=item C&lt;value()&gt;: Return the value of the current attribute, if any.
This will be an empty string if the type is "IMPLIED", a string of
some sort if the type is "CDATA" or "TOKEN" (if it is "TOKEN", you may
want to split the string into a series of separate tokens), an
C&lt;SGMLS_Notation&gt; object if the type is "NOTATION", or an
C&lt;SGMLS_Entity&gt; object if the type is "ENTITY".  Note that if the
value is "CDATA", it will I&lt;not&gt; have escape sequences for 8-bit
characters, record ends, or SDATA processed -- that will be your
responsibility.

=item C&lt;is_implied()&gt;: Return C&lt;true&gt; (ie. 1) if the value of the
attribute is implied, or C&lt;false&gt; (ie. 0) if it is specified in the
document.

=item C&lt;set_type(TYPE)&gt;: Change the type of the attribute to the
string C&lt;TYPE&gt; (which should be all in upper case).  Available types
are "IMPLIED", "CDATA", "NOTATION", "ENTITY", and "TOKEN".

=item C&lt;set_value(VALUE)&gt;: Change the value of the attribute to
C&lt;VALUE&gt;, which may be a string, an C&lt;SGMLS_Entity&gt; object, or an
C&lt;SGMLS_Notation&gt; subject, depending on the attribute's type.

=item C&lt;ext()&gt;: Return a reference to an associative array available
for user-defined extensions.


=head2 The C&lt;SGMLS_Notation&gt; class

All declared notations appear as objects belonging to this class,
which recognises the following methods:

=item C&lt;name()&gt;: Return a string containing the name of the notation.

=item C&lt;sysid()&gt;: Return a string containing the system identifier of
the notation, if any.

=item C&lt;pubid()&gt;: Return a string containing the public identifier of
the notation, if any.

=item C&lt;ext()&gt;: Return a reference to an associative array available
for user-defined extensions.


=head2 The C&lt;SGMLS_Entity&gt; class

All declared entities appear as objects belonging to this class, which
recognises the following methods:

=item C&lt;name()&gt;: Return a string containing the name of the entity, in
mixed case.

=item C&lt;type()&gt;: Return a string containing the type of the entity, in
upper case.  Available types are "CDATA", "SDATA", "NDATA" (external
entities only), "SUBDOC", "PI" (newer versions of B&lt;nsgmls&gt; only), or
"TEXT" (newer versions of B&lt;nsgmls&gt; only).

=item C&lt;value()&gt;: Return a string containing the value of the entity,
if it is internal.

=item C&lt;sysid()&gt;: Return a string containing the system identifier of
the entity (if any), if it is external.

=item C&lt;pubid()&gt;: Return a string containing the public identifier of
the entity (if any), if it is external.

=item C&lt;filenames()&gt;: Return an array of strings containing any file
names generated from the identifiers, if the entity is external.

=item C&lt;notation()&gt;: Return the C&lt;SGMLS_Notation&gt; object associated
with the entity, if it is external.

=item C&lt;data_attributes()&gt;: Return a reference to an associative array
of data attribute names (in upper case) and the associated
C&lt;SGMLS_Attribute&gt; objects for the current entity.

=item C&lt;data_attribute_names()&gt;: Return an array of data attribute
names (in upper case) for the current entity.

=item C&lt;data_attribute(ANAME)&gt;: Return the C&lt;SGMLS_Attribute&gt; object
for the data attribute named C&lt;ANAME&gt; for the current entity.

=item C&lt;set_data_attribute(ATTRIB)&gt;: Add the C&lt;SGMLS_Attribute&gt; object
C&lt;ATTRIB&gt; to the current entity, replacing any other data attribute
with the same name.

=item C&lt;ext()&gt;: Return a reference to an associative array for
user-defined extensions.


=head1 AUTHOR AND COPYRIGHT

Copyright 1994 and 1995 by David Megginson,
C&lt;dmeggins@aix1.uottawa.ca&gt;.  Distributed under the terms of the Gnu
General Public License (version 2, 1991) -- see the file C&lt;COPYING&gt;
which is included in the B&lt;SGMLS.pm&gt; distribution.


=head1 SEE ALSO:

L&lt;SGMLS::Output&gt; and L&lt;SGMLS::Refs&gt;.

=cut

#
# Data class for a single SGMLS ESIS output event.  The object will
# keep information about its own current element and, if available,
# the source file and line where the event appeared.
#
# Event types are as follow:
#        Event                 Data
# -------------------------------------------------------
#     'start_element'        SGMLS_Element
#     'end_element'          SGMLS_Element
#     'cdata'                string
#     'sdata'                string
#     're'                   [none]
#     'pi'                   string
#     'entity'               SGMLS_Entity
#     'start_subdoc'         SGMLS_Entity
#     'end_subdoc'           SGMLS_Entity
#     'conforming'           [none]
#
package SGMLS_Event;
use Carp;
				# Constructor.
sub new {
    my ($class,$type,$data,$parse) = @_;
    return bless [$type,
		  $data,
		  $parse-&gt;file,
		  $parse-&gt;line,
		  $parse-&gt;element,
		  $parse,
		  {}
		  ];
}
				# Accessors.
sub type { return $_[0]-&gt;[0]; }
sub data { return $_[0]-&gt;[1]; }
sub file { return $_[0]-&gt;[2]; }
sub line { return $_[0]-&gt;[3]; }
sub element { return $_[0]-&gt;[4]; }
sub parse { return $_[0]-&gt;[5]; }
sub ext { return $_[0]-&gt;[6]; }
				# Generate a key for the event.
sub key {
    my $self = shift;
    if (ref($self-&gt;data) eq SGMLS_Element ||
	ref($self-&gt;data) eq SGMLS_Entity) {
	return $self-&gt;data-&gt;name;
    } else {
	return $self-&gt;data;
    }
}
				# Look up an entity in the parse.
sub entity {
    my ($self,$ename) = (@_);
    return $self-&gt;parse-&gt;entity($ename);
}
				# Look up a notation in the parse.
sub notation {
    my ($self,$nname) = (@_);
    return $self-&gt;parse-&gt;notation($nname);
}
    

#
# Data class for a single SGML attribute.  The object will know its
# type, and will keep a value unless the type is 'IMPLIED', in which
# case no meaningful value is available.
#
# Attribute types are as follow:
#      Type                    Value
# ---------------------------------------
#     IMPLIED                 [none]
#     CDATA                   string
#     NOTATION                SGMLS_Notation
#     ENTITY                  SGMLS_Entity
#     TOKEN                   string
#
package SGMLS_Attribute;
use Carp;
				# Constructor.
sub new {
    my ($class,$name,$type,$value) = @_;
    return bless [$name,$type,$value,{}];
}
				# Accessors.
sub name { return $_[0]-&gt;[0]; }
sub type { return $_[0]-&gt;[1]; }
sub value { return $_[0]-&gt;[2]; }
sub ext { return $_[0]-&gt;[3]; }
				# Return 1 if the value is implied.
sub is_implied {
    my $self = shift;
    return ($self-&gt;type eq 'IMPLIED');
}
				# Set the attribute's type.
sub set_type {
    my ($self,$type) = @_;
    $self-&gt;[1] = $type;
}

				# Set the attribute's value.
sub set_value {
    my ($self,$value) = @_;
    $self-&gt;[2] = $value;
}


#
# Data class for a single element of an SGML document.  The object will not
# know about its children (data or other elements), but it keeps track of its
# parent and its attributes.
#
package SGMLS_Element;
use Carp;
				# Constructor.
sub new {
    my ($class,$name,$parent,$attributes,$parse) = @_;
    return bless [$name,$parent,$attributes,$parse,{}];
}
				# Accessors.
sub name { return $_[0]-&gt;[0]; }
sub parent { return $_[0]-&gt;[1]; }
sub parse { return $_[0]-&gt;[3]; }
sub ext { return $_[0]-&gt;[4]; }

				# Return the associative array of
				# attributes, parsing it the first
				# time through.
sub attributes {
    my $self = shift;
    if (ref($self-&gt;[2]) eq 'ARRAY') {
	my $new = {};
	foreach (@{$self-&gt;[2]}) {
	    /^(\S+) (IMPLIED|CDATA|NOTATION|ENTITY|TOKEN)( (.*))?$/
		|| croak "Bad attribute event data: $_";
	    my ($name,$type,$value) = ($1,$2,$4);
	    if ($type eq 'NOTATION') {
		$value = $self-&gt;parse-&gt;notation($value);
	    } elsif ($type eq 'ENTITY') {
		$value = $self-&gt;parse-&gt;entity($value);
	    }
	    $new-&gt;{$name} =
		new SGMLS_Attribute($name,$type,$value);
	}
	$self-&gt;[2] = $new;
    }
    return $self-&gt;[2];
}
				# Return a list of attribute names.
sub attribute_names {
    my $self = shift;
    return keys(%{$self-&gt;attributes});
}
				# Find an attribute by name.
sub attribute {
    my ($self,$aname) = @_;
    return $self-&gt;attributes-&gt;{$aname};
}
				# Add a new attribute.
sub set_attribute {
    my ($self,$attribute) = @_;
    $self-&gt;attributes-&gt;{$attribute-&gt;name} = $attribute;
}
				# Check parent by name.
sub in {
    my ($self,$name) = @_;
    if ($self-&gt;parent &amp;&amp; $self-&gt;parent-&gt;name eq $name) {
	return $self-&gt;parent;
    } else {
	return '';
    }
}
				# Check ancestors by name.
sub within {
    my ($self,$name) = @_;
    for ($self = $self-&gt;parent; $self; $self = $self-&gt;parent) {
	return $self if ($self-&gt;name eq $name);
    }
    return '';
}
    

#
# Data class for an SGML notation.  The only information available
# will be the name, the sysid, and the pubid -- the rest is up to the
# processing application.
#
package SGMLS_Notation;
use Carp;
				# Constructor.
sub new {
    my ($class,$name,$sysid,$pubid) = @_;
    return bless [$name,$sysid,$pubid,{}];
}
				# Accessors.
sub name { return $_[0]-&gt;[0]; }
sub sysid { return $_[0]-&gt;[1]; }
sub pubid { return $_[0]-&gt;[2]; }
sub ext { return $_[0]-&gt;[3]; }

#
# Data class for a single SGML entity.  All entities will have a name
# and a type.  Internal entities will be of type CDATA or SDATA only,
# and will have a value rather than a notation and sysid/pubid.  External
# CDATA, NDATA, and SDATA entities will always have notations attached,
# and SUBDOC entities are always external (and will be parsed by SGMLS).
#
# Entity types are as follow:
#      Type     Internal    External
# -----------------------------------------------------------
#     CDATA      x           x
#     NDATA                  x
#     SDATA      x           x
#     SUBDOC                 x
# (newer versions of NSGMLS only:)
#     PI         x
#     TEXT       x           x
#
package SGMLS_Entity;
use Carp;
				# Constructor.
sub new {
    my ($class,$name,$type,$value,$sysid,$pubid,$filenames,$notation) = @_;
    return bless [$name,$type,$value,{},$sysid,$pubid,$filenames,$notation,{}];
}
				# Accessors.
sub name { return $_[0]-&gt;[0]; }
sub type { return $_[0]-&gt;[1]; }
sub value { return $_[0]-&gt;[2]; }
sub data_attributes { return $_[0]-&gt;[3]; }
sub sysid { return $_[0]-&gt;[4]; }
sub pubid { return $_[0]-&gt;[5]; }
sub filenames { return $_[0]-&gt;[6]; }
sub notation { return $_[0]-&gt;[7]; }
sub ext { return $_[0]-&gt;[8]; }
				# Return a list of data-attribute names.
sub data_attribute_names {
    my $self = shift;
    return keys(%{$self-&gt;data_attributes});
}
				# Find a data attribute by name.
sub data_attribute {
    my ($self,$aname) = @_;
    return $self-&gt;data_attributes-&gt;{$aname};
}
				# Add a new data attribute.
sub set_data_attribute {
    my ($self,$data_attribute) = @_;
    $self-&gt;data_attributes()-&gt;{$data_attribute-&gt;name} = $data_attribute;
}

    

#
# Data class for a single SGMLS parse.  The constructor takes a single
# argument, a file handle from which the SGMLS ESIS events will be read
# (it may be a pipe, a fifo, a file, a socket, etc.).  It is essential
# that no two SGMLS objects have the same handle.
#
package SGMLS;
				# Constructor.
sub new {
    my ($class,$handle) = @_;

    # Force unqualified filehandles into caller's package
    my ($package) = caller;
    $handle =~ s/^[^':]+$/$package\:\:$&amp;/;

    return bless {
	'handle' =&gt; $handle,
	'event_stack' =&gt; [],
	'current_element' =&gt; '',
	'current_attributes' =&gt; [],
	'current_entities' =&gt; {},
	'entity_stack' =&gt; [],
	'current_notations' =&gt; {},
	'notation_stack' =&gt; [],
	'current_sysid' =&gt; '',
	'current_pubid' =&gt; '',
	'current_filenames' =&gt; [],
	'current_file' =&gt; '',
	'current_line' =&gt; '',
	'appinfo' =&gt; '',
	'ext' =&gt; {}
	};
}
				# Accessors.
sub element { return $_[0]-&gt;{'current_element'}; }
sub file { return $_[0]-&gt;{'current_file'}; }
sub line { return $_[0]-&gt;{'current_line'}; }
sub appinfo { return $_[0]-&gt;{'appinfo'}; }
sub ext { return $_[0]-&gt;{'ext'}; }

				# Given its name, look up a notation.
sub notation {
    my ($self,$nname) = @_;
    return $self-&gt;{'current_notations'}-&gt;{$nname};
}
				# Given its name, look up an entity.
sub entity {
    my ($self,$ename) = @_;
    return $self-&gt;{'current_entities'}-&gt;{$ename};
}

				# Return the next SGMLS_Event, or ''
				# if the document has finished.
sub next_event {
    my $self = shift;
    my $handle = $self-&gt;{'handle'};

				# If there are any queued up events,
				# grab them first.
    if ($#{$self-&gt;{event_stack}} &gt;= 0) {
	return pop @{$self-&gt;{event_stack}};
    }

  dispatch: while (!eof($handle)) {

      my $c = getc($handle);
      my $data = &lt;$handle&gt;;
      chop $data;

      ($c eq '(') &amp;&amp; do {	# start an element
	  $self-&gt;{'current_element'} =
	      new SGMLS_Element($data,
				$self-&gt;{'current_element'},
				$self-&gt;{'current_attributes'},
				$self);
	  $self-&gt;{'current_attributes'} = [];
	  return new SGMLS_Event('start_element',
				 $self-&gt;{'current_element'},
				 $self);
      };
      
      ($c eq ')') &amp;&amp; do {	# end an element
	  my $old = $self-&gt;{'current_element'};
	  $self-&gt;{'current_element'} = $self-&gt;{'current_element'}-&gt;parent;
	  return new SGMLS_Event('end_element',$old,$self);
      };
      
      ($c eq '-') &amp;&amp; do {	# some data
	  my $sdata_flag = 0;
	  my $out = '';
	  while ($data =~ /\\(\\|n|\||[0-7]{1,3})/) {
	      $out .= $`;
	      $data = $';
				# beginning or end of SDATA
	      if ($1 eq '|') {
		  if ("$out" ne '') {
		      unshift(@{$self-&gt;{'event_stack'}},
			      new SGMLS_Event($sdata_flag?'sdata':'cdata',
					      $out,
					      $self));
		      $out = '';
		  }
		  $sdata_flag = !$sdata_flag;
				# record end
	      } elsif ($1 eq 'n') {
		  if ("$out" ne '') {
		      unshift(@{$self-&gt;{'event_stack'}},
			      new SGMLS_Event($sdata_flag?'sdata':'cdata',
					      $out,
					      $self));
		      $out = '';
		  }
		  unshift(@{$self-&gt;{'event_stack'}},
			  new SGMLS_Event('re','',$self));
	      } elsif ($1 eq '\\') {
		  $out .= '\\';
	      } else {
		  $out .= chr(oct($1));
	      }
	  }
	  $out .= $data;
	  if ("$out" ne '') {
	      unshift(@{$self-&gt;{'event_stack'}},
		      new SGMLS_Event($sdata_flag?'sdata':'cdata',
				      $out,
				      $self));
	  }
	      return $self-&gt;next_event;
      };
      
      ($c eq '&amp;') &amp;&amp; do {	# external entity reference
	  return new SGMLS_Event('entity',
				 ($self-&gt;{'current_entities'}-&gt;{$data}
				  || croak "Unknown external entity: $data\n"),
				 $self);
      };
      
      ($c eq '?') &amp;&amp; do {	# processing instruction
	  return new SGMLS_Event('pi',
				 $data,
				 $self);
      };
      
      ($c eq 'A') &amp;&amp; do {	# attribute declaration
				# (will parse only on demand)
	  push @{$self-&gt;{'current_attributes'}}, $data;
	  next dispatch;
      };
      
      ($c eq 'a') &amp;&amp; do {	# link attribute declaration
	  # NOT YET IMPLEMENTED!
	  next dispatch;
      };
      
      ($c eq 'D') &amp;&amp; do {	# data attribute declaration
	  $data =~ /^(\S+) (\S+) (\S+)( (.*))?$/
	    || croak "Bad data-attribute event data: $data";
	  my ($ename,$aname,$type,$value) = ($1,$2,$3,$5);
	  my $entity = $self-&gt;{'current_entities'}-&gt;{$ename};
	  my $attribute = new SGMLS_Attribute($aname,$type,$value);
	  $entity-&gt;set_data_attribute($attribute);
	  next dispatch;
      };
      
      ($c eq 'N') &amp;&amp; do {	# notation declaration
	  $self-&gt;{'current_notations'}-&gt;{$data} =
	      new SGMLS_Notation($data,
				 $self-&gt;{'current_sysid'},
				 $self-&gt;{'current_pubid'});
	  $self-&gt;{'current_sysid'} = '';
	  $self-&gt;{'current_pubid'} = '';
	  next dispatch;
      };
      
      ($c eq 'E') &amp;&amp; do {	# external entity declaration
	  $data =~ /^(\S+) (\S+) (\S+)$/
	      || croak "Bad external entity event data: $data";
	  my ($name,$type,$nname) = ($1,$2,$3);
	  my $notation = $self-&gt;{'current_notations'}-&gt;{$nname} if $nname;
	  $self-&gt;{'current_entities'}-&gt;{$name} =
	      new SGMLS_Entity($name,
			       $type,
			       '',
			       $self-&gt;{'current_sysid'},
			       $self-&gt;{'current_pubid'},
			       $self-&gt;{'current_filenames'},
			       $notation);
	  $self-&gt;{'current_sysid'} = '';
	  $self-&gt;{'current_pubid'} = '';
	  $self-&gt;{'current_filenames'} = [];
	  next dispatch;
      };
      
      ($c eq 'I') &amp;&amp; do {	# internal entity declaration
	  $data =~ /^(\S+) (\S+) (.*)$/
	      || croak "Bad external entity event data: $data";
	  my ($name,$type,$value) = ($1,$2,$3);
	  $self-&gt;{'current_entities'}-&gt;{$name} =
	      new SGMLS_Entity($name, $type, $value);
	  next dispatch;
      };
      
      ($c eq 'T') &amp;&amp; do {	# external text entity declaration
	  $self-&gt;{'current_entities'}-&gt;{$data} =
	      new SGMLS_Entity($data,
			       'TEXT',
			       '',
			       $self-&gt;{'current_sysid'},
			       $self-&gt;{'current_pubid'},
			       $self-&gt;{'current_filenames'},
			       '');
	  $self-&gt;{'current_sysid'} = '';
	  $self-&gt;{'current_pubid'} = '';
	  $self-&gt;{'current_filenames'} = [];
	  next dispatch;
      };
      
      ($c eq 'S') &amp;&amp; do {	# subdocument entity declaration
	  $self-&gt;{'current_entities'}-&gt;{$data} =
	      new SGMLS_Entity($data,
			       'SUBDOC',
			       '',
			       $self-&gt;{'current_sysid'},
			       $self-&gt;{'current_pubid'},
			       $self-&gt;{'current_filenames'},
			       '');
	  $self-&gt;{'current_sysid'} = '';
	  $self-&gt;{'current_pubid'} = '';
	  $self-&gt;{'current_filenames'} = [];
	  next dispatch;
      };
      
      ($c eq 's') &amp;&amp; do {	# system id
	  $self-&gt;{'current_sysid'} = $data;
	  next dispatch;
      };
      
      ($c eq 'p') &amp;&amp; do {	# public id
	  $self-&gt;{'current_pubid'} = $data;
	  next dispatch;
      };
      
      ($c eq 'f') &amp;&amp; do {	# generated filename
	  push @{$self-&gt;{'current_filenames'}}, $data;
	  next dispatch;
      };
      
      ($c eq '{') &amp;&amp; do {	# begin subdocument entity
	  my $subdoc = ($self-&gt;{'current_entities'}-&gt;{$data}||
			croak "Unknown SUBDOC entity $data\n");
	  push @{$self-&gt;{'notation_stack'}}, $self-&gt;{'current_notations'};
	  push @{$self-&gt;{'entity_stack'}}, $self-&gt;{'current_entities'};
	  $self-&gt;{'current_notations'} = {};
	  $self-&gt;{'current_entities'} = {};
	  return new SGMLS_Event('start_subdoc',
				 $subdoc,
				 $self);
      };
      
      ($c eq '}') &amp;&amp; do {	# end subdocument entity
	  $self-&gt;{'current_notations'} = pop @{$self-&gt;{'notation_stack'}};
	  $self-&gt;{'current_entities'} = pop @{$self-&gt;{'entity_stack'}};
	  return new SGMLS_Event('end_subdoc',
				 ($self-&gt;{'current_entities'}-&gt;{$data} ||
				  croak "Unknown SUBDOC entity $data\n"),
				 $self);
      };

      ($c eq 'L') &amp;&amp; do {	# line number (and file name)
	  $data =~ /^(\d+)( (.*))?$/;
	  $self-&gt;{'current_line'} = $1;
	  $self-&gt;{'current_file'} = $3 if $3;
	  next dispatch;
      };
      
      ($c eq '#') &amp;&amp; do {	# APPINFO parameter
	  $self-&gt;{'appinfo'} = $data;
	  next dispatch;
      };
      
      ($c eq 'C') &amp;&amp; do {	# document is conforming
	  return new SGMLS_Event('conforming','',$self);
      };
  }
    return '';
}

1;

########################################################################
# Local Variables:
# mode: perl
# End:
########################################################################
</pre></body></html>