This file is indexed.

/usr/share/perl5/XML/SAX/Writer.pm is in libxml-sax-writer-perl 0.53-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
###
# XML::SAX::Writer - SAX2 XML Writer
# Robin Berjon <robin@knowscape.com>
###

package XML::SAX::Writer;
use strict;
use vars qw($VERSION %DEFAULT_ESCAPE %COMMENT_ESCAPE);
$VERSION = '0.53';

use Encode                  qw();
use XML::SAX::Exception     qw();
use XML::SAX::Writer::XML   qw();
use XML::Filter::BufferText qw();
@XML::SAX::Writer::Exception::ISA = qw(XML::SAX::Exception);


%DEFAULT_ESCAPE = (
                    '&'     => '&amp;',
                    '<'     => '&lt;',
                    '>'     => '&gt;',
                    '"'     => '&quot;',
                    "'"     => '&apos;',
                  );

%COMMENT_ESCAPE = (
                    '--'    => '&#45;&#45;',
                  );


#-------------------------------------------------------------------#
# new
#-------------------------------------------------------------------#
sub new {
    my $class = ref($_[0]) ? ref(shift) : shift;
    my $opt   = (@_ == 1)  ? { %{shift()} } : {@_};

    # default the options
    $opt->{Writer}          ||= 'XML::SAX::Writer::XML';
    $opt->{Escape}          ||= \%DEFAULT_ESCAPE;
    $opt->{CommentEscape}   ||= \%COMMENT_ESCAPE;
    $opt->{EncodeFrom}      ||= 'utf-8';
    $opt->{EncodeTo}        ||= 'utf-8';
    $opt->{Format}          ||= {}; # needs options w/ defaults, we'll see later
    $opt->{Output}          ||= *{STDOUT}{IO};
    $opt->{QuoteCharacter}  ||= q['];
    
    eval "use $opt->{Writer};";

    my $obj = bless $opt, $opt->{Writer};
    $obj->init;

    # we need to buffer the text to escape it right
    my $bf = XML::Filter::BufferText->new( Handler => $obj );

    return $bf;
}
#-------------------------------------------------------------------#

#-------------------------------------------------------------------#
# init
#-------------------------------------------------------------------#
sub init {} # noop, for subclasses
#-------------------------------------------------------------------#

#-------------------------------------------------------------------#
# setConverter
#-------------------------------------------------------------------#
sub setConverter {
    my $self = shift;

    if (lc($self->{EncodeFrom}) ne lc($self->{EncodeTo})) {
        $self->{Encoder} = XML::SAX::Writer::Encode->new($self->{EncodeFrom}, $self->{EncodeTo});
    }
    else {
        $self->{Encoder} = XML::SAX::Writer::NullConverter->new;
    }
    return $self;
}
#-------------------------------------------------------------------#

#-------------------------------------------------------------------#
# setConsumer
#-------------------------------------------------------------------#
sub setConsumer {
    my $self = shift;

    # create the Consumer
    my $ref = ref $self->{Output};
    if ($ref eq 'SCALAR') {
        $self->{Consumer} = XML::SAX::Writer::StringConsumer->new($self->{Output});
    }
    elsif ($ref eq 'CODE') {
        $self->{Consumer} = XML::SAX::Writer::CodeConsumer->new($self->{Output});
    }
    elsif ($ref eq 'ARRAY') {
        $self->{Consumer} = XML::SAX::Writer::ArrayConsumer->new($self->{Output});
    }
    elsif (
            $ref eq 'GLOB'                                or
            UNIVERSAL::isa(\$self->{Output}, 'GLOB')      or
            UNIVERSAL::isa($self->{Output}, 'IO::Handle')) {
        $self->{Consumer} = XML::SAX::Writer::HandleConsumer->new($self->{Output});
    }
    elsif (not $ref) {
        $self->{Consumer} = XML::SAX::Writer::FileConsumer->new($self->{Output});
    }
    elsif (UNIVERSAL::can($self->{Output}, 'output')) {
        $self->{Consumer} = $self->{Output};
    }
    else {
        XML::SAX::Writer::Exception->throw( Message => 'Unknown option for Output' );
    }
    return $self;
}
#-------------------------------------------------------------------#

#-------------------------------------------------------------------#
# setEscaperRegex
#-------------------------------------------------------------------#
sub setEscaperRegex {
    my $self = shift;

    $self->{EscaperRegex} = eval 'qr/'                                                .
                            join( '|', map { $_ = "\Q$_\E" } keys %{$self->{Escape}}) .
                            '/;'                                                  ;
    return $self;
}
#-------------------------------------------------------------------#

#-------------------------------------------------------------------#
# setCommentEscaperRegex
#-------------------------------------------------------------------#
sub setCommentEscaperRegex {
    my $self = shift;

    $self->{CommentEscaperRegex} =
                        eval 'qr/'                                                .
                        join( '|', map { $_ = "\Q$_\E" } keys %{$self->{CommentEscape}}) .
                        '/;'                                                  ;
    return $self;
}
#-------------------------------------------------------------------#

#-------------------------------------------------------------------#
# escape
#-------------------------------------------------------------------#
sub escape {
    my $self = shift;
    my $str  = shift;

    $str =~ s/($self->{EscaperRegex})/$self->{Escape}->{$1}/oge;
    return $str;
}
#-------------------------------------------------------------------#

#-------------------------------------------------------------------#
# escapeComment
#-------------------------------------------------------------------#
sub escapeComment {
    my $self = shift;
    my $str  = shift;

    $str =~ s/($self->{CommentEscaperRegex})/$self->{CommentEscape}->{$1}/oge;
    return $str;
}
#-------------------------------------------------------------------#

#-------------------------------------------------------------------#
# convert and checking the return value
#-------------------------------------------------------------------#
sub safeConvert {
    my $self = shift;
    my $str = shift;

    my $out = $self->{Encoder}->convert($str);
    
    if (!defined $out and defined $str) {
	warn "Conversion error returned by Encoder [$self->{Encoder}], string: '$str'";
	$out = '_LOST_DATA_';
    }
    return $out;
}
#-------------------------------------------------------------------#


#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
#`,`, The Empty Consumer ,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,#
#```````````````````````````````````````````````````````````````````#

# this package is only there to provide a smooth upgrade path in case
# new methods are added to the interface

package XML::SAX::Writer::ConsumerInterface;

sub new {
    my $class = shift;
    my $ref = shift;
    ## $self is a reference to the reference that we will send output
    ## to.  This allows us to bless $self without blessing $$self.
    return bless \$ref, ref $class || $class;
}

sub output {}
sub finalize {}


#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
#`,`, The String Consumer `,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,#
#```````````````````````````````````````````````````````````````````#

package XML::SAX::Writer::StringConsumer;
@XML::SAX::Writer::StringConsumer::ISA = qw(XML::SAX::Writer::ConsumerInterface);

#-------------------------------------------------------------------#
# new
#-------------------------------------------------------------------#
sub new {
    my $self = shift->SUPER::new( @_ );
    ${${$self}} = '';
    return $self;
}
#-------------------------------------------------------------------#

#-------------------------------------------------------------------#
# output
#-------------------------------------------------------------------#
sub output { ${${$_[0]}} .= $_[1] }
#-------------------------------------------------------------------#

#-------------------------------------------------------------------#
# finalize
#-------------------------------------------------------------------#
sub finalize { ${$_[0]} }
#-------------------------------------------------------------------#

#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
#`,`, The Code Consumer `,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,#
#```````````````````````````````````````````````````````````````````#

package XML::SAX::Writer::CodeConsumer;
@XML::SAX::Writer::CodeConsumer::ISA = qw(XML::SAX::Writer::ConsumerInterface );

#-------------------------------------------------------------------#
# new
#-------------------------------------------------------------------#
sub new {
    my $self = shift->SUPER::new( @_ );
    $$self->( 'start_document', '' );
    return $self;
}
#-------------------------------------------------------------------#

#-------------------------------------------------------------------#
# output
#-------------------------------------------------------------------#
sub output { ${$_[0]}->('data', pop) } ## Avoid an extra copy
#-------------------------------------------------------------------#

#-------------------------------------------------------------------#
# finalize
#-------------------------------------------------------------------#
sub finalize { ${$_[0]}->('end_document', '') }
#-------------------------------------------------------------------#


#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
#`,`, The Array Consumer ,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,#
#```````````````````````````````````````````````````````````````````#

package XML::SAX::Writer::ArrayConsumer;
@XML::SAX::Writer::ArrayConsumer::ISA = qw(XML::SAX::Writer::ConsumerInterface);

#-------------------------------------------------------------------#
# new
#-------------------------------------------------------------------#
sub new {
    my $self = shift->SUPER::new( @_ );
    @$$self = ();
    return $self;
}
#-------------------------------------------------------------------#

#-------------------------------------------------------------------#
# output
#-------------------------------------------------------------------#
sub output { push @${$_[0]}, pop }
#-------------------------------------------------------------------#

#-------------------------------------------------------------------#
# finalize
#-------------------------------------------------------------------#
sub finalize { return ${$_[0]} }
#-------------------------------------------------------------------#


#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
#`,`, The Handle Consumer `,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,#
#```````````````````````````````````````````````````````````````````#

package XML::SAX::Writer::HandleConsumer;
@XML::SAX::Writer::HandleConsumer::ISA = qw(XML::SAX::Writer::ConsumerInterface);

#-------------------------------------------------------------------#
# output
#-------------------------------------------------------------------#
sub output {
    my $fh = ${$_[0]};
    print $fh pop or XML::SAX::Exception->throw(
        Message => "Could not write to handle: $fh ($!)"
    );
}
#-------------------------------------------------------------------#

#-------------------------------------------------------------------#
# finalize
#-------------------------------------------------------------------#
sub finalize { return 0 }
#-------------------------------------------------------------------#


#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
#`,`, The File Consumer `,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,#
#```````````````````````````````````````````````````````````````````#

package XML::SAX::Writer::FileConsumer;
@XML::SAX::Writer::FileConsumer::ISA = qw(XML::SAX::Writer::HandleConsumer);

#-------------------------------------------------------------------#
# new
#-------------------------------------------------------------------#
sub new {
    my ( $proto, $file, $opt ) = @_;
    my $enc_to = (defined $opt and ref $opt eq 'HASH'
                  and defined $opt->{EncodeTo}) ? $opt->{EncodeTo}
                                                : 'utf-8';

    XML::SAX::Writer::Exception->throw(
        Message => "No filename provided to " . ref( $proto || $proto )
    ) unless defined $file;

    local *XFH;

    open XFH, ">:encoding($enc_to)", $file
      or XML::SAX::Writer::Exception->throw(
        Message => "Error opening file $file: $!"
      );

    return $proto->SUPER::new( *{XFH}{IO}, @_ );
}
#-------------------------------------------------------------------#

#-------------------------------------------------------------------#
# finalize
#-------------------------------------------------------------------#
sub finalize {
    close ${$_[0]};
    return 0;
}
#-------------------------------------------------------------------#


#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
#`,`, Noop Converter ,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,#
#```````````````````````````````````````````````````````````````````#

package XML::SAX::Writer::NullConverter;
sub new     { return bless [], __PACKAGE__ }
sub convert { $_[1] }


#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
#`,`, Encode Converter ,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,#
#```````````````````````````````````````````````````````````````````#

package XML::SAX::Writer::Encode;
sub new {
    my $class = shift;
    my $self = {
        from_enc => shift,
        to_enc => shift,
    };
    return bless $self, $class;
}
sub convert {
    my $self = shift;
    my $data = shift;
    eval {
        Encode::from_to( $data, $self->{from_enc}, $self->{to_enc}, Encode::FB_CROAK );
    };
    return $@ ? undef : $data;
};


1;
#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
#`,`, Documentation `,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,`,#
#```````````````````````````````````````````````````````````````````#

=pod

=head1 NAME

XML::SAX::Writer - SAX2 Writer

=head1 SYNOPSIS

  use XML::SAX::Writer;
  use XML::SAX::SomeDriver;

  my $w = XML::SAX::Writer->new;
  my $d = XML::SAX::SomeDriver->new(Handler => $w);

  $d->parse('some options...');

=head1 DESCRIPTION


=head2 Why yet another XML Writer ?

A new XML Writer was needed to match the SAX2 effort because quite
naturally no existing writer understood SAX2. My first intention had
been to start patching XML::Handler::YAWriter as it had previously
been my favourite writer in the SAX1 world.

However the more I patched it the more I realised that what I thought
was going to be a simple patch (mostly adding a few event handlers and
changing the attribute syntax) was turning out to be a rewrite due to
various ideas I'd been collecting along the way. Besides, I couldn't
find a way to elegantly make it work with SAX2 without breaking the
SAX1 compatibility which people are probably still using. There are of
course ways to do that, but most require user interaction which is
something I wanted to avoid.

So in the end there was a new writer. I think it's in fact better this
way as it helps keep SAX1 and SAX2 separated.

=head1 METHODS

=over 4

=item * new(%hash)

This is the constructor for this object. It takes a number of
parameters, all of which are optional.

=item * -- Output

This parameter can be one of several things. If it is a simple
scalar, it is interpreted as a filename which will be opened for
writing. If it is a scalar reference, output will be appended to this
scalar. If it is an array reference, output will be pushed onto this
array as it is generated. If it is a filehandle, then output will be
sent to this filehandle.

Finally, it is possible to pass an object for this parameter, in which
case it is assumed to be an object that implements the consumer
interface L<described later in the documentation|/THE CONSUMER
INTERFACE>.

If this parameter is not provided, then output is sent to STDOUT.

=item * -- Escape

This should be a hash reference where the keys are characters
sequences that should be escaped and the values are the escaped form
of the sequence. By default, this module will escape the ampersand
(&), less than (<), greater than (>), double quote ("), and apostrophe
('). Note that some browsers don't support the &apos; escape used for
apostrophes so that you should be careful when outputting XHTML.

If you only want to add entries to the Escape hash, you can first
copy the contents of %XML::SAX::Writer::DEFAULT_ESCAPE.

=item * -- CommentEscape

Comment content often needs to be escaped differently from other
content. This option works exactly as the previous one except that
by default it only escapes the double dash (--) and that the contents
can be copied from %XML::SAX::Writer::COMMENT_ESCAPE.

=item * -- EncodeFrom

The character set encoding in which incoming data will be provided.
This defaults to UTF-8, which works for US-ASCII as well.

=item * -- EncodeTo

The character set encoding in which output should be encoded. Again,
this defaults to UTF-8.

=item * -- QuoteCharacter

Set the character used to quote attributes. This defaults to single quotes (') 
for backwards compatiblity.

=back

=head1 THE CONSUMER INTERFACE

XML::SAX::Writer can receive pluggable consumer objects that will be
in charge of writing out what is formatted by this module. Setting a
Consumer is done by setting the Output option to the object of your
choice instead of to an array, scalar, or file handle as is more
commonly done (internally those in fact map to Consumer classes and
and simply available as options for your convienience).

If you don't understand this, don't worry. You don't need it most of
the time.

That object can be from any class, but must have two methods in its
API. It is also strongly recommended that it inherits from
XML::SAX::Writer::ConsumerInterface so that it will not break if that
interface evolves over time. There are examples at the end of
XML::SAX::Writer's code.

The two methods that it needs to implement are:

=over 4

=item * output STRING

(Required)

This is called whenever the Writer wants to output a string formatted
in XML. Encoding conversion, character escaping, and formatting have
already taken place. It's up to the consumer to do whatever it wants
with the string.

=item * finalize()

(Optional)

This is called once the document has been output in its entirety,
during the end_document event. end_document will in fact return
whatever finalize() returns, and that in turn should be returned
by parse() for whatever parser was invoked. It might be useful if
you need to provide feedback of some sort.

=back

Here's an example of a custom consumer.  Note the extra C<$> signs in
front of $self; the base class is optimized for the overwhelmingly
common case where only one data member is required and $self is a
reference to that data member.

    package MyConsumer;

    @ISA = qw( XML::SAX::Writer::ConsumerInterface );

    use strict;

    sub new {
        my $self = shift->SUPER::new( my $output );

        $$self = '';      # Note the extra '$'

        return $self;
    }

    sub output {
        my $self = shift;
        $$self .= uc shift;
    }

    sub get_output {
        my $self = shift;
        return $$self;
    }

And here's one way to use it:

    my $c = MyConsumer->new;
    my $w = XML::SAX::Writer->new( Output => $c );

    ## ... send events to $w ...

    print $c->get_output;

If you need to store more that one data member, pass in an array or hash
reference:

        my $self = shift->SUPER::new( {} );

and access it like:

    sub output {
        my $self = shift;
        $$self->{Output} .= uc shift;
    }

=head1 THE ENCODER INTERFACE

Encoders can be plugged in to allow one to use one's favourite encoder
object. Presently there are two encoders: Encode and NullEncoder. They
need to implement two methods, and may inherit from
XML::SAX::Writer::NullConverter if they wish to

=over 4

=item new FROM_ENCODING, TO_ENCODING

Creates a new Encoder. The arguments are the chosen encodings.

=item convert STRING

Converts that string and returns it.

=back

Note that the return value of the convert method is B<not> checked. Output may
be truncated if a character couldn't be converted correctly. To avoid problems
the encoder should take care encoding errors itself, for example by raising an
exception.

=head1 CUSTOM OUTPUT

This module is generally used to write XML -- which it does most of the
time -- but just like the rest of SAX it can be used as a generic
framework to output data, the opposite of a non-XML SAX parser.

Of course there's only so much that one can abstract, so depending on
your format this may or may not be useful. If it is, you'll need to
know the followin API (and probably to have a look inside
C<XML::SAX::Writer::XML>, the default Writer).

=over

=item init

Called before the writing starts, it's a chance for the subclass to do
some initialisation if it needs it.

=item setConverter

This is used to set the proper converter for character encodings. The
default implementation should suffice but you can override it. It must
set C<< $self->{Encoder} >> to an Encoder object. Subclasses *should* call
it.

=item setConsumer

Same as above, except that it is for the Consumer object, and that it
must set C<< $self->{Consumer} >>.

=item setEscaperRegex

Will initialise the escaping regex C<< $self->{EscaperRegex} >> based on
what is needed.

=item escape STRING

Takes a string and escapes it properly.

=item setCommentEscaperRegex and escapeComment STRING

These work exactly the same as the two above, except that they are meant
to operate on comment contents, which often have different escaping rules
than those that apply to regular content.

=back

=head1 TODO

    - proper UTF-16 handling

    - the formatting options need to be developed.

    - test, test, test (and then some tests)

    - doc, doc, doc (actually this part is in better shape)

    - remove the xml_decl and replace it with intelligent logic, as
    discussed on perl-xml

    - make a the Consumer selecting code available in the API, to avoid
    duplicating

    - add an Apache output Consumer, triggered by passing $r as Output

=head1 CREDITS

Michael Koehne (XML::Handler::YAWriter) for much inspiration and Barrie
Slaymaker for the Consumer pattern idea, the coderef output option and
miscellaneous bugfixes and performance tweaks. Of course the usual
suspects (Kip Hampton and Matt Sergeant) helped in the usual ways.

=head1 AUTHOR

Robin Berjon, robin@knowscape.com

=head1 COPYRIGHT

Copyright (c) 2001-2006 Robin Berjon and Perl XML project. Some rights reserved. 
This program is free software; you can redistribute it and/or modify it under 
the same terms as Perl itself.

=head1 SEE ALSO

XML::SAX::*

=cut