File Coverage

blib/lib/XML/XSH2/Functions.pm
Criterion Covered Total %
statement 1986 3760 52.8
branch 786 2324 33.8
condition 241 820 29.3
subroutine 301 442 68.1
pod 0 280 0.0
total 3314 7626 43.4


line stmt bran cond sub pod time code
1             # -*- cperl -*-
2             # $Id: Functions.pm,v 2.49 2008-01-27 10:48:39 pajas Exp $
3              
4             package XML::XSH2::Functions;
5              
6             #eval "no encoding";
7             #undef $@;
8 16     16   121 use strict;
  16         35  
  16         644  
9 16     16   117 no warnings;
  16         31  
  16         420  
10              
11 15     15   8392 use XML::XSH2::Help;
  15         64  
  15         716  
12 15     15   14468 use XML::XSH2::Iterators;
  15         37  
  15         288  
13 15     15   495 use IO::File;
  15         7116  
  15         1298  
14 15     15   94 use File::Spec;
  15         30  
  15         220  
15 17     17   121 use Scalar::Util;
  17         35  
  17         957  
16 17     17   6167 use File::Temp qw(tempfile tempdir);
  17         89673  
  17         572  
17 15     15   118 use Carp;
  15         31  
  15         849  
18 15     15   4306 use URI;
  15         35463  
  15         298  
19 17     17   3914 use URI::file;
  17         38069  
  17         701  
20              
21 17     17   114 use Exporter;
  17         46  
  17         668  
22 15         5888 use vars qw/@ISA @EXPORT_OK %EXPORT_TAGS $VERSION $REVISION $OUT
23             @PARAM_VARS
24             $_xml_module $_sigint
25             $_xsh $_xpc $_parser @stored_variables
26             $lexical_variables $_newdoc
27             $TRAP_SIGINT $TRAP_SIGPIPE $_die_on_err $_on_exit
28             $_want_returns
29             %_files %_defs %_includes %_ns %_func %COMMANDS
30             $ENCODING $QUERY_ENCODING
31             $INDENT $BACKUPS $SWITCH_TO_NEW_DOCUMENTS $EMPTY_TAGS $SKIP_DTD
32             $QUIET $DEBUG $TEST_MODE $WARNINGS $ERRORS
33             $VALIDATION $RECOVERING $PARSER_EXPANDS_ENTITIES $KEEP_BLANKS
34             $PEDANTIC_PARSER $LOAD_EXT_DTD $PARSER_COMPLETES_ATTRIBUTES
35             $PARSER_EXPANDS_XINCLUDE $MAXPRINTLENGTH
36             $XPATH_AXIS_COMPLETION $STRICT_PWD
37             $XPATH_COMPLETION $DEFAULT_FORMAT $LINE_NUMBERS
38             $RT_LINE $RT_COLUMN $RT_OFFSET $RT_SCRIPT $SCRIPT
39             $BENCHMARK $DUMP $DUMP_APPEND $Xinclude_prefix $HISTFILE
40             $PROMPT
41 15     15   123 /;
  15         33  
42              
43             BEGIN {
44 15     15   77 $VERSION='2.2.8'; # VERSION TEMPLATE
45 15         29 $REVISION=q($Revision: 2.49 $);
46 15         177 @ISA=qw(Exporter);
47 14         122 @PARAM_VARS=qw/$ENCODING
48             $QUERY_ENCODING
49             $INDENT
50             $EMPTY_TAGS
51             $SKIP_DTD
52             $BACKUPS
53             $SWITCH_TO_NEW_DOCUMENTS
54             $QUIET
55             $DEBUG
56             $TEST_MODE
57             $VALIDATION
58             $RECOVERING
59             $PARSER_EXPANDS_ENTITIES
60             $XPATH_AXIS_COMPLETION
61             $XPATH_COMPLETION
62             $KEEP_BLANKS
63             $PEDANTIC_PARSER
64             $LOAD_EXT_DTD
65             $PARSER_COMPLETES_ATTRIBUTES
66             $PARSER_EXPANDS_XINCLUDE
67             $DEFAULT_FORMAT
68             $LINE_NUMBERS
69             $WARNINGS
70             $MAXPRINTLENGTH
71             $HISTFILE
72             $STRICT_PWD
73             $PROMPT
74             /;
75 14         46 *XSH_NS=*XML::XSH2::xshNS;
76 14         295 *XML::XSH2::Map::XSH_NS=*XML::XSH2::xshNS;
77 14         53 *XML::XSH2::Map::OUT=\$OUT;
78 14         32 *EMPTY_TAGS=*XML::LibXML::setTagCompression;
79 14         53 *SKIP_DTD=*XML::LibXML::skipDTD;
80 14         63 *XML::XSH2::Map::PROGRAM_NAME=\$RT_SCRIPT;
81 14         99 @EXPORT_OK=(qw(&xsh_init &xsh &xsh_get_output
82             &xsh_set_output &xsh_set_parser
83             &set_quiet &set_debug &set_compile_only_mode
84             &create_doc &open_doc &set_doc
85             &xsh_pwd &out
86             &toUTF8 &fromUTF8 &xsh_set_script
87             &xsh_context_node &xsh_context_var
88             &xsh_xml_parser &xsh_parse_string
89             ),@PARAM_VARS);
90 14         395 %EXPORT_TAGS = (
91             default => [@EXPORT_OK],
92             param_vars => [@PARAM_VARS]
93             );
94              
95 14         69 $TRAP_SIGINT=0;
96 14         25 $_xml_module='XML::XSH2::LibXMLCompat';
97 14         49 $INDENT=1;
98 14         69 $EMPTY_TAGS=1; # no effect (reseted by XML::LibXML)
99 14         31 $SKIP_DTD=0; # no effect (reseted by XML::LibXML)
100 14         270 $BACKUPS=1;
101 14         61 $SWITCH_TO_NEW_DOCUMENTS=1;
102 14         37 $ENCODING='utf-8';
103 14         56 $QUERY_ENCODING='utf-8';
104 14         72 $QUIET=0;
105 14         44 $DEBUG=0;
106 14         291 $TEST_MODE=0;
107 14         54 $VALIDATION=0;
108 14         27 $RECOVERING=0;
109 14         51 $PARSER_EXPANDS_ENTITIES=1;
110 14         70 $KEEP_BLANKS=1;
111 14         32 $PEDANTIC_PARSER=0;
112 14         303 $LOAD_EXT_DTD=0;
113 14         63 $PARSER_COMPLETES_ATTRIBUTES=1;
114 14         36 $PARSER_EXPANDS_XINCLUDE=0;
115 14         54 $XPATH_COMPLETION=1;
116 14         71 $XPATH_AXIS_COMPLETION='always'; # never / when-empty
117 14         22 $DEFAULT_FORMAT='xml';
118 14         266 $LINE_NUMBERS=1;
119 14         46 $WARNINGS=1;
120 14         25 $ERRORS=1;
121 14         56 $BENCHMARK=0;
122 13         55 $MAXPRINTLENGTH=256;
123 13         53 $HISTFILE="$ENV{HOME}/.xsh2_history";
124 13         227 $STRICT_PWD=1;
125 13         46 $PROMPT='%p> ';
126 13         32 *XML::XSH2::Map::CURRENT_SCRIPT=\$RT_SCRIPT;
127              
128 13         51 $_newdoc=1;
129 13         60 $_die_on_err=1;
130 13         28 $_want_returns=0;
131              
132 13         324 autoflush STDOUT;
133 13         481 autoflush STDERR;
134 13         244 $lexical_variables = [];
135 13         43 $Xinclude_prefix = 'http://www.w3.org/2001/XInclude';
136 13         12602 require XML::XSH2::Commands;
137             }
138              
139             sub VERSION {
140 1 0   3 0 3 shift if $_[0] eq __PACKAGE__;
141 1         32 my $ver = shift;
142 1 0       5 if (defined($ver)) {
143 1         2 my @V = split /\./,$VERSION;
144 1         4 my @v = split /\./,$ver;
145 1         7 for my $component (@v) {
146 1 0       2 croak __PACKAGE__." version $ver required--this is only version $VERSION"
147             if $component > shift @V;
148             }
149             }
150 1         29 return $VERSION;
151             }
152              
153              
154 1 0   3 0 5 sub min { $_[0] > $_[1] ? $_[1] : $_[0] }
155              
156             sub out {
157 141 100   143 0 736 if (ref($OUT) eq 'IO::Scalar') {
158 6         20 $OUT->print(@_);
159             } else {
160 136         837 foreach (map(fromUTF8($ENCODING,$_), @_)) {
161 198         428 my $l = length;
162 198         325 my $i = 1;
163 198         534 while ($l > $i*$MAXPRINTLENGTH) {
164 6         194 print $OUT (substr($_,($i-1)*$MAXPRINTLENGTH,$MAXPRINTLENGTH));
165 6         29 $i++;
166             }
167 198         2192 print $OUT (substr($_,($i-1)*$MAXPRINTLENGTH)); # the rest
168             }
169             }
170             }
171              
172             sub __debug {
173 1     3   2 _err(@_);
174             }
175              
176             sub __bug {
177 1     3   42 _err("BUG: ",@_);
178             }
179              
180             sub _tilde_expand {
181 2     4   9 my ($filename)=@_;
182 2         5 $filename=~s{^(\~[^\/]*)}{(glob($1))[0]}eg;
  1         4  
183              
184             # $filename =~ s{ ^ ~ ( [^/]* ) }
185             # { $1
186             # ? (getpwnam($1))[7]
187             # : ( $ENV{HOME} || $ENV{LOGDIR}
188             # || (getpwuid($>))[7]
189             # )
190             # }ex;
191 2         8 return $filename;
192             }
193              
194             sub _ev_opts {
195 266     266   618 my ($opts)=@_;
196 266 50       795 return {} unless ref($opts);
197 266 50       761 return $opts if ref($opts) eq 'HASH';
198 266         455 my %o;
199 266         528 my @opts = @$opts;
200 266         736 while (@opts) {
201 27         164 my ($t,$n)=split /\//,shift(@opts);
202 27         193 my $v=shift @opts;
203 27 100 100     185 if ($t eq '' or $t eq 'exp') {
    50          
    100          
204 20         87 $o { $n } = _ev($v);
205             } elsif ($t eq 'var') {
206 1         31 $o { $n } = $v;
207             } elsif ($t eq 'xpath') {
208 6         22 utf8::upgrade($v);
209 6         19 $o { $n } = _expand($v);
210             } else { # string
211 1         4 $o { $n } = _ev_string($v);
212             }
213             }
214 265         724 return \%o;
215             }
216              
217             sub _hash_opts {
218 11     12   31 my ($opts)=@_;
219 11 50       31 return {} unless ref($opts);
220 11         18 my %o;
221 11         28 my @opts = @$opts;
222 11         34 while (@opts) {
223 16         62 my ($t,$n)=split /\//,shift(@opts);
224 16         34 my $v=shift @opts;
225 16         63 $o { $n } = $v;
226             }
227 11         29 return \%o;
228             }
229              
230             sub alias_sr {
231 364     365 0 958 my($src, $dest)=@_;
232 364         1386 tie($$dest, 'XML::XSH2::VarAlias', $src);
233             }
234              
235             sub lexicalize {
236 342     343 0 691 my $p="package XML::XSH2::Map; no strict qw(vars); \$Lexical::Alias::SWAP=0; use utf8;";
237 342         497 my %seen;
238 342         1101 for (my $i=$#$lexical_variables; $i>=0; $i--) {
239 904         1184 foreach my $v (keys %{$lexical_variables->[$i]}) {
  904         2897  
240 364 50       849 next if $seen{$v};
241 364         636 $seen{$v}=1;
242 364         1495 $p.="my \$$v; \&XML::XSH2::Functions::alias_sr(\\\$XML::XSH2::Functions::lexical_variables->[$i]{q#$v#}, \\\$$v);"
243             }
244             }
245             # $p.="\n# line $RT_LINE \"$RT_SCRIPT\"\n";
246 342         25895 return $p.$_[0];
247             }
248              
249             # initialize XSH and XML parsers
250             sub xsh_init {
251 15     16 0 58 my $module=shift;
252 15 50       52 shift unless ref($_[0]);
253 15 50       51 if (ref($_[0])) {
254 0         0 $OUT=$_[0];
255             } else {
256 15 50       461 if (open $OUT, '>&', \*STDOUT) {
257 15         62 binmode $OUT;
258 15         96 binmode $OUT, ':bytes';
259             } else {
260 0         0 $OUT = \*STDOUT;
261             }
262             }
263 15         78 set_encoding({},$ENCODING);
264 15 50       49 $_xml_module=$module if $module;
265 15         802 eval("require $_xml_module;");
266 15 50       81 if ($@) {
267 0         0 _err(
268             "\n------------------------------------------------------------\n",
269             $@,
270             "\n------------------------------------------------------------.\n",
271             "I suspect you have not installed XML::LibXML properly.\n",
272             "Please install and try again. If you are 100% sure you have, send\n",
273             "a full bug report to \n");
274 0         0 exit 1;
275             }
276 15         90 my $mod=$_xml_module->module();
277 15 50       63 if ($] >= 5.008) {
278 15         70 require Encode;
279 15         68 *encodeToUTF8=*Encode::decode;
280 15         39 *decodeFromUTF8=*Encode::encode;
281             } else {
282 13     14   78 no strict 'refs';
  13         266  
  13         10986  
283 0         0 *encodeToUTF8=*{"$mod"."::encodeToUTF8"};
  0         0  
284 0         0 *decodeFromUTF8=*{"$mod"."::decodeFromUTF8"};
  0         0  
285             }
286 15         55 $_parser = $_xml_module->new_parser();
287              
288 15         366 xpc_init();
289             # xsh_rd_parser_init();
290              
291             # create a first document so that we always have non-empty context
292 15         105 create_doc('$scratch',"scratch",'xml','scratch.xml');
293 15         50 set_local_xpath({},'/');
294             }
295              
296             sub xsh_rd_parser_init {
297 8 50   9 0 31 unless ($_xsh) {
298 8 50       13 if (eval { require XML::XSH2::Parser; }) {
  8         37722  
299 8         52 $_xsh=XML::XSH2::Parser->new();
300             } else {
301 0         0 print STDERR "Parsing raw grammar...\n";
302 0         0 require XML::XSH2::Grammar;
303 0         0 $_xsh=XML::XSH2::Grammar->new();
304 0         0 print STDERR "... done.\n";
305 0 0       0 unless ($QUIET) {
306 0         0 print STDERR << 'EOF';
307             NOTE: To avoid this, you should regenerate the XML::XSH2::Parser.pm
308             module from XML::XSH2::Grammar.pm module by changing to XML/XSH/
309             directory in your load-path and running the following command:
310              
311             perl -MGrammar -e XML::XSH2::Grammar::compile
312              
313             EOF
314             }
315             }
316             }
317 8         62 return $_xsh;
318             }
319              
320 0 0   1 0 0 sub set_validation { shift if @_>1; $VALIDATION=$_[0]; 1; }
  0         0  
  0         0  
321 0 0   1 0 0 sub set_recovering { shift if @_>1; $RECOVERING=$_[0]; 1; }
  0         0  
  0         0  
322 0 0   1 0 0 sub set_expand_entities { shift if @_>1; $PARSER_EXPANDS_ENTITIES=$_[0]; 1; }
  0         0  
  0         0  
323 0 0   1 0 0 sub set_keep_blanks { shift if @_>1; $KEEP_BLANKS=$_[0]; 1; }
  0         0  
  0         0  
324 0 0   1 0 0 sub set_pedantic_parser { shift if @_>1; $PEDANTIC_PARSER=$_[0]; 1; }
  0         0  
  0         0  
325 0 0   1 0 0 sub set_load_ext_dtd { shift if @_>1; $LOAD_EXT_DTD=$_[0]; 1; }
  0         0  
  0         0  
326 0 0   1 0 0 sub set_complete_attributes { shift if @_>1; $PARSER_COMPLETES_ATTRIBUTES=$_[0]; 1; }
  0         0  
  0         0  
327 0 0   1 0 0 sub set_expand_xinclude { shift if @_>1; $PARSER_EXPANDS_XINCLUDE=$_[0]; 1; }
  0         0  
  0         0  
328 2 50   3 0 11 sub set_indent { shift if @_>1; $INDENT=$_[0]; 1; }
  2         4  
  2         45  
329 0 0   1 0 0 sub set_empty_tags { shift if @_>1; $EMPTY_TAGS=$_[0]; 1; }
  0         0  
  0         0  
330 0 0   1 0 0 sub set_skip_dtd { shift if @_>1; $SKIP_DTD=$_[0]; 1; }
  0         0  
  0         0  
331 0 0   1 0 0 sub set_backups { shift if @_>1; $BACKUPS=$_[0]; 1; }
  0         0  
  0         0  
332 0 0   1 0 0 sub set_cdonopen { shift if @_>1; $SWITCH_TO_NEW_DOCUMENTS=$_[0]; 1; }
  0         0  
  0         0  
333 0 0   1 0 0 sub set_xpath_completion { shift if @_>1; $XPATH_COMPLETION=$_[0]; 1; }
  0         0  
  0         0  
334 0 0   1 0 0 sub set_xpath_axis_completion { shift if @_>1; $XPATH_AXIS_COMPLETION=$_[0];
  0         0  
335 0 0       0 if ($XPATH_AXIS_COMPLETION!~/^always|when-empty|never$/) {
336 0         0 $XPATH_AXIS_COMPLETION='never';
337             }
338 0         0 1; }
339 0 0   1 0 0 sub set_line_numbers { shift if @_>1; $LINE_NUMBERS=$_[0]; 1; }
  0         0  
  0         0  
340              
341 0     0 0 0 sub get_validation { $VALIDATION }
342 0     0 0 0 sub get_recovering { $RECOVERING }
343 0     0 0 0 sub get_expand_entities { $PARSER_EXPANDS_ENTITIES }
344 8     8 0 61 sub get_keep_blanks { $KEEP_BLANKS }
345 0     0 0 0 sub get_pedantic_parser { $PEDANTIC_PARSER }
346 0     0 0 0 sub get_load_ext_dtd { $LOAD_EXT_DTD }
347 0     0 0 0 sub get_complete_attributes { $PARSER_COMPLETES_ATTRIBUTES }
348 0     0 0 0 sub get_expand_xinclude { $PARSER_EXPANDS_XINCLUDE }
349 0     0 0 0 sub get_indent { $INDENT }
350 0     0 0 0 sub get_empty_tags { $EMPTY_TAGS }
351 0     0 0 0 sub get_skip_dtd { $SKIP_DTD }
352 0     0 0 0 sub get_backups { $BACKUPS }
353 0     0 0 0 sub get_cdonopen { $SWITCH_TO_NEW_DOCUMENTS }
354 0     0 0 0 sub get_xpath_completion { $XPATH_COMPLETION }
355 0     0 0 0 sub get_xpath_axis_completion { $XPATH_AXIS_COMPLETION }
356 0     0 0 0 sub get_line_numbers { $LINE_NUMBERS }
357              
358             # initialize global XPathContext
359             sub xpc_init {
360 15     15 0 44 $_xpc=new_xpath_context();
361 15         55 $_ns{xsh}=$XML::XSH2::xshNS;
362             }
363              
364             sub init_XPATH_funcs {
365 16     16 0 46 my ($xpc,$ns)=@_;
366 16         50 foreach my $name (get_XPATH_extensions()) {
367 784         1177 my $func=$name; $func =~ s/-/_/g;
  784         1213  
368 784         959 $xpc->registerFunctionNS($name,$ns,\&{"XPATH_$func"});
  784         3860  
369             }
370             }
371              
372             sub new_xpath_context {
373 15     15 0 25 my $xpc;
374 15 50       29 unless (eval { require XML::LibXML::XPathContext;
  15         63  
375 15         610 $xpc=XML::LibXML::XPathContext->new();
376             }) {
377 0         0 require XML::XSH2::DummyXPathContext;
378 0         0 print STDERR ("Warning: XML::LibXML::XPathContext not found!\n".
379             "XSH will lack namespace and function registering functionality!\n\n");
380 0         0 return XML::XSH2::DummyXPathContext->new();
381             }
382 15         461 $xpc = XML::LibXML::XPathContext->new();
383 15         102 $xpc->registerVarLookupFunc(\&xpath_var_lookup,undef);
384 15         69 $xpc->registerNs('xsh',$XML::XSH2::xshNS);
385 15         66 init_XPATH_funcs($xpc,$XML::XSH2::xshNS);
386 15         164 return $xpc;
387             }
388              
389             sub clone_xpc {
390 0     0 0 0 my $xpc = new_xpath_context();
391 0         0 foreach (keys(%_ns)) {
392 0         0 $xpc->registerNs($_,$_ns{$_});
393             }
394 0         0 foreach (keys(%_func)) {
395 0 0       0 if (/\n/) {
396 0         0 my ($name,$ns)=/^(.*)\n((?:.|\n)*)$/;
397 0         0 $xpc->registerFunctionNS($name, $ns, $_func{$_});
398             } else {
399 0         0 $xpc->registerFunction($_, $_func{$_});
400             }
401             }
402 0         0 $xpc->setContextNode($_xpc->getContextNode());
403 0         0 return $xpc;
404             }
405              
406             sub xpath_extensions {
407 1     1 0 2 my $opts = shift;
408 1         5 init_XPATH_funcs($_xpc,shift);
409 1         27 return 1;
410             }
411              
412             # ===================== XPATH EXT FUNC ================
413              
414             sub get_XPATH_extensions {
415 16     16 0 198 qw( current doc filename grep id2 if join lc uc ucfirst lcfirst
416             lineno evaluate map matches match max min new-attribute
417             new-cdata new-chunk new-comment new-element new-element-ns new-pi
418             new-text node-type parse path reverse same serialize split sprintf
419             strmax strmin subst substr sum times var document documents lookup span context
420             resolve-uri base-uri document-uri
421             )
422             }
423              
424             sub XPATH_doc {
425 0 0   0 0 0 die "Wrong number of arguments for function xsh:doc(nodeset)!\n" if (@_!=1);
426 0         0 my ($nodelist)=@_;
427 0 0 0     0 die "1st argument must be a nodeset in xsh:doc(nodeset)!\n"
428             unless (ref($nodelist) and UNIVERSAL::isa($nodelist,'XML::LibXML::NodeList'));
429 13     14   4869 use utf8;
  13         144  
  13         110  
430 0         0 return XML::LibXML::NodeList->new(grep { ref($_) } map { $_->ownerDocument } @$nodelist);
  0         0  
  0         0  
431             }
432              
433             sub XPATH_filename {
434 0 0   0 0 0 die "Wrong number of arguments for function xsh:filename(nodeset?) or xsh:document-uri(nodeset?)!\n" if (@_>1);
435 0         0 my $doc;
436 0 0       0 if (@_) {
437 0 0 0     0 die "1st argument must be a node in xsh:filename(nodeset?) or xsh:document-uri(nodeset?)!\n"
438             unless (ref($_[0]) and UNIVERSAL::isa($_[0],'XML::LibXML::NodeList'));
439             }
440 0 0       0 if ($_[0]) {
441 0 0       0 return XML::LibXML::Literal->new('') unless $_[0][0];
442 0         0 $doc = $_[0][0]->ownerDocument;
443             } else {
444 0         0 $doc = $XML::XSH2::Functions::_xpc->getContextNode()->ownerDocument;
445             }
446 13     14   1351 use utf8;
  13         237  
  13         57  
447 0         0 return XML::LibXML::Literal->new($doc->URI());
448             }
449              
450             sub XPATH_resolve_uri {
451 0 0 0 0 0 0 die "Wrong number of arguments for function xsh:resolve-uri(relative-URI,base-URI?)!\n" if (@_>2 or @_==0);
452 0         0 my ($rel,$base)=map literal_value($_), @_;
453 0         0 return XML::LibXML::Literal->new(XML::XSH2::Map::resolve_uri($rel,$base)->as_string);
454             }
455              
456             sub XPATH_document_uri {
457 0     0 0 0 &XPATH_filename;
458             }
459              
460             sub XPATH_base_uri {
461 0 0   0 0 0 die "Wrong number of arguments for function xsh:base_uri(node?)!\n" if (@_>1);
462 0         0 my $node;
463 0 0       0 if (@_) {
464 0 0 0     0 die "1st argument must be a node in xsh:base_uri(node?)!\n"
465             unless (ref($_[0]) and UNIVERSAL::isa($_[0],'XML::LibXML::NodeList'));
466             }
467 0 0       0 if ($_[0]) {
468 0 0       0 return XML::LibXML::Literal->new('') unless $_[0][0];
469 0         0 $node = $_[0][0];
470             } else {
471 0         0 $node = $XML::XSH2::Functions::_xpc->getContextNode();
472             }
473 13     14   2201 use utf8;
  13         50  
  13         65  
474 0   0     0 return XML::LibXML::Literal->new($node->baseURI() || '');
475             }
476              
477              
478             sub XPATH_var {
479 4 50   4 0 296 die "Wrong number of arguments for function xsh:var(id)!\n" if (@_!=1);
480 4         15 my ($id)=literal_value($_[0]);
481 4         27 return var_value($id);
482             }
483              
484             sub XPATH_matches {
485 6 50 66 6 0 545 die "Wrong number of arguments for function xsh:matches(string,regexp)!\n" if (@_!=2 and @_!=3);
486 13     14   1222 use utf8;
  13         216  
  13         59  
487 6         17 my ($string,$regexp,$ignore_case)=@_;
488 6         17 $string=literal_value($string);
489 6         12 $regexp=literal_value($regexp);
490 6         10 $ignore_case=literal_value($ignore_case);
491 6 100       101 return ($ignore_case ?
    100          
492             $string=~m{$regexp}i :
493             $string=~m{$regexp}) ?
494             XML::LibXML::Boolean->True :
495             XML::LibXML::Boolean->False;
496             }
497              
498             sub XPATH_substr {
499 7 50 33 7 0 545 die "Wrong number of arguments for function xsh:substr(string,position,[length])!\n" if (@_<2 or @_>3);
500 13     14   1178 use utf8;
  13         42  
  13         77  
501 7         18 my ($str,$pos,$len)=@_;
502 7 100       26 my $result = (@_ == 2) ?
503             substr(literal_value($str),
504             literal_value($pos)) :
505             substr(literal_value($str),
506             literal_value($pos),
507             literal_value($len));
508 7 50       19 $result = "" unless defined ($result);
509 7         18 return $result;
510             }
511              
512             sub XPATH_reverse {
513 3 50   3 0 204 die "Wrong number of arguments for function xsh:reverse(string)!\n" if (@_!=1);
514 13     14   1174 use utf8;
  13         261  
  13         56  
515 3         9 return scalar reverse(literal_value($_[0]));
516             }
517              
518             sub XPATH_lc {
519 0 0   0 0 0 die "Wrong number of arguments for function xsh:lc(string)!\n" if (@_!=1);
520 13     14   690 use utf8;
  13         45  
  13         81  
521 0         0 return lc(literal_value($_[0]));
522             }
523              
524             sub XPATH_uc {
525 0 0   0 0 0 die "Wrong number of arguments for function xsh:uc(string)!\n" if (@_!=1);
526 13     14   633 use utf8;
  13         240  
  13         59  
527 0         0 return uc(literal_value($_[0]));
528             }
529              
530             sub XPATH_lcfirst {
531 0 0   0 0 0 die "Wrong number of arguments for function xsh:lcfirst(string)!\n" if (@_!=1);
532 13     14   641 use utf8;
  13         43  
  13         71  
533 0         0 return lcfirst(literal_value($_[0]));
534             }
535              
536             sub XPATH_ucfirst {
537 0 0   0 0 0 die "Wrong number of arguments for function xsh:ucfirst(string)!\n" if (@_!=1);
538 13     14   637 use utf8;
  13         235  
  13         57  
539 0         0 return ucfirst(literal_value($_[0]));
540             }
541              
542             sub XPATH_grep {
543 13 50   13 0 1050 die "Wrong number of arguments for function xsh:grep(list,regexp)!\n" if (@_!=2);
544 13         30 my ($nodelist,$regexp)=@_;
545 13 50 33     78 die "1st argument must be a nodeset in grep(list,regexp)!\n"
546             unless (ref($nodelist) and UNIVERSAL::isa($nodelist,'XML::LibXML::NodeList'));
547 13     14   916 use utf8;
  13         45  
  13         84  
548 13         33 [grep { $_->to_literal=~m{$regexp} } @$nodelist];
  26         309  
549             }
550              
551             sub XPATH_same {
552 13 50   13 0 1066 die "Wrong number of arguments for function xsh:same(node,node)!\n" if (@_!=2);
553 13         34 my ($nodea,$nodeb)=@_;
554 13 50 33     90 die "1st argument must be a node in grep(list,regexp)!\n"
555             unless (ref($nodea) and UNIVERSAL::isa($nodea,'XML::LibXML::NodeList'));
556 13 50 33     68 die "2nd argument must be a node in grep(list,regexp)!\n"
557             unless (ref($nodeb) and UNIVERSAL::isa($nodeb,'XML::LibXML::NodeList'));
558 13   100     49 return XML::LibXML::Boolean->new($nodea->size() && $nodeb->size() &&
559             $nodea->[0]->isSameNode($nodeb->[0]));
560             }
561              
562             sub XPATH_max {
563 7     7 0 654 my $r;
564 7         21 foreach (cast_objects_to_values(@_)) {
565 17 50       105 next unless /^\s*(-\s*)?(\d+(\.\d*)?|\.\d+)\s*$/;
566 17 100       38 $r = $_ unless defined($r);
567 17 100       43 $r = $_>$r ? $_ : $r;
568             }
569 7         28 ; 0+$r;
570             }
571              
572             sub XPATH_strmax {
573 4     4 0 257 my $r;
574 4         10 foreach (cast_objects_to_values(@_)) {
575 9 100       18 $r = $_ unless defined($r);
576 9 100       20 $r = $_ ge $r ? $_ : $r;
577             }
578 4 50       14 ; defined($r) ? $r : "";
579             }
580              
581             sub XPATH_min {
582 9     9 0 707 my $r;
583 9         25 foreach (cast_objects_to_values(@_)) {
584 21 50       131 next unless /^\s*(-\s*)?(\d+(\.\d*)?|\.\d+)\s*$/;
585 21 100       40 $r = $_ unless defined($r);
586 21 100       52 $r = $_ < $r ? $_ : $r;
587             }
588             ;
589 9         32 return 0+$r;
590             }
591              
592             sub XPATH_strmin {
593 4     4 0 252 my $r;
594 4         9 foreach (cast_objects_to_values(@_)) {
595 9 100       19 $r = $_ unless defined($r);
596 9 100       18 $r = $_ le $r ? $_ : $r;
597             }
598 4 50       17 ; defined($r) ? $r : "";
599             }
600              
601             sub XPATH_sum {
602 9     9 0 680 my $r=0;
603 9         24 foreach (cast_objects_to_values(@_)) {
604 29         50 $r += $_;
605             }
606 9         23 ; $r;
607             }
608              
609             sub XPATH_join {
610 4     4 0 361 my $j=literal_value(shift @_);
611 4         10 join $j,cast_objects_to_values(@_);
612             }
613              
614             sub XPATH_serialize {
615 21     21 0 2211 my $result="";
616 21         63 foreach my $obj (@_) {
617 23 50 33     178 if (ref($obj) and
618             UNIVERSAL::isa($obj,'XML::LibXML::NodeList')) {
619 23         68 foreach my $node (@$obj) {
620 41         908 $result.=$node->toString();
621             }
622             } else {
623 0         0 $result.=literal_value($obj);
624             }
625             }
626 21         72 $result;
627             }
628              
629             sub XPATH_subst {
630 11 50 66 11 0 1049 die "Wrong number of arguments for function xsh:subst(string,regexp,replacement,[options])!\n" if (@_!=3 and @_!=4);
631 13     13   6201 use utf8;
  13         269  
  13         77  
632 11         38 my ($string,$regexp,$replace,$options)=@_;
633 11         32 $string=literal_value($string);
634 11         20 $regexp=literal_value($regexp);
635 11 50       32 return $string unless $regexp ne "";
636 11         18 $replace=literal_value($replace);
637 11         17 $options=literal_value($options);
638 11 50       52 die "Invalid options: $options (should only consist of 'egimsx')!\n"
639             unless ($options =~ /^[egimsx]*$/);
640 11         58 $replace =~ s{\\(.)|(/)|(\\)$}{\\$1$2$3}gs;
641 11         999 eval "\$string=~s/\$regexp/$replace/$options";
642 11         56 return $string;
643             }
644              
645             sub XPATH_parse {
646 13     13   1699 use utf8;
  13         70  
  13         92  
647 0     0 0 0 my $string=join '',map {literal_value($_)} @_;
  0         0  
648 0         0 my $dom=xsh_parse_string($string,'xml');
649 0 0       0 if ($dom) {
650 0         0 return XML::LibXML::NodeList->new($dom->childNodes());
651             } else {
652 0         0 return XML::LibXML::NodeList->new();
653             }
654             }
655              
656             sub XPATH_sprintf {
657 7 50   7 0 636 die "Wrong number of arguments for function xsh:sprintf(format-string,...)!\n" if (@_<1);
658 13     13   1155 use utf8;
  13         275  
  13         63  
659 7         20 my @args=map { literal_value($_) } @_;
  16         34  
660 7         81 return sprintf(shift(@args),@args);
661             }
662              
663             sub XPATH_current {
664 6 50   6 0 258 die "Wrong number of arguments for function xsh:current()!\n" if (@_);
665 6         12 my $ln = xsh_context_node();
666 6 50       20 return XML::LibXML::NodeList->new($ln ? $ln : ());
667             }
668              
669             sub XPATH_path {
670 5 50   5 0 319 die "Wrong number of arguments for function xsh:path(nodeset)!\n" if (@_!=1);
671 5 50 33     38 die "Wrong type of argument 1 for xsh:path(nodeset)!\n" unless (ref($_[0]) and UNIVERSAL::isa($_[0],'XML::LibXML::NodeList'));
672 5 50       20 return "" unless $_[0][0];
673             return
674 5         44 XML::LibXML::Literal->new(pwd($_[0][0]));
675             }
676              
677             sub XPATH_node_type {
678 1 50   1 0 72 die "Wrong number of arguments for function xsh:node-type(node-set)!\n" if (@_!=1);
679 1 50 33     13 die "Wrong type of argument 1 for xsh:node-type(node-set)!\n" unless (ref($_[0]) and UNIVERSAL::isa($_[0],'XML::LibXML::NodeList'));
680 1 50       61 return "" unless $_[0][0];
681             return
682 1         13 XML::LibXML::Literal->new(node_type($_[0][0]));
683             }
684              
685             sub XPATH_object_type {
686 0 0   0 0 0 die "Wrong number of arguments for function xsh:object-type(object)!\n" if (@_!=1);
687 0         0 my ($obj)=@_;
688 0         0 my $ret;
689 0 0       0 if (!ref($obj)) {
    0          
    0          
    0          
    0          
690 0         0 $ret = "string"
691             } elsif (UNIVERSAL::isa($obj,"XML::LibXML::NodeList")) {
692 0         0 $ret = "nodeset"
693             } elsif (UNIVERSAL::isa($obj,"XML::LibXML::Literal")) {
694 0         0 $ret = "string"
695             } elsif (UNIVERSAL::isa($obj,"XML::LibXML::Boolean")) {
696 0         0 $ret = "boolean"
697             } elsif (UNIVERSAL::isa($obj,"XML::LibXML::Number")) {
698 0         0 $ret = "float"
699             } else {
700 0         0 $ret = "unknown"
701             }
702 0         0 return XML::LibXML::Literal->new($ret);
703             }
704              
705             sub XPATH_evaluate {
706 0 0 0 0 0 0 die "Wrong number of arguments for function xsh:evaluate(string)!\n"
707             if ((@_==0) or (@_>4));
708 0         0 my ($xpath,$node,$size,$pos)=@_;
709 0         0 my $old_context;
710 0 0       0 if (@_>1) {
711 0         0 $old_context = _save_context();
712 0 0 0     0 die "Wrong type of argument 1 for xsh:evaluate(string,node?,size?,position?)!\n"
713             unless (ref($node) and UNIVERSAL::isa($node,'XML::LibXML::NodeList'));
714 0 0       0 if (@$node) {
715 0         0 _set_context([$node->[0],$size,$pos]);
716             } else {
717 0         0 return XML::LibXML::NodeList->new();
718             }
719             }
720 0 0       0 if ($xpath eq "") { return XML::LibXML::NodeList->new() }
  0         0  
721 0         0 my $val;
722 0         0 eval { $val = $_xpc->find($xpath) };
  0         0  
723 0 0       0 _set_context($old_context) if $old_context;
724 0 0       0 return defined($val) ? $val : XML::LibXML::NodeList->new();
725             }
726              
727             sub XPATH_map {
728 7 50   7 0 540 die "Wrong number of arguments for function xsh:map(nodeset,string)!\n"
729             if (@_!=2);
730 7 50 33     50 die "Wrong type of argument 1 for xsh:map(nodeset,string)!\n"
731             unless (ref($_[0]) and UNIVERSAL::isa($_[0],'XML::LibXML::NodeList'));
732 7         14 my ($nl,$xpath)=@_;
733 7         21 my $res = XML::LibXML::NodeList->new();
734 7 50 33     29 unless (@{$nl} and $xpath ne "") { return $res; }
  7         36  
  0         0  
735 7 50       14 return $res if $xpath eq "";
736             # my $xpc = clone_xpc();
737 7         8 my $res_el;
738 7 50       53 $_xpc->setContextSize(0+@{$nl}) if $_xpc->can('setContextSize');
  7         22  
739 7         10 my $pos=1;
740 7         10 foreach my $node (@{$nl}) {
  7         19  
741 9 50       88 $_xpc->setContextPosition($pos++) if $_xpc->can('setContextSize');
742 9         11 my $val;
743 9         12 eval { $val = $_xpc->find($xpath,$node); };
  9         31  
744 9 50       325 return XML::LibXML::NodeList->new() if $@;
745 9 50       21 next unless (ref($val));
746 9         25 push @$res,cast_value_to_objects($val,undef,1);
747             }
748 7         82 return $res;
749             }
750              
751             sub XPATH_match {
752 0 0 0 0 0 0 die "Wrong number of arguments for function xsh:match(string,regexp,options?)!\n" if (@_!=2 and @_!=3);
753 13     13   7230 use utf8;
  13         50  
  13         91  
754 0         0 my ($string,$regexp,$options)=@_;
755 0         0 $string=literal_value($string);
756 0         0 $regexp=literal_value($regexp);
757 0         0 $options=literal_value($options);
758              
759 0 0       0 die "Invalid options: $options (should only consist of 'cgimosx')!\n"
760             unless ($options =~ /^[cgimosx]*$/);
761 0         0 my @result = eval "\$string=~/\$regexp/$options";
762 0 0       0 die $@ if $@;
763 0         0 my $res = XML::LibXML::NodeList->new();
764 0         0 my $res_doc=XML::LibXML::Document->new();
765 0         0 my $res_el=$res_doc->createElementNS($XML::XSH2::xshNS,'xsh:result');
766 0         0 $res_doc->setDocumentElement($res_el);
767 0         0 my $el;
768 0         0 foreach my $str (@result) {
769 0         0 $el = $res_doc->createElementNS($XML::XSH2::xshNS,'xsh:string');
770 0         0 $el->appendText($str);
771 0         0 $res_el->appendChild($el);
772 0         0 push @$res,$el;
773             }
774 0         0 return $res;
775             }
776              
777             sub XPATH_split {
778 0 0   0 0 0 die "Wrong number of arguments for function xsh:split(regexp,string)!\n"
779             if (@_!=2);
780 0         0 my ($regexp,$string)=@_;
781 0         0 $regexp=literal_value($regexp);
782 0         0 $string=literal_value($string);
783 0         0 my $res = XML::LibXML::NodeList->new();
784 0         0 my $res_doc=XML::LibXML::Document->new();
785 0         0 my $res_el=$res_doc->createElementNS($XML::XSH2::xshNS,'xsh:result');
786 0         0 $res_doc->setDocumentElement($res_el);
787 0         0 my $el;
788 0         0 foreach my $str (split $regexp,$string) {
789 0         0 $el = $res_doc->createElementNS($XML::XSH2::xshNS,'xsh:string');
790 0         0 $el->appendText($str);
791 0         0 $res_el->appendChild($el);
792 0         0 push @$res,$el;
793             }
794 0         0 return $res;
795             }
796              
797             sub XPATH_new_attribute {
798 3 50 33 3 0 503 die "Wrong number of arguments for function xsh:new-attributes(string, string, [string, string,...])!\n"
799             unless (@_ and (scalar(@_) % 2 == 0));
800 3         15 my %attr=map { literal_value($_) } @_;
  10         23  
801 3         17 my $doc = $_xpc->getContextNode;
802 3 50 33     56 unless (ref($doc) and ref($doc = $doc->ownerDocument())) {
803 0         0 die "No context document\n";
804             }
805 3         37 return XML::LibXML::NodeList->new(map {$doc->createAttribute($_,$attr{$_})} keys %attr);
  5         80  
806             }
807              
808             sub XPATH_new_element {
809 2 50   2 0 340 die "Wrong number of arguments for function xsh:new-element(string, [string,string,...])!\n"
810             unless (scalar(@_)%2);
811 2         9 my ($name,%attrs)=map {literal_value($_)} @_;
  6         18  
812 2         12 my $doc = $_xpc->getContextNode;
813 2 50 33     33 unless (ref($doc) and ref($doc = $doc->ownerDocument())) {
814 0         0 die "No context document\n";
815             }
816 2         22 my $e = $doc->createElement($name);
817 2         13 foreach my $aname (keys %attrs) {
818 2         28 $e->setAttribute($aname,$attrs{$aname});
819             }
820 2         25 return XML::LibXML::NodeList->new($e);
821             }
822              
823             sub XPATH_new_element_ns {
824 1 50 33 1 0 201 die "Wrong number of arguments for function xsh:new-element-ns(string, string, [string,string])!\n"
825             unless (@_ and (scalar(@_)+1)%2);
826 1         6 my ($name,$ns,%attrs)=map {literal_value($_)} @_;
  6         11  
827 1         8 my $doc = $_xpc->getContextNode;
828 1 50 33     18 unless (ref($doc) and ref($doc = $doc->ownerDocument())) {
829 0         0 die "No context document\n";
830             }
831             # __debug("ns: $ns");
832 1         17 my $e=$doc->createElementNS("$ns",$name);
833             # my ($prefix,$name) = split ':',$name;
834             # my $e=XML::LibXML::Element->new($name);
835             # $e->setNamespace("$ns","$prefix",1);
836 1         7 foreach my $aname (keys %attrs) {
837 2         28 $e->setAttribute($aname,$attrs{$aname});
838             }
839 1         33 return XML::LibXML::NodeList->new($e);
840             }
841              
842              
843             sub XPATH_new_text {
844 1 50   1 0 116 die "Wrong number of arguments for function xsh:new-text(string)!\n"
845             if (@_!=1);
846 1         6 my $text=literal_value(shift);
847 1         7 my $doc = $_xpc->getContextNode;
848 1 50 33     18 unless (ref($doc) and ref($doc = $doc->ownerDocument())) {
849 0         0 die "No context document\n";
850             }
851 1         13 my $t=$doc->createTextNode($text);
852 1         10 return XML::LibXML::NodeList->new($t);
853             }
854              
855             sub XPATH_new_comment {
856 1 50   1 0 117 die "Wrong number of arguments for function xsh:new-comment(string)!\n"
857             if (@_!=1);
858 1         6 my $text=literal_value(shift);
859 1         5 my $doc = $_xpc->getContextNode;
860 1 50 33     18 unless (ref($doc) and ref($doc = $doc->ownerDocument())) {
861 0         0 die "No context document\n";
862             }
863 1         12 my $t=$doc->createComment($text);
864 1         10 return XML::LibXML::NodeList->new($t);
865             }
866              
867             sub XPATH_new_cdata {
868 2 50   2 0 231 die "Wrong number of arguments for function xsh:new-cdata(string)!\n"
869             if (@_!=1);
870 2         11 my $name=literal_value(shift);
871 2         10 my $doc = $_xpc->getContextNode;
872 2 50 33     35 unless (ref($doc) and ref($doc = $doc->ownerDocument())) {
873 0         0 die "No context document\n";
874             }
875 2         21 my $t=$doc->createCDATASection($name);
876 2         18 return XML::LibXML::NodeList->new($t);
877             }
878              
879             sub XPATH_new_pi {
880 1 50 33 1 0 156 die "Wrong number of arguments for function xsh:new-pi(string,[ string])!\n"
881             if (!@_ or @_>2);
882 1         6 my ($name,$value)=map { literal_value($_) } @_;
  2         7  
883 1         5 my $doc = $_xpc->getContextNode;
884 1 50 33     18 unless (ref($doc) and ref($doc = $doc->ownerDocument())) {
885 0         0 die "No context document\n";
886             }
887 1         14 my $pi = $doc->createPI($name => $value);
888 1         9 return XML::LibXML::NodeList->new($pi);
889             }
890              
891             sub XPATH_new_chunk {
892 0 0   0 0 0 die "Wrong number of arguments for function xsh:new-chunk(string,[ string])!\n"
893             if (@_!=1);
894 0         0 return XPATH_parse(@_);
895             }
896              
897             sub XPATH_times {
898 0 0   0 0 0 die "Wrong number of arguments for function xsh:times(string,float)!\n"
899             if (@_!=2);
900 0         0 my ($string,$times)=@_;
901 0         0 $times=literal_value($times);
902 0         0 $string=literal_value($string);
903 0         0 return XML::LibXML::Literal->new($times x $string);
904             }
905              
906             sub XPATH_if {
907 0 0   0 0 0 die "Wrong number of arguments for function xsh:if(object,object,object)!\n"
908             if (@_!=3);
909 0         0 my ($test, $if, $else)=@_;
910 0 0 0     0 if (ref($test) and
      0        
      0        
911             (UNIVERSAL::isa($test,'XML::LibXML::NodeList') and @$test
912             or $test->to_literal->value)
913             or $test) {
914 0         0 return $if;
915             } else {
916 0         0 return $else;
917             }
918             }
919              
920             sub XPATH_id2 {
921 0 0   0 0 0 die "Wrong number of arguments for function xsh:id2(object,string)!\n"
922             if (@_!=2);
923 0         0 my ($nl, $id)=@_;
924 0 0 0     0 die "Wrong type of argument 1 for function xsh:id2(object,string)!\n"
925             if (!ref($nl) or not UNIVERSAL::isa($nl,"XML::LibXML::NodeList"));
926 0 0       0 die "Argument 2 for function xsh:id2(object,string) isn't a valid qname!\n"
927             if ($id =~ /\'/);
928 0         0 my $res=XML::LibXML::NodeList->new();
929 0 0       0 if ($nl->[0]) {
930 0         0 push @$res, $nl->[0]->findnodes("id('".$id."')");
931             }
932 0         0 return $res;
933             }
934              
935             sub XPATH_lineno {
936 0 0   0 0 0 die "Wrong number of arguments for function xsh:lineno(node-set)!\n"
937             if (@_!=1);
938 0         0 my ($nl, $id)=@_;
939 0 0 0     0 die "Wrong type of argument 1 for function xsh:lineno(node-set)!\n"
940             if (!ref($nl) or not UNIVERSAL::isa($nl,"XML::LibXML::NodeList"));
941 0         0 my $res=-1;
942 0 0       0 if ($nl->[0]) {
943 0         0 $res=$nl->[0]->line_number;
944             }
945 0         0 return XML::LibXML::Number->new($res);
946             }
947              
948             sub XPATH_document {
949 0 0   0 0 0 die "Wrong number of arguments for function xsh:document(string)!\n"
950             if (@_!=1);
951 0         0 my $URI = shift;
952 0         0 my $abs;
953 0         0 my @files = _files();
954 0         0 for my $f (@files) {
955 0 0       0 return XML::LibXML::NodeList->new($f->[0])
956             if ($f->[0]->URI eq $URI);
957             }
958 0 0       0 unless (_is_absolute($URI)) {
959 0         0 $URI = File::Spec->rel2abs(_tilde_expand($URI));
960 0         0 for my $f (@files) {
961 0 0       0 return XML::LibXML::NodeList->new($f->[0])
962             if ($f->[0]->URI eq $URI);
963             }
964             }
965 0         0 my $is_URL = _is_url($URI);
966 0         0 for my $f (@files) {
967 0         0 my $f_URI = $f->[0]->URI;
968 0 0 0     0 return XML::LibXML::NodeList->new($f->[0])
      0        
      0        
      0        
      0        
969             if (_is_url($f_URI) and !$is_URL and $f_URI eq 'file://'.$URI
970             or
971             !_is_url($f_URI) and $is_URL and $URI eq 'file://'.$f_URI);
972             }
973              
974 0         0 return XML::LibXML::NodeList->new();
975             }
976              
977             sub XPATH_documents {
978 0 0   0 0 0 die "Wrong number of arguments for function xsh:documents()!\n"
979             if (@_!=0);
980 0         0 my $res = XML::LibXML::NodeList->new();
981 0         0 for my $f (_files()) {
982 0         0 $res->push($f->[0]);
983             }
984 0         0 return $res;
985             }
986              
987             sub XPATH_lookup {
988 0 0   0 0 0 die "Wrong number of arguments for function xsh:lookup(string,string)!\n"
989             if (@_!=2);
990 0         0 my $name = shift;
991 0         0 my $key = shift;
992 0         0 my $res;
993 0         0 $name=~s/^\$//;
994 13     13   16367 no strict 'refs';
  13         269  
  13         4028  
995 0         0 my $lex = lex_var($name);
996 0 0       0 if ($lex) {
    0          
997 0         0 $res = $$lex
998 0         0 } elsif (defined(${"XML::XSH2::Map::$name"})) {
999 0         0 $res = ${"XML::XSH2::Map::$name"};
  0         0  
1000             } else {
1001 0         0 die "xsh:lookup(): variable '\$$name' not defined\n";
1002             }
1003            
1004 0 0       0 if (ref($res) eq 'HASH') {
1005 0         0 my $val = $res->{to_literal($key)};
1006 0 0       0 if (defined($val)) {
1007 0         0 return $val;
1008             } else {
1009 0         0 return XML::LibXML::NodeList->new();
1010             }
1011             } else {
1012 0         0 return XML::LibXML::NodeList->new();
1013             }
1014             }
1015              
1016             sub XPATH_span {
1017 0 0   0 0 0 die "Wrong number of arguments for function xsh:span(node-set,node-set)!\n"
1018             if (@_!=2);
1019             # the first argument is a start node and
1020             # the second is an end node;
1021             # only the first argument is taken from each node-set!
1022             #
1023             # returns span of sibling nodes "between" them (inclusively).
1024             # it is an error if the start and end nodes are not siblings.
1025              
1026 0         0 my ($start,$end)=@_;
1027 0         0 for my $nl ($start,$end) {
1028 0 0 0     0 die "Wrong type of argument in function xsh:span(node-set,node-set)!\n"
1029             if (!ref($nl) or not UNIVERSAL::isa($nl,"XML::LibXML::NodeList"));
1030             }
1031 0         0 ($start,$end) = map { $_->[0] } ($start,$end);
  0         0  
1032 0 0 0     0 if ($start and $end) {
1033 0 0       0 if ($start->parentNode->isSameNode($end->parentNode)) {
1034 0         0 my @nodes = ();
1035 0         0 do {{
1036 0         0 push @nodes, $start;
  0         0  
1037 0 0       0 if ($start->isSameNode($end)) {
1038 0         0 return XML::LibXML::NodeList->new_from_ref(\@nodes,1);
1039             }
1040 0         0 $start = $start->nextSibling();
1041             }} while ($start);
1042 0         0 return XML::LibXML::NodeList->new();
1043             } else {
1044 0         0 die "Start node and end node are not siblings at xsh:span(node-set,node-set)!\n"
1045             }
1046             } else {
1047 0         0 return XML::LibXML::NodeList->new();
1048             }
1049             }
1050              
1051             sub XPATH_context {
1052 0 0 0 0 0 0 die "Wrong number of arguments for function xsh:context(node-set,float,float)!\n"
1053             if (@_<2 or @_>3);
1054             # returns a span of nodes consisting of a given number of nodes
1055             # before the given context node, the context node itself and a given number of nodes
1056             # following the context node
1057              
1058             # $context ... preceding-sibling::node()[position()<$before] | . | following-sibling::node()[position<$after]
1059 0         0 my ($context,$before,$after)=@_;
1060 0 0 0     0 die "Wrong type of argument in function xsh:context(node-set,float,float)!\n"
1061             if (!ref($context) or not UNIVERSAL::isa($context,"XML::LibXML::NodeList"));
1062 0         0 $before = int($before);
1063 0 0       0 $after = defined ($after) ? $before : int($after);
1064 0         0 return scalar($_xpc->findnodes("preceding-sibling::node()[position()<$before] | . | following-sibling::node()[position()<$after]",$context->[0]));
1065             }
1066              
1067             # ===================== END OF XPATH EXT FUNC ================
1068              
1069             sub get_flags_as_vars {
1070 13     13   76 no strict qw(refs);
  13         50  
  13         330  
1071 13     13   5211 use Data::Dumper;
  13         54033  
  13         1007  
1072 0     0 0 0 return Data::Dumper->Dump([map eval, @PARAM_VARS],\@PARAM_VARS);
1073             }
1074              
1075             sub list_flags {
1076 0     0 0 0 my ($opts) = @_;
1077 0         0 $opts = _ev_opts($opts);
1078 0 0       0 if ($opts->{variables}) {
1079 13     13   74 no strict qw(refs);
  13         522  
  13         352  
1080 13     13   57 use Data::Dumper;
  13         257  
  13         9076  
1081 0         0 out(get_flags_as_vars());
1082             } else {
1083 0   0     0 out("validation ".(get_validation() or "0").";\n");
1084 0   0     0 out("recovering ".(get_recovering() or "0").";\n");
1085 0   0     0 out("parser_expands_entities ".(get_expand_entities() or "0").";\n");
1086 0   0     0 out("parser_expands_xinclude ".(get_expand_xinclude() or "0").";\n");
1087 0   0     0 out("keep_blanks ".(get_keep_blanks() or "0").";\n");
1088 0   0     0 out("pedantic_parser ".(get_pedantic_parser() or "0").";\n");
1089 0   0     0 out("load_ext_dtd ".(get_load_ext_dtd() or "0").";\n");
1090 0   0     0 out("complete_attributes ".(get_complete_attributes() or "0").";\n");
1091 0   0     0 out("indent ".(get_indent() or "0").";\n");
1092 0   0     0 out("empty_tags ".(get_empty_tags() or "0").";\n");
1093 0   0     0 out("skip_dtd ".(get_skip_dtd() or "0").";\n");
1094 0 0       0 out(((get_backups() ? "backups" : "nobackups"),";\n"));
1095 0 0       0 out((($QUIET ? "quiet" : "verbose"),";\n"));
1096 0 0       0 out((($DEBUG ? "debug" : "nodebug"),";\n"));
1097 0 0       0 out((($TEST_MODE ? "run-mode" : "test-mode"),";\n"));
1098 0   0     0 out("switch_to_new_documents ".(get_cdonopen() or "0").";\n");
1099 0         0 out("encoding '$ENCODING';\n");
1100 0         0 out("query_encoding '$QUERY_ENCODING';\n");
1101 0   0     0 out("xpath_completion ".(get_xpath_completion() or "0").";\n");
1102 0         0 out("xpath_axis_completion \'".get_xpath_axis_completion()."';\n");
1103             }
1104             }
1105              
1106             sub toUTF8 {
1107             # encode/decode from UTF8 returns undef if string not marked as utf8
1108             # by perl (for example ascii)
1109             # return $_[1];
1110 383     383 0 763 my $res=eval { encodeToUTF8($_[0],$_[1]) };
  383         2069  
1111 383 50       30653 if ($@ =~ /^SIGINT/) {
1112 0         0 die $@
1113             } else {
1114 383         780 undef $@;
1115             }
1116 383 100       1996 return defined($res) ? $res : $_[1];
1117             }
1118              
1119             sub fromUTF8 {
1120             # encode/decode from UTF8 returns undef if string not marked as utf8
1121             # by perl (for example ascii)
1122             # return $_[1];
1123 214     214 0 487 my $res=eval { decodeFromUTF8($_[0],$_[1]) };
  214         1118  
1124 214 50       9454 if ($@ =~ /^SIGINT/) {
1125 0         0 die $@
1126             } else {
1127 214         382 undef $@;
1128             }
1129 214 100       8457 return defined($res) ? $res : $_[1];
1130             }
1131              
1132             # return true if given string is a XSH command name
1133             sub is_command {
1134 10     10 0 38 my ($test)=@_;
1135 10         37 foreach my $cmd (@XML::XSH2::CompletionList::XSH_COMMANDS) {
1136 1860 50       2927 return 1 if $cmd eq $test;
1137             }
1138 10         90 return 0;
1139             }
1140              
1141             # set current script name
1142             sub xsh_set_script {
1143 0     0 0 0 $SCRIPT=$_[0];
1144             }
1145              
1146             sub benchtime {
1147 0     0 0 0 my ($t0,$t1)=@_;
1148 0         0 Benchmark::timestr(Benchmark::timediff($t0,$t1));
1149             }
1150              
1151             # evaluate a XSH command
1152             sub xsh {
1153 367 50   367 0 39963 print STDERR "Benchmark: running script $SCRIPT\n" if $BENCHMARK;
1154 367 50       813 require Benchmark if $BENCHMARK;
1155 367         703 my ($t0,$t1);
1156 367 50       844 $t0 = Benchmark->new() if $BENCHMARK;
1157 367 100       1242 unless (ref($_xsh)) {
1158 8         33 xsh_init();
1159 8         36 xsh_rd_parser_init();
1160             }
1161 367 50       1166 $t1 = Benchmark->new() if $BENCHMARK;
1162 367 50       1101 print STDERR "Benchmark: init xsh took:",benchtime($t1,$t0),"\n" if $BENCHMARK;
1163 367 50       1164 if (ref($_xsh)) {
1164 367         1537 my $code=join "",map toUTF8($QUERY_ENCODING,$_),@_;
1165 367         1273 return run($code);
1166             } else {
1167 0         0 die "XSH init failed!\n";
1168             }
1169             }
1170              
1171             sub run {
1172 367     367 0 829 my ($code) = @_;
1173 367 100       2719 return 1 if ($code=~/^\s*$/);
1174 366 50       1110 require Benchmark if $BENCHMARK;
1175 366 50       1088 my $t0 = Benchmark->new() if $BENCHMARK;
1176 366         3375 my $pt = $_xsh->startrule($code);
1177 366 50       1093 my $t1 = Benchmark->new() if $BENCHMARK;
1178 366 50       975 print STDERR "Benchmark: xsh parsing took:",benchtime($t1,$t0),"\n" if $BENCHMARK;
1179              
1180             # __debug "Post processing parse tree";
1181 366 50       875 $t0 = Benchmark->new() if $BENCHMARK;
1182 366         1114 post_process_parse_tree($pt);
1183 366 50       938 $t1 = Benchmark->new() if $BENCHMARK;
1184 366 50       854 print STDERR "Benchmark: compile took:",benchtime($t1,$t0),"\n" if $BENCHMARK;
1185 366 50       791 dump_parse_tree($pt) if defined $DUMP;
1186 366 50       748 $t0 = Benchmark->new() if $BENCHMARK;
1187 366         1106 my $result=run_commands($pt,1);
1188 366 50       952 $t1 = Benchmark->new() if $BENCHMARK;
1189 366 50       817 print STDERR "Benchmark: execution took:",benchtime($t1,$t0),"\n" if $BENCHMARK;
1190 366         3298 return $result;
1191             }
1192              
1193             sub dump_parse_tree {
1194 0     0 0 0 my ($pt) = shift;
1195 13     13   105 use Data::Dumper;
  13         56  
  13         24161  
1196 0         0 local $Data::Dumper::Purity=1;
1197            
1198 0         0 my $dump = '';
1199              
1200 0 0       0 unless ($DUMP_APPEND) {
1201 0         0 $dump .= <<"EOS";
1202             require XML::XSH2;
1203             XML::XSH2::Functions->VERSION( '$VERSION' );
1204              
1205             EOS
1206              
1207 0         0 $dump .= <<'EOS';
1208             {
1209             package XML::XSH2::Functions;
1210              
1211             # initialize context
1212             xsh_init();
1213              
1214             # prepare ARGV
1215             @XML::XSH2::Map::ARGV=@ARGV;
1216             for (@ARGV,@XML::XSH2::Map::ARGV) {
1217             $_=toUTF8($QUERY_ENCODING,$_)
1218             }
1219             # XPath variant of perlish {@ARGV} ($ARGV[1] is the first arg though)
1220             $XML::XSH2::Map::ARGV = XML::LibXML::NodeList->new(
1221             map { cast_value_to_objects($_) } @XML::XSH2::Map::ARGV
1222             );
1223              
1224             EOS
1225             {
1226 0         0 local $TEST_MODE;
  0         0  
1227 0         0 $dump.= get_flags_as_vars();
1228             }
1229             } else {
1230 0         0 $dump.=<<'EOS'
1231             {
1232             package XML::XSH2::Functions;
1233             EOS
1234             }
1235 0         0 $dump.= "# script $SCRIPT\n\n";
1236 0         0 local $Data::Dumper::Deparse=1;
1237 0         0 $dump.= Data::Dumper->Dump([$pt],['parse_tree']);
1238 0         0 $dump.= "\nXML::XSH2::Functions::run_commands(\$parse_tree);\n";
1239 0         0 $dump.= "};\n\n";
1240 0 0       0 if (ref($DUMP) eq 'SCALAR') {
1241 0 0       0 if ($DUMP_APPEND) {
1242 0         0 $$DUMP.=$dump;
1243             } else {
1244 0         0 $$DUMP=$dump;
1245             }
1246             } else {
1247 0 0       0 print STDERR "Saving compiled '$SCRIPT' to '$DUMP'\n" unless $QUIET;
1248 0 0 0     0 open my $f, ($DUMP_APPEND ? '>>' : '>'), $DUMP || die "Can't dump parse tree to '$DUMP': $!";
1249 0         0 require Config;
1250 0 0       0 print {$f} "$Config::Config{startperl}\n" unless $DUMP_APPEND;
  0         0  
1251 0         0 print {$f} $dump;
  0         0  
1252 0         0 close $f;
1253 0 0       0 chmod 0755, $DUMP unless $DUMP_APPEND;
1254             }
1255 0         0 $DUMP_APPEND = 1;
1256             }
1257              
1258             sub post_process_parse_tree {
1259 6007     6007 0 8681 my ($pt)=@_;
1260             # __debug "[ $pt";
1261 6007 100       15021 if (ref($pt) eq 'XML::XSH2::Command') {
    100          
1262             # __debug "COMMAND: @$pt\n";
1263 669         2114 my ($line,$column,$offset,$script,$cmd,@args)=@$pt;
1264 669 50       1456 unless (ref($cmd)) {
1265 669         2002 my $spec = $COMMANDS{$cmd};
1266 669 100 100     2522 $spec = $COMMANDS{$spec} if ($spec and !ref($spec));
1267 669 100       1416 if ($spec) {
1268 540         1694 my ($func,$minargs,$maxargs,$optspec,@extraargs)=@$spec;
1269 540         872 my @opts;
1270             # print STDERR ("matched $cmd\n");
1271 540         1227 while (@args) {
1272 540 100 100     2496 if ($optspec and $args[0]=~/^--(.*)$|^:(.)$/) {
1273 35 100       170 my $opt = defined($1) ? $1 : $optspec->{$2}; # resolve short opt
1274 35 50       117 if (exists($optspec->{$opt})) {
1275 35         69 shift(@args);
1276 35 100       128 if ($optspec->{$opt} ne '') {
1277 22         84 push @opts, $optspec->{$opt}.'/'.$opt;
1278 22         68 push @opts,shift(@args);
1279             } else {
1280 13         72 push @opts, '/'.$opt,1;
1281             }
1282             } else {
1283 0         0 die "$script: Compile error: unknown option '$args[0]' for command '$cmd' at line $line column $column offset $offset\n";
1284             }
1285             } else {
1286 505         851 last;
1287             }
1288             }
1289 540 50 66     2454 if (@args<$minargs) {
    50          
1290 0         0 die "$script: Compile error: too few arguments (have ",$#args+1,", expect $minargs) for command '$cmd' at line $line column $column offset $offset\n";
1291             } elsif (defined($maxargs) and @args>$maxargs) {
1292 0         0 die "$script: Compile error: too many arguments (have ",$#args+1,", expect $maxargs) for command '$cmd' at line $line column $column offset $offset\n";
1293             }
1294 540         1037 foreach (@args) {
1295 725 100       1648 if (ref eq 'ARRAY') {
1296 1         4 post_process_parse_tree($_);
1297             }
1298             }
1299 540         3818 @$pt=($line,$column,$offset,$script,$func,\@opts,@args,@extraargs);
1300             } else {
1301             # print STDERR ("$cmd is a sub call?\n");
1302 129         761 @$pt=($line,$column,$offset,$script,'call',{},1,$cmd,@args);
1303             }
1304             }
1305             } elsif (ref($pt) eq 'ARRAY') {
1306             # __debug "Processing array @$pt\n";
1307 1439         3068 for(my $i=0;$i<=$#$pt;$i++) {
1308 5432 50       10790 post_process_parse_tree($pt->[$i]) if ref($pt);
1309             }
1310             }
1311             # __debug "]";
1312             }
1313              
1314             # setup output stream
1315             sub xsh_set_output {
1316 0     0 0 0 $OUT=$_[0];
1317 0         0 binmode $OUT;
1318 0         0 binmode $OUT, ':bytes';
1319 0 0       0 open STDOUT, ">&", $OUT or warn "Can't dup STDOUT\n";
1320 0         0 select $OUT;
1321 0         0 return 1;
1322             }
1323              
1324             # get output stream
1325             sub xsh_get_output {
1326 0     0 0 0 return $OUT;
1327             }
1328              
1329             sub cast_objects_to_values {
1330             return map {
1331 282 100       629 if (ref($_)) {
1332 74 50       433 UNIVERSAL::can($_,'textContent') ? $_->textContent() : $_->value();
1333 208         822 } else { $_ }
1334             } map {
1335 220 100   220 0 531 UNIVERSAL::isa($_,'XML::LibXML::NodeList') ? @$_ : $_;
  245         2068  
1336             } @_;
1337             }
1338              
1339             sub create_xsh_result_element {
1340 12     12 0 71 my $res_doc=XML::LibXML::Document->new();
1341 12         103 my $res_el=$res_doc->createElementNS($XML::XSH2::xshNS,'xsh:result');
1342 12         58 $res_doc->setDocumentElement($res_el);
1343 12         168 return $res_el;
1344             }
1345              
1346             sub cast_value_to_objects {
1347 286     286 0 632 my ($val, $res_el, $wrap)=@_;
1348 286 100       874 if (UNIVERSAL::isa($val,"XML::LibXML::NodeList")) {
    100          
1349 266         1226 return @$val;
1350             } elsif (UNIVERSAL::isa($val,"XML::LibXML::Node")) {
1351 8         15 return ($val);
1352             }
1353 12 50       40 $res_el = create_xsh_result_element() unless (ref($res_el));
1354 12         52 my $res_doc = $res_el->ownerDocument;
1355 12         29 my $el;
1356             my $res;
1357 12 100       43 if (!ref($val)) {
    100          
    50          
    50          
    0          
1358 7 100       40 if ($val =~ /^\s*[+-]?(\d+(\.\d*)?|\.\d+)\s*$/) {
1359 6         38 $el = $res_doc->createElementNS($XML::XSH2::xshNS,'xsh:number');
1360             } else {
1361 1         7 $el = $res_doc->createElementNS($XML::XSH2::xshNS,'xsh:string');
1362             }
1363 7         34 $el->appendText($val);
1364 7         27 $res = $el->firstChild;
1365             } elsif (UNIVERSAL::isa($val,"XML::LibXML::Literal")) {
1366 4         19 $el = $res_doc->createElementNS($XML::XSH2::xshNS,'xsh:string');
1367 4         10 $el->appendText($val->value);
1368 4         55 $res = $el->firstChild;
1369             } elsif (UNIVERSAL::isa($val,"XML::LibXML::Boolean")) {
1370 0         0 $el = $res_doc->createElementNS($XML::XSH2::xshNS,'xsh:boolean');
1371 0 0       0 $el->appendText($val ? 'true' : '');
1372 0         0 $res = $el->firstChild;
1373             } elsif (UNIVERSAL::isa($val,"XML::LibXML::Number")) {
1374 1         6 $el = $res_doc->createElementNS($XML::XSH2::xshNS,'xsh:number');
1375 1         11 $el->appendText($val->value);
1376 1         12 $res = $el->firstChild;
1377             } elsif (ref($val) eq 'ARRAY') {
1378 0         0 $el = $res_doc->createElementNS($XML::XSH2::xshNS,'xsh:array');
1379 0         0 map { cast_value_to_objects($_,$el) } @$val;
  0         0  
1380 0         0 $res=$el;
1381             } else {
1382 0         0 die("don't know how to cast object '$val' to nodeset");
1383 0         0 return ();
1384             }
1385 12         80 $res_el->appendChild($el);
1386 12 100       42 return $wrap ? $el : $res;
1387             }
1388              
1389             sub expr {
1390 2     2 0 6 my $opts = shift;
1391 2         6 &_ev;
1392             }
1393              
1394             # evaluate given XPath or Perl expression
1395             sub _ev {
1396 1532     1532   2947 my ($exp,$map,$in_place)=@_;
1397 1532 100       3068 return undef unless defined $exp;
1398 1529 100       5518 utf8::upgrade($exp) unless ref($exp);
1399 1529 100       10726 if (ref($exp) eq 'ARRAY') {
    100          
    100          
    100          
    50          
1400 3         12 return run_commands($exp,0,1);
1401             } elsif ($exp =~ /^<<(.)/) {
1402             # inline document
1403 1 50       8 if ($1 eq "{") {
    50          
    50          
1404 0         0 return perl_eval($',$map,$in_place);
1405             } elsif ($1 eq "(") {
1406 0         0 my $ret = eval { $_xpc->find(_expand($')); };
  0         0  
1407 0         0 _check_err($@,1,1);
1408 0 0 0     0 if (ref($ret) and UNIVERSAL::isa($ret,'XML::LibXML::Literal')) {
1409 0         0 return $ret->value;
1410             } else {
1411 0         0 return $ret;
1412             }
1413             } elsif ($1 eq "'") {
1414 0         0 return $';
1415             } else {
1416 1         3 return _expand($');
1417             }
1418             } elsif ($exp =~ /^(?:\d*\.\d+|\d+)$/) { # a number/float literal
1419 81         322 return 0+$exp;
1420             } elsif ($exp =~ /^{/) { # a perl expression
1421 89         306 return perl_eval($exp,$map,$in_place);
1422             } elsif ($exp eq "") { # empty
1423 0         0 return "";
1424             } else { # an xpath expression
1425 1355         2208 my $ret = eval { $_xpc->find(_expand($exp)); };
  1355         3208  
1426 1355         57463 _check_err($@,1,1);
1427 1355 100 66     7872 if (ref($ret) and UNIVERSAL::isa($ret,'XML::LibXML::Literal')) {
1428 624         1977 return $ret->value;
1429             } else {
1430 731         1972 return $ret;
1431             }
1432             }
1433             }
1434              
1435             # Evaluate given XPath or Perl expression to a node-list. Evaluate the
1436             # expression using _ev. If the result is a plain scalar string, it is
1437             # re-evaluated as XPath. If the result is a node or node-list object
1438             # or a perl array containing nodes, it is cast to node-list and
1439             # returned. Otherwise an error is reported.
1440              
1441             sub _ev_nodelist {
1442 267     267   836 my @res = map { cast_value_to_objects($_) } _ev($_[0]);
  277         1060  
1443 267 100       880 if (wantarray) {
1444 13         43 return @res;
1445             } else {
1446 254         818 XML::LibXML::NodeList->new(@res);
1447             }
1448             }
1449              
1450             sub _ev_list {
1451 1     1   2 my $exp = $_[0];
1452 1 50       5 if (ref($exp) eq 'ARRAY') {
1453 0         0 $exp = run_commands($exp, 0, 1);
1454 1 50       7 } if ($exp =~ /^<<(.)/) {
    50          
1455             # inline document
1456 0 0       0 if ($1 eq "{") {
1457 0         0 return perl_eval($');
1458             } else {
1459 0         0 $exp = $';
1460             }
1461             } elsif ($exp =~ /^{/) {
1462 1         4 return (perl_eval($exp));
1463             } else {
1464 0         0 $exp = _expand($exp);
1465             }
1466 0         0 my $val = eval { $_xpc->find($exp); };
  0         0  
1467 0         0 _check_err($@,1,1);
1468 0 0       0 if (UNIVERSAL::isa($val,"XML::LibXML::NodeList")) {
    0          
1469 0         0 return @$val;
1470             } elsif (UNIVERSAL::isa($val,"XML::LibXML::Node")) {
1471 0         0 return ($val);
1472             }
1473              
1474 0         0 return ();
1475             }
1476              
1477              
1478             # evaluate given XPath or Perl expression and return the text content
1479             # of the result
1480             sub _ev_literal {
1481 254     254   620 my ($exp,$map,$in_place)=@_;
1482 254 100       755 return "" if $exp eq "";
1483 226         576 my $val = _ev($exp,$map,$in_place);
1484 226         1599 return to_literal($val);
1485             }
1486              
1487             sub to_literal {
1488 242     242 0 533 my ($obj)=@_;
1489 242 100       634 if (!ref($obj)) {
1490 211         375 my $r=$obj;
1491 211         530 utf8::upgrade($r);
1492 211         676 return $r;
1493             } else {
1494 31 50       257 if (UNIVERSAL::isa($obj,'XML::LibXML::NodeList')) {
    100          
    50          
    50          
    50          
1495 0 0       0 if (wantarray) {
    0          
1496 0         0 return map { literal_value($_->to_literal) } @$obj;
  0         0  
1497             } elsif ($obj->[0]) {
1498 0         0 return literal_value($obj->[0]->to_literal);
1499             } else {
1500 0         0 return '';
1501             }
1502             } elsif (UNIVERSAL::isa($obj,'XML::LibXML::Element')) {
1503 8         50 return $obj->textContent();
1504             } elsif (UNIVERSAL::isa($obj,'XML::LibXML::Attr')) {
1505 0         0 return $obj->getValue();
1506             } elsif (UNIVERSAL::isa($obj,'XML::LibXML::Node')) {
1507 0         0 return $obj->getData();
1508             } elsif(ref($obj)=~/^XML::LibXML/) {
1509 23         60 return literal_value($obj);
1510             } else {
1511 0         0 my $r = "$obj";
1512 0         0 utf8::upgrade($r);
1513 0         0 return $r;
1514             }
1515             }
1516             }
1517              
1518             # evaluate given XPath or Perl expression and return the text content
1519             # of the result
1520             sub _ev_count {
1521 318     318   636 my ($exp)=@_;
1522 318 50       762 return "" if $exp eq "";
1523 318         727 my $result = _ev($exp);
1524 318 100       870 if (!ref($result)) {
1525 50         191 return $result;
1526             } else {
1527 268 100       2090 if (UNIVERSAL::isa($result,'XML::LibXML::NodeList')) {
    50          
    50          
1528 61         197 return $result->size();
1529             } elsif (UNIVERSAL::isa($result,'XML::LibXML::Node')) {
1530 0         0 return 1;
1531             } elsif(ref($result)=~/^XML::LibXML/) {
1532 207         617 return literal_value($result);
1533             }
1534             }
1535             }
1536              
1537              
1538             # evaluate given expression to obtain a node-set, take
1539             # and return the owner document of the first node
1540              
1541             sub _ev_doc {
1542 5     5   29 my ($exp)=@_;
1543 5 100       65 $exp = "." if $exp eq "";
1544 5         40 my ($node)=_ev_nodelist($exp)->pop();
1545 5 50       125 if ($node) {
1546 5         94 return $_xml_module->owner_document($node);
1547             } else {
1548 0         0 _warn("Expression '$exp' returns no node");
1549             }
1550             }
1551              
1552             sub _doc {
1553 0     0   0 my ($obj)=@_;
1554 0         0 my ($node) = map { cast_value_to_objects($_) } $obj;
  0         0  
1555 0 0       0 if ($node) {
1556 0         0 return $_xml_module->owner_document($node);
1557             } else {
1558 0         0 _warn("Cannot cast object to a document");
1559             }
1560             }
1561              
1562             sub _ev_string {
1563 247     247   657 my ($exp,$map,$in_place)=@_;
1564 247 100       1510 if ($exp=~/^\s*(?:&|<<|{)|[\'\"\[\]\(\)\@]|::|\$/) {
1565 140         456 return _ev_literal($exp,$map,$in_place);
1566             } else {
1567 107         373 return _expand($exp);
1568             }
1569             }
1570              
1571             sub xsh_parse_string {
1572 53   66 53 0 193 my $format=$_[1] || $DEFAULT_FORMAT;
1573 53         107 local $VALIDATION=0;
1574 53 50       156 if ($format eq 'xml') {
    0          
    0          
1575 53         81 my $xmldecl;
1576 53 100       224 $xmldecl="" unless $_[0]=~/^\s*\<\?xml /;
1577 53         419 return $_xml_module->parse_string($_parser,$xmldecl.$_[0]);
1578             } elsif ($format eq 'html') {
1579 0         0 return $_xml_module->parse_html_string($_parser,$_[0]);
1580             } elsif ($format eq 'docbook') {
1581 0         0 return $_xml_module->parse_sgml_string($_parser,$_[0]);
1582             }
1583             }
1584              
1585             sub xsh_xml_parser {
1586 0 0   0 0 0 xsh_init() unless ref($_parser);
1587 0         0 return $_parser;
1588             }
1589              
1590             # store a pointer to an XSH-Grammar parser
1591             sub xsh_set_parser {
1592 0     0 0 0 $_xsh=$_[0];
1593 0         0 return 1;
1594             }
1595              
1596             # print version info
1597             sub print_version {
1598 0     0 0 0 my $opts = shift;
1599 0         0 out("Main program: $::VERSION $::REVISION\n");
1600 0         0 out("XML::XSH2::Functions: $VERSION $REVISION\n");
1601 0         0 out("XML::LibXML: $XML::LibXML::VERSION\n");
1602             # out($_xml_module->module(),"\t",$_xml_module->version(),"\n");
1603 0 0       0 out("XML::LibXSLT $XML::LibXSLT::VERSION\n")
1604             if defined($XML::LibXSLT::VERSION);
1605 0 0       0 out("XML::LibXML::XPathContext $XML::LibXML::XPathContext::VERSION\n")
1606             if defined($XML::LibXML::XPathContext::VERSION);
1607 0         0 return 1;
1608             }
1609              
1610             # print a list of all open files
1611             sub _files {
1612             # out(map { "$_ = $_files{$_}\n" } sort keys %_files);
1613 1     1   4 my @ret;
1614 13     13   85 no strict 'refs';
  13         253  
  13         3437  
1615             my %seen;
1616 1         3 foreach my $var (keys %{"XML::XSH2::Map::"}) {
  1         120  
1617 53         61 my $value = ${"XML::XSH2::Map::".$var};
  53         224  
1618 53 100       116 if (ref($value)) {
1619 4 50 33     28 $value = $value->[0] if (UNIVERSAL::isa($value,'XML::LibXML::NodeList')
1620             and $value->size()==1);
1621 4 50 66     38 if (UNIVERSAL::isa($value,'XML::LibXML::Node') and
      66        
1622             $_xml_module->is_document($value) and !exists($seen{$$value})) {
1623 2         26 push @ret, [$value, $var];
1624 2         16 $seen{$$value}=undef;
1625             }
1626             }
1627             }
1628 1         14 my $cur_doc = $_xml_module->owner_document(xsh_context_node());
1629 1 50       6 if (!exists($seen{$$cur_doc})) {
1630 0         0 push @ret, [$cur_doc, undef];
1631             }
1632 1         9 return @ret;
1633             }
1634              
1635              
1636             sub files {
1637 1     1 0 4 my $opts = shift;
1638 1         4 for my $f (_files) {
1639 2 50       21 out((defined($f->[1]) ? "\$".$f->[1] . " := " : ()),
    50          
1640             'open ',
1641             ($_xml_module->document_type($f->[0]) eq 'html' ?
1642             '--format html ' : ()),
1643             "'".$f->[0]->URI()."';\n");
1644             }
1645             }
1646              
1647              
1648             sub close_undef_value {
1649 34     34 0 75 my ($doc,$value)=@_;
1650 34 100       75 if (ref($value)) {
1651 6 50 100     92 if (UNIVERSAL::isa($value,'XML::LibXML::NodeList')) {
    100          
1652             @$value = grep
1653 0 0       0 {eval { $doc->isSameNode($_xml_module->owner_document($_)) ? 0 : 1}}
  0         0  
  0         0  
1654             @$value;
1655             } elsif (UNIVERSAL::isa($value,'XML::LibXML::Node')
1656             and $doc->isSameNode($_xml_module->owner_document($value))) {
1657 1         9 undef $value;
1658             }
1659             }
1660 34         90 return $value;
1661             }
1662              
1663             sub close_doc {
1664 1     1 0 24 my ($opts,$exp)=@_;
1665 1         26 my $doc = _ev_doc($exp);
1666 13     13   76 no strict 'refs';
  13         64  
  14         1581  
1667 1         14 foreach my $var (keys %{"XML::XSH2::Map::"}) {
  1         147  
1668 55         80 my $value = ${"XML::XSH2::Map::".$var};
  55         209  
1669 55 100       125 next unless defined $value;
1670 34 100       100 undef ${"XML::XSH2::Map::".$var}
  1         19  
1671             unless defined(close_undef_value($doc,$value));
1672             }
1673 1         15 foreach my $lex_context (@$lexical_variables) {
1674 1         8 my ($name,$value);
1675 1         25 while (($name,$value) = each %$lex_context) {
1676 0 0       0 next unless defined $value;
1677 0         0 $value = close_undef_value($doc,$value);
1678 0 0       0 unless (defined($value)) {
1679 0         0 $lex_context->{$name} = undef
1680             }
1681             }
1682             }
1683 1         39 return 1;
1684             }
1685              
1686             sub xpath_var_lookup {
1687 334     334 0 10090 my ($data,$name,$ns)=@_;
1688 14     13   83 no strict;
  14         306  
  14         3412  
1689 334         575 my $res;
1690 334 50       928 if ($ns ne "") {
1691 0         0 $res = $XML::XSH2::Map::NAMESPACED_VARIABLES{$ns." ".$name};
1692 0 0       0 unless (defined ($res)) {
1693 0         0 die "Undefined variable '\%$name' in namespace `$ns'\n";
1694             }
1695             } else {
1696 334         799 my $lex = lex_var($name);
1697 334 100       767 if ($lex) {
    50          
1698 37         71 $res = $$lex
1699 297         1625 } elsif (defined(${"XML::XSH2::Map::$name"})) {
1700 297         437 $res = ${"XML::XSH2::Map::$name"};
  297         1583  
1701             } else {
1702 0         0 die "Undefined variable '\$$name'\n";
1703             }
1704             }
1705 334 100 100     2011 if (ref($res) and UNIVERSAL::isa($res,'XML::LibXML::Node')) {
1706 26         426 return XML::LibXML::NodeList->new($res);
1707             } else {
1708 308         2866 return $res;
1709             }
1710             }
1711              
1712             sub lex_var {
1713 1165     1165 0 3353 my ($n)=@_;
1714 1165         3363 for (my $i=$#$lexical_variables; $i>=0; $i--) {
1715 2293 100       5983 return \$lexical_variables->[$i]{$n} if exists($lexical_variables->[$i]{$n});
1716             }
1717 693         3972 return undef;
1718             }
1719              
1720             # return a value of the given XSH variable
1721             sub var_value {
1722 91     91 0 268 my ($var) = @_;
1723 91 50       494 if ($var=~/^\$(\$.*)/) {
    50          
1724 0         0 my $name = var_value($1);
1725 0 0       0 die "Dereferencing $var to a non-ID: $name\n"
1726             if ($name !~ /((?:::)?[a-zA-Z_][a-zA-Z0-9_]*)*/);
1727 0         0 return var_value(q($).$name);
1728             } elsif ($var=~/^\$?(.*)/) {
1729 14     13   79 no strict qw(refs);
  14         54  
  14         1024  
1730 91         211 my $lex = lex_var($1);
1731 91 100       237 if ($lex) {
    100          
1732 48         193 return $$lex
1733 43         355 } elsif (defined(${"XML::XSH2::Map::$1"})) {
1734 36         58 return ${"XML::XSH2::Map::$1"};
  36         208  
1735             }
1736             } else {
1737 0         0 return undef;
1738             }
1739             }
1740              
1741             sub string_vars {
1742 14     13   79 no strict qw(refs);
  14         282  
  14         728  
1743 0     0 0 0 return sort grep { defined(${"XML::XSH2::Map::$_"}) } keys %{"XML::XSH2::Map::"};
  0         0  
  0         0  
  0         0  
1744             }
1745              
1746             # print a list of XSH variables and their values
1747             #
1748             # KNOWN BUG: $ARGV doesn't map to the correct i.e. (no) package
1749             #
1750              
1751             sub variables {
1752 0     0 0 0 my $opts = shift;
1753 14     13   75 no strict 'refs';
  14         50  
  15         18333  
1754 0         0 foreach (string_vars()) {
1755 0         0 my $value = var_value(q($).$_);
1756 0 0       0 if (!defined($value)) {
    0          
    0          
1757 0         0 out(qq(\$$_={ )."undef".qq( };\n));
1758             } elsif (ref($value)) {
1759 0         0 out(qq(\$$_={ ).var_value(q($).$_).qq( };\n));
1760             } elsif (0+$value eq $value) {
1761 0         0 out(qq(\$$_=).var_value(q($).$_).qq(;\n));
1762             } else {
1763 0         0 out(qq(\$$_=\').var_value(q($).$_).qq(\';\n));
1764             }
1765             }
1766 0         0 return 1;
1767             }
1768              
1769             sub echo {
1770 10     10 0 28 my $opts = _ev_opts(shift);
1771             my $val = join(($opts->{nospace} ? "" : " ") ,
1772 10 50       45 (map _ev_string($_),@_)).($opts->{nonl} ? "" : "\n");
    50          
1773 10 50       123 $opts->{stderr} ? (print STDERR $val) : out($val);
1774 10         37 return 1;
1775             }
1776 14 100   14 0 1023 sub set_quiet { shift if @_>1; $QUIET=$_[0]; return 1; }
  14         39  
  14         78  
1777 0 0   0 0 0 sub set_debug { shift if @_>1; $DEBUG=$_[0]; return 1; }
  0         0  
  0         0  
1778 0 0   0 0 0 sub set_compile_only_mode { shift if @_>1; $TEST_MODE=$_[0]; return 1; }
  0         0  
  0         0  
1779              
1780             sub test_enc {
1781 16     16 0 44 my ($enc)=@_;
1782 16 50 33     57 if (
1783             defined(toUTF8($enc,'')) and defined(fromUTF8($enc,''))
1784             ) {
1785             # print STDERR "OK\n";
1786 16         47 return 1;
1787             } else {
1788             # print STDERR "NOT-OK\n";
1789 0         0 _err("Error: Cannot convert between $enc and utf-8\n");
1790 0         0 return 0;
1791             }
1792             }
1793              
1794             sub set_encoding {
1795 16 50   16 0 90 shift if @_>1; # opts
1796             # print STDERR "ENCOD: @_\n";
1797 16         69 my $enc=_ev_string($_[0]);
1798 16         61 my $ok=test_enc($enc);
1799 16         41 $ENCODING=$enc;
1800 16         36 return 1;
1801             # my $ok=test_enc($enc);
1802             # if ($ok) {
1803             # $ENCODING=$enc;
1804             # # $enc = "encoding($enc)" unless $enc eq 'utf8';
1805             # # binmode $OUT;
1806             # # binmode $OUT,":$enc";
1807             # # binmode STDOUT;
1808             # # binmode STDOUT,":$enc";
1809             # # binmode STDERR;
1810             # # binmode STDERR,":$enc";
1811             # # print "Setting encoding to :$enc\n";
1812             # }
1813             # return $ok;
1814             }
1815              
1816             sub set_qencoding {
1817 0     0 0 0 my ($opts,$exp)=@_;
1818 0         0 my $enc=_ev_string($exp);
1819 0         0 my $ok=test_enc($enc);
1820 0 0       0 $QUERY_ENCODING=$enc if $ok;
1821 0         0 return $ok;
1822             }
1823              
1824             sub sigint {
1825 0 0   0 0 0 if ($TRAP_SIGINT) {
1826 0         0 print STDERR "\nCtrl-C pressed. \n";
1827 0         0 die "SIGINT";
1828             } else {
1829 0         0 print STDERR "\nCtrl-C pressed. \n";
1830 0         0 exit 1;
1831             }
1832             }
1833              
1834             sub sigpipe {
1835 0 0   0 0 0 if ($TRAP_SIGPIPE) {
1836 0         0 die "SIGPIPE";
1837             } else {
1838 0         0 _err('broken pipe (SIGPIPE)');
1839 0         0 exit 1;
1840             }
1841             }
1842              
1843             sub flagsigint {
1844 0     0 0 0 print STDERR "\nCtrl-C pressed. \n";
1845 0         0 $_sigint=1;
1846             }
1847              
1848             sub propagate_flagsigint {
1849 1372 50   1372 0 15492 if ($_sigint) {
1850 0         0 $_sigint=0;
1851 0         0 die 'SIGINT';
1852             }
1853             }
1854              
1855              
1856             sub convertFromDocEncoding ($$\$) {
1857 0     0 0 0 my ($doc,$encoding,$str)=@_;
1858 0         0 return fromUTF8($encoding, toUTF8($_xml_module->doc_encoding($doc), $str));
1859             }
1860              
1861             sub _rt_position {
1862 5 100   5   54 return "$RT_SCRIPT line $RT_LINE, column $RT_COLUMN,".
1863             ($RT_LINE==1 ? "" : " offset $RT_OFFSET.");
1864             }
1865              
1866             sub _err {
1867 0 0   0   0 print STDERR @_," at ",_rt_position(),"\n" if $ERRORS;
1868             }
1869              
1870             sub _warn {
1871 1 50   1   4 print STDERR "Warning: ",@_," at ",_rt_position(),"\n" if $WARNINGS;
1872             }
1873              
1874              
1875             # if the argument is non-void then print it and return 0; return 1 otherwise
1876             sub _check_err {
1877 2389     2389   5193 my ($err,$survive_int,$remove_at)=@_;
1878 2389 100       4658 if ($err) {
1879             # cleanup the error message
1880 63         160 $err =~ s/^XPathContext: error coming back from perl-dispatcher in pm file\.\s*//;
1881 63 50 33     161 if ($remove_at and !ref($err)) {
1882 0         0 $err=~s/ at (?:.|\n)*$//;
1883             }
1884              
1885 63 50       233 if ($err=~/^SIGINT/) {
    50          
1886 0 0       0 if ($survive_int) {
    0          
1887 0         0 $err=~s/ at (?:.|\n)*$//;
1888 0         0 _err($err);
1889 0         0 return 0;
1890             } elsif (ref($err)) {
1891 0         0 die $err; # propagate
1892             } else {
1893 0         0 chomp $err;
1894 0 0       0 unless ($err=~/ at (?:.|\n)*$/) {
1895 0         0 $err.=" at "._rt_position();
1896             }
1897 0         0 die $err."\n"; # propagate
1898             }
1899             } elsif ($_die_on_err) {
1900 63 50       195 if ($err=~/^SIGPIPE/) {
    100          
1901 0         0 _err('broken pipe (SIGPIPE)');
1902             } elsif (ref($err)) {
1903 40         192 die $err; # propagate
1904             } else {
1905 23         57 chomp $err;
1906 23 100       173 unless ($err=~/ at (?:.|\n)*$/) {
1907 5         23 $err.=" at "._rt_position();
1908             }
1909 23         402 die $err."\n"; # propagate
1910             }
1911             } else {
1912 0 0       0 if ($err=~/^SIGPIPE/) {
1913 0         0 _err('broken pipe (SIGPIPE)');
1914             } else {
1915 0         0 _err($err);
1916             }
1917 0         0 return 0;
1918             }
1919             }
1920 2326         3363 return 1;
1921             }
1922              
1923              
1924             # return current node for given document or document root if
1925             # current node is not from the given document
1926             sub xsh_context_node {
1927 118     118 0 1106 return $_xpc->getContextNode;
1928             }
1929              
1930             sub xsh_context_var {
1931 0     0 0 0 my $node = xsh_context_node();
1932 0 0       0 if ($node) {
1933 0         0 return xsh_search_docvar($node);
1934             }
1935 0         0 return "";
1936             }
1937              
1938              
1939             # set current node to given XPath
1940             sub set_local_xpath {
1941 27     27 0 81 my ($opts,$exp)=@_;
1942 27 100       82 $exp = "/" if ($exp eq "");
1943 27         86 _set_context([_ev_nodelist($exp)->shift()]);
1944 27         198 return 1;
1945             }
1946              
1947             sub cannon_name {
1948 61     61 0 123 my ($node)=@_;
1949 61         253 my $local_name =$node->localname();
1950 61         172 my $uri = $node->namespaceURI();
1951 61 50       136 if ($uri ne '') {
1952 0         0 my $prefix=$node->prefix;
1953             #if ($prefix eq '') {
1954 0         0 my %r = reverse %_ns;
1955 0         0 $prefix = $r{ $uri };
1956 0 0       0 if ($prefix ne '') {
    0          
1957 0         0 return $prefix.':'.$local_name
1958             } elsif(my $parent = $node->parentNode) {
1959 0         0 $prefix = $parent->lookupNamespacePrefix($uri);
1960 0 0       0 if ($prefix ne '') {
1961 0         0 return $prefix.':'.$local_name
1962             }
1963             }
1964 0         0 return '*[name()="'.$node->getName().'"]';
1965             }
1966 61         158 return $local_name;
1967             }
1968              
1969             # return XPath identifying a node within its parent's subtree
1970             sub node_address {
1971 97   33 97 0 203 my $node = shift || $_xpc->getContextNode();
1972 97         488 my $no_parent = shift;
1973 97         135 my $name;
1974 97 100 66     370 if ($_xml_module->is_element($node)) {
    100          
    50          
    50          
    50          
1975 61         138 $name=cannon_name($node);
1976             } elsif ($_xml_module->is_text($node) or
1977             $_xml_module->is_cdata_section($node)) {
1978 5         36 $name="text()";
1979             } elsif ($_xml_module->is_comment($node)) {
1980 0         0 $name="comment()";
1981             } elsif ($_xml_module->is_pi($node)) {
1982 0         0 $name="processing-instruction()";
1983             } elsif ($_xml_module->is_attribute($node)) {
1984 0         0 return "@".cannon_name($node);
1985             }
1986            
1987 97 100 66     758 if (!$no_parent and $node->parentNode) {
1988 66         461 my @children;
1989             # if ($_xml_module->is_element($node)) {
1990             # @children=$_xpc->findnodes("./$name",$node->parentNode);
1991             # } else {
1992 66         172 my $context = $_xpc->getContextNode;
1993 66         112 @children= eval { $_xpc->findnodes("./$name",$node->parentNode) };
  66         374  
1994             # }
1995 66 100 66     3198 if (@children == 1 and $_xml_module->xml_equal($node,$children[0])) {
1996 45         173 return "$name";
1997             }
1998 21         437 for (my $pos=0;$pos<@children;$pos++) {
1999 106 100       265 return "$name"."[".($pos+1)."]"
2000             if ($_xml_module->xml_equal($node,$children[$pos]));
2001             }
2002 0         0 return "??$name??";
2003             } else {
2004 31         65 return ();
2005             }
2006             }
2007              
2008             # parent element (even for attributes)
2009             sub tree_parent_node {
2010 170     170 0 567 my $node=$_[0];
2011 170 50       1947 if ($_xml_module->is_attribute($node)) {
2012 0         0 return $node->ownerElement();
2013             } else {
2014 170         851 return $node->parentNode();
2015             }
2016             }
2017              
2018             # get node's ID
2019             sub node_id {
2020 0     0 0 0 my ($node)=@_;
2021 0 0       0 if ($node) {
2022 0         0 for my $attr ($node->attributes) {
2023 0 0 0     0 if ($attr->can('isId') and $attr->isId) {
2024 0         0 my $value = $attr->value;
2025 0 0       0 return $value if defined $value;
2026             }
2027             }
2028             }
2029 0         0 return undef;
2030             }
2031              
2032             # return canonical xpath for the given or current node
2033             sub pwd {
2034 31   66 31 0 287 my $node=shift || $_xpc->getContextNode();
2035 31         186 my $use_id = shift;
2036 31 50       108 return undef unless ref($node);
2037 31 50 33     87 return $node->nodePath() if !$STRICT_PWD and UNIVERSAL::can($node,'nodePath');
2038 31         47 my @pwd=();
2039 31         47 do {
2040 97 50       711 if ($use_id) {
2041 0         0 my $id = node_id($node);
2042 0 0       0 if (defined $id) {
2043 0         0 return join "/","id('$id')",@pwd;
2044             }
2045             }
2046 97         227 unshift @pwd,node_address($node);
2047 97         2051 $node=tree_parent_node($node);
2048             } while ($node);
2049 31         105 my $pwd="/".join "/",@pwd;
2050 31         128 return $pwd;
2051             }
2052              
2053             # return canonical xpath for current node (encoded)
2054             sub xsh_pwd {
2055 0 0 0 0 0 0 shift if $_[0] && !ref($_[0]); # package name
2056 0         0 &pwd;
2057             }
2058              
2059             # print current node's xpath
2060             sub print_pwd {
2061 17     17 0 90 my $opts = _ev_opts(shift);
2062            
2063 17         96 my $pwd=pwd(undef, $opts->{id});
2064 17 50       54 if ($pwd) {
2065 17         80 out("$pwd\n");
2066 17         159 return $pwd;
2067             } else {
2068 0         0 return 0;
2069             }
2070             }
2071              
2072             # return base file-name of a given path
2073             sub _base_filename {
2074 0     0   0 my ($fn)=@_;
2075 0 0       0 ($^O eq 'Win32') ? ($fn =~ m{([^\\]*)$}) : ($fn =~ m{([^/]*)$});
2076 0         0 return $1;
2077             }
2078              
2079             # evaluate variable and xpath expresions in a given string
2080             sub _expand {
2081 1497     1497   3342 my ($l,$vars)=@_;
2082 1497         2042 my $k;
2083 15     13   172 no strict;
  15         332  
  15         3105  
2084 1497         3734 $l=~/^/o;
2085 1497         4295 while ($l !~ /\G$/gsco) {
2086 2170 100 33     15444 if ($l=~/\G\\\$\{/gsco) {
    100          
    50          
    50          
    100          
    50          
2087 3         10 $k.='${';
2088             } elsif ($l=~/\G\$\{(\$?[a-zA-Z_][a-zA-Z0-9_]*)\}/gsco) {
2089 69         290 $k.=var_value(q($).$1);
2090             } elsif ($vars and $l=~/\G(\$\$?[a-zA-Z_][a-zA-Z0-9_]*)/gsco) {
2091 0         0 $k.=var_value($1);
2092             } elsif ($l=~/\G\$\{\{(.*?)\}\}/gsco) {
2093 0         0 $k.=perl_eval($1);
2094             } elsif ($l=~/\G\$\{\((.+?)\)\}/gsco) {
2095 9         32 $k.=_ev_literal($1);
2096             } elsif ($l=~/\G(\$(?!\{)|\\(?!\$\{)|[^\\\$]+)/gsco) {
2097             # skip to the next \ or $
2098 2089         8268 $k.=$1;
2099             }
2100             }
2101 1497         6988 return $k;
2102             }
2103              
2104             # expand one or all parameters (according to return context)
2105             sub expand {
2106 20 50   20 0 124 return wantarray ? (map { _expand($_) } @_) : _expand($_[0]);
  0         0  
2107             }
2108              
2109             # return a reference to a variable storage
2110             sub _get_var_ref {
2111 0     0   0 my ($name,$value)=@_;
2112 15     13   78 no strict 'refs';
  15         53  
  15         2138  
2113 0 0       0 if ($name=~/^\$(\$.*)/) {
    0          
2114 0         0 my $prev = $name;
2115 0         0 $name = var_value($1);
2116 0 0       0 die "Dereferencing $prev to a non-ID: $name\n"
2117             if ($name !~ /((?:::)?[a-zA-Z_][a-zA-Z0-9_]*)*$/);
2118 0         0 return _get_var_ref(q($).$name);
2119             } elsif ($name=~/^\$((?:::)?[a-zA-Z_][a-zA-Z0-9_]*)*$/) {
2120 0         0 my $lex = lex_var($1);
2121 0 0       0 return $lex if ($lex);
2122 0         0 return \${"XML::XSH2::Map::$1"};
  0         0  
2123             } else {
2124 0         0 die "Invalid variable name $name\n"
2125             }
2126 0         0 return 1;
2127             }
2128              
2129             # assign a value to a variable
2130             sub _assign {
2131 740     740   1666 my ($name,$value,$op)=@_;
2132 15     13   86 no strict 'refs';
  15         317  
  15         2729  
2133 740 50       4529 if ($name=~/^\$(\$.*)/) {
    50          
2134 0         0 my $prev = $name;
2135 0         0 $name = var_value($1);
2136 0 0       0 die "Dereferencing $prev to a non-ID: $name\n"
2137             if ($name !~ /((?:::)?[a-zA-Z_][a-zA-Z0-9_]*)*$/);
2138 0         0 return _assign(q($).$name,$value,$op);
2139             } elsif ($name=~/^\$?((?:::)?[a-zA-Z_][a-zA-Z0-9_]*)*$/) {
2140 740 100       1722 $op = '=' unless $op;
2141 740         1644 my $lex = lex_var($1);
2142 740 100       1560 if ($lex) {
2143 387 50       20650 eval '$$lex'.$op.'$value'; die $@ if $@;
  387         1511  
2144 387 50       1231 print STDERR "lexical \$$1=",${"XML::XSH2::Map::$1"},"\n" if $DEBUG;
  0         0  
2145             } else {
2146 353 50       27864 eval '${"XML::XSH2::Map::$1"}'.$op.'$value'; die $@ if $@;
  353         1858  
2147 353 50       1892 print STDERR "\$$1=",${"XML::XSH2::Map::$1"},"\n" if $DEBUG;
  0         0  
2148             }
2149             } else {
2150 0         0 die "Invalid variable name $name\n"
2151             }
2152             }
2153              
2154             # undefine global or localized variable
2155             sub _undef {
2156 0     0   0 my ($name)=@_;
2157 15     13   77 no strict 'refs';
  15         51  
  15         2869  
2158 0 0       0 if ($name=~/^\$(\$.*)/) {
    0          
2159 0         0 my $prev = $name;
2160 0         0 $name = var_value($1);
2161 0 0       0 die "Dereferencing $prev to a non-ID: $name\n"
2162             if ($name !~ /((?:::)?[a-zA-Z_][a-zA-Z0-9_]*)*$/);
2163 0         0 return _undef(q($).$name);
2164             } elsif ($name=~/^\$((?:::)?[a-zA-Z_][a-zA-Z0-9_]*)*$/) {
2165 0         0 undef ${"XML::XSH2::Map::$1"};
  0         0  
2166             } else {
2167 0         0 die "Invalid variable name $name\n"
2168             }
2169 0         0 return 1;
2170             }
2171              
2172             # undefine lexical, global or localized variable
2173             sub undefine {
2174             my ($name)=@_;
2175             if ($name=~/^\$(\$.*)/) {
2176             my $prev = $name;
2177             $name = var_value($1);
2178             die "Dereferencing $prev to a non-ID: $name\n"
2179             if ($name !~ /((?:::)?[a-zA-Z_][a-zA-Z0-9_]*)*$/);
2180             return undefine(q($).$name);
2181             } elsif ($name=~/^\$((?:::)?[a-zA-Z_][a-zA-Z0-9_]*)*$/) {
2182             my $lex = lex_var($1);
2183             if ($lex) {
2184             undef $$lex;
2185             } else {
2186 15     13   72 no strict qw(refs);
  15         338  
  15         12198  
2187             undef ${"XML::XSH2::Map::".$1};
2188             }
2189             } else {
2190             die "Invalid variable name $name\n"
2191             }
2192             return 1;
2193             }
2194              
2195             sub literal_value {
2196 363 100   363 0 1635 return ref($_[0]) ? $_[0]->value() : $_[0];
2197             }
2198              
2199             # evaluate xpath and assign the result to a variable
2200             sub xpath_assign {
2201 254     254 0 685 my ($exp,$op,$type,$name)=@_;
2202 254 100       825 if ($type eq 'my') {
    100          
2203 1         3 store_lex_variables(0,$name);
2204             } elsif ($type eq 'local') {
2205 11         26 store_variables(0,$name);
2206             }
2207 254         676 my $val = _ev($exp);
2208 254         1148 _assign($name,$val,$op);
2209 254         916 return 1;
2210             }
2211              
2212             sub command_assign {
2213 48     48 0 198 my ($command,$op,$type,$name)=@_;
2214 48 50       284 if ($type eq 'my') {
    50          
2215 0         0 store_lex_variables(0,$name);
2216             } elsif ($type eq 'local') {
2217 0         0 store_variables(0,$name);
2218             }
2219 48         374 $op =~ s/\s*:\s*//;
2220 48         249 _assign($name,run_commands([$command],0,1),$op);
2221 48         487 return 1;
2222             }
2223              
2224             sub make_local {
2225 1     1 0 8 foreach (@_) {
2226 1         4 xpath_assign(undef,'=','local',$_);
2227             }
2228             }
2229              
2230             sub get_stored_nodelists {
2231 0     0 0 0 return grep { ref($_) } map { @$_ } @stored_variables;
  0         0  
  0         0  
2232             }
2233              
2234             sub store_variables {
2235 1051     1051 0 1889 my ($new,@vars)=@_;
2236 1051         1363 my $pool;
2237 1051 100 33     1865 if ($new) {
    50          
2238 1040         1706 $pool=[];
2239             } elsif (@stored_variables and ref($stored_variables[$#stored_variables])) {
2240 11         38 $pool=$stored_variables[$#stored_variables];
2241             } else {
2242 0         0 _warn "Ignoring attempt to make a local variable outside a localizable context!";
2243 0         0 return 0;
2244             }
2245              
2246 1051         1968 foreach (@vars) {
2247 18         59 my $value=var_value($_);
2248 18         63 push @$pool, $_ => $value;
2249             }
2250 1051 100       2677 push @stored_variables, $pool if ($new);
2251              
2252 1051         1594 return 1;
2253             }
2254              
2255             sub store_lex_variables {
2256 1288     1288 0 1813 my $new = shift;
2257 1288         1656 my $pool;
2258 1288 100       2088 if ($new) {
    50          
2259 1287         1954 $pool={};
2260 1287         2342 push @$lexical_variables, $pool;
2261             } elsif (@$lexical_variables) {
2262 1         3 $pool=$lexical_variables->[$#$lexical_variables]
2263             } else {
2264 0         0 _warn "Ignoring attempt to make a lexical variable outside a lexical context!";
2265 0         0 return 0;
2266             }
2267 1288         2439 foreach (@_) {
2268 387 50       1765 if (/^\s*\$?([^\$]*)/) {
2269 387         1196 $pool->{$1} = undef;
2270             } else {
2271 0         0 die "Invalid lexical variable name $_\n";
2272             }
2273             }
2274             }
2275              
2276             sub restore_lex_variables {
2277 1287 50   1287 0 4054 unless (ref(pop @$lexical_variables)) {
2278 0         0 __bug("Lexical variable pool is empty, which was not expected!\n");
2279             }
2280             }
2281              
2282             sub create_block_var {
2283 7     7 0 43 my ($var,$local) = @_;
2284 7 50       35 if ($local =~ /local/) {
    0          
2285 7         25 store_variables(1,$var);
2286             } elsif ($local=~/my/) {
2287 0         0 store_lex_variables(1,$var);
2288             }
2289             }
2290              
2291             sub destroy_block_var {
2292 7 50   7 0 38 if ($_[0] =~ /local/) {
    0          
2293 7         24 restore_variables();
2294             } elsif ($_[0]=~/my/) {
2295 0         0 restore_lex_variables();
2296             }
2297             }
2298              
2299             sub restore_variables {
2300 1040     1040 0 1814 my $pool=pop @stored_variables;
2301 1040 50       2129 unless (ref($pool)) {
2302 0         0 __bug("Local variable pool is empty, which was not expected!\n");
2303 0         0 return 0;
2304             }
2305 1040         2324 while (@$pool) {
2306 18         48 my ($value,$name)=(pop(@$pool), pop(@$pool));
2307 18 50       109 if ($name =~ m/^\$/) {
2308 18 50       48 if (defined($value)) {
2309 18         49 _assign($name,$value);
2310             } else {
2311 0         0 _undef($name);
2312             }
2313             } else {
2314 0         0 __bug("Invalid variable name '$_'\n");
2315             }
2316             }
2317 1040         1685 return 1;
2318             }
2319              
2320             sub _prepare_result_nl {
2321             # my ($opts)=@_;
2322             # return undef unless ref($opts);
2323             # my ($append, $var);
2324             # if (exists($opts->{'append-result'})) {
2325             # $append = 1;
2326             # $var = $opts->{'append-result'};
2327             # } elsif (exists($opts->{result})) {
2328             # $append = 0;
2329             # $var = $opts->{result};
2330             # } elsif (exists($opts->{'append-result'})) {
2331             # $append = 1;
2332             # $var = $opts->{'append-result'};
2333             # } else {
2334             # return undef;
2335             # }
2336             # my $rl;
2337             # if ($var ne "") {
2338             # $rl = var_value($var);
2339             # unless ($append and (UNIVERSAL::isa($rl,'XML::LibXML::NodeList') or ref($rl) eq 'ARRAY')) {
2340             # _assign($var,XML::LibXML::NodeList->new());
2341             # $rl = var_value($var);
2342             # }
2343             # }
2344             # return $rl;
2345 120 100   120   292 if ($_want_returns) {
2346 16         57 return XML::LibXML::NodeList->new();
2347             } else {
2348 104         197 return undef;
2349             }
2350             }
2351              
2352              
2353             sub count_xpath {
2354 52     52 0 141 my ($exp)=@_;
2355 52         191 my $result = _ev($exp);
2356 52 100       217 if (ref($result)) {
2357 36 100 66     536 if (UNIVERSAL::isa($result,'XML::LibXML::NodeList')) {
    50          
    50          
2358 4         37 return $result->size();
2359             } elsif (UNIVERSAL::isa($result,'XML::LibXML::Literal')) {
2360 0         0 return $result->value();
2361             } elsif (UNIVERSAL::isa($result,'XML::LibXML::Number') or
2362             UNIVERSAL::isa($result,'XML::LibXML::Boolean')) {
2363 32         193 return $result->value();
2364             }
2365             } else {
2366 16         45 return $result;
2367             }
2368             }
2369              
2370             sub new_doc {
2371 31     31 0 121 my ($opts,$root_element)=@_;
2372 31         123 $opts = _ev_opts($opts);
2373 31         156 $root_element = _ev_string($root_element);
2374 31   33     242 my $format= $opts->{format} || $DEFAULT_FORMAT;
2375 31         192 create_doc(undef, $root_element, $format);
2376             }
2377              
2378             # create new document
2379             sub create_doc {
2380 53     53 0 984 my ($id, $root_element, $format, $filename)=@_;
2381             # TODO: $format argument is not used by the grammar
2382 53         81 my $doc;
2383 53 100       357 $root_element="<$root_element/>" unless ($root_element=~/^\s*
2384 53         191 $root_element=~s/^\s+//;
2385 53         205 $doc=xsh_parse_string($root_element,$format);
2386 53 100       12209 set_doc($id,$doc,$filename) if defined($id);
2387 53         99 $_newdoc++;
2388              
2389 53 50       337 _set_context([$doc]) if $SWITCH_TO_NEW_DOCUMENTS;
2390 53         207 return $doc;
2391             }
2392              
2393             # bind a document with a given id and filename
2394             sub set_doc {
2395 22     22 0 57 my ($id,$doc,$file)=@_;
2396             # $_doc{$id}=$doc;
2397             # $_files{$id}=$file;
2398 22         770 _assign($id,$doc);
2399 22         90 set_doc_URI($doc,$file);
2400 22         38 return $doc;
2401             }
2402              
2403             sub set_filename {
2404 0     0 0 0 my ($opts,$file, $doc)=@_;
2405 0         0 $file = _tilde_expand(_ev_string($file));
2406 0 0       0 $doc = _ev_doc(defined($doc) ? $doc : '.');
2407 0         0 set_doc_URI($doc,$file);
2408             }
2409              
2410             sub set_doc_URI {
2411 22     22 0 51 my ($doc,$file)=@_;
2412 22 50 66     293 $doc->setBaseURI($file)
      66        
2413             if (defined($file) and ref($doc) and
2414             UNIVERSAL::can($doc,'setBaseURI'));
2415 22         87 return $doc->URI;
2416             }
2417              
2418              
2419             sub xsh_search_docvar {
2420 0     0 0 0 my ($node)=@_;
2421 0         0 my $doc = $_xml_module->owner_document($node);
2422 0 0       0 return undef unless ref($doc);
2423 15     13   83 no strict 'refs';
  15         179  
  15         28171  
2424 0         0 foreach my $var (keys %{"XML::XSH2::Map::"}) {
  0         0  
2425 0         0 my $value = ${"XML::XSH2::Map::".$var};
  0         0  
2426 0 0       0 if (ref($value)) {
2427 0 0 0     0 $value = $value->[0] if (UNIVERSAL::isa($value,'XML::LibXML::NodeList')
2428             and $value->size()==1);
2429 0 0 0     0 if (UNIVERSAL::isa($value,'XML::LibXML::Document')
2430             and $value->isSameNode($doc)) {
2431 0         0 return "\$".$var;
2432             }
2433             }
2434             }
2435             }
2436              
2437             sub index_doc {
2438 0     0 0 0 my ($opts,$exp)=@_;
2439 0 0       0 $exp = '.' if $exp eq "";
2440 0         0 my $doc = _ev_doc($exp);
2441 0         0 my $result;
2442 0 0       0 if ($doc->can('indexElements')) {
2443 0         0 $result = $doc->indexElements;
2444 0 0       0 print STDERR $result." elements indexed.\n" unless $QUIET;
2445             } else {
2446 0         0 _warn "Indexing not supported by installed version of XML::LibXML\n";
2447             }
2448 0         0 return $result;
2449             }
2450              
2451             sub _is_url {
2452 0 0   0   0 return ($_[0] =~ m(^\s*[[:alnum:]]+://)) ? 1 : 0;
2453             }
2454             sub _is_absolute {
2455 0     0   0 my ($path) = @_;
2456 0 0 0     0 return ($path eq '-' or
2457             _is_url($path) or
2458             File::Spec->file_name_is_absolute($path)) ? 1 : 0;
2459             }
2460              
2461             # create a new document by parsing a file
2462             sub open_doc {
2463 1     1 0 2 my ($opts,$src)=@_;
2464 1         5 $opts = _ev_opts($opts);
2465              
2466 1 50       6 if (exists($opts->{file})+exists($opts->{pipe})+
2467             exists($opts->{string})>1) {
2468 0         0 die "'open' may have only one input flag: --file | ".
2469             "--pipe | --string\n";
2470             }
2471 1   33     4 my $format= $opts->{format} || $DEFAULT_FORMAT;
2472 1 50       4 if ($format !~ /^xml$|^html$/) {
2473 0         0 die "Unknown --format for command open: '$format'! Use 'xml' or 'html'.\n";
2474             }
2475              
2476 1         2 foreach my $o (qw(switch-to validate recover expand-entities xinclude
2477             keep-blanks pedantic load-ext-dtd complete-attributes)) {
2478             die "Can't use --$o and --no-$o together\n"
2479 9 0 33     16 if ($opts->{'no-'.$o} and $opts->{$o});
2480             }
2481 1 50       4 local $SWITCH_TO_NEW_DOCUMENTS = 1 if $opts->{'switch-to'};
2482 1 50       10 local $SWITCH_TO_NEW_DOCUMENTS = 0 if $opts->{'no-switch-to'};
2483 1 50       4 local $VALIDATION = 1 if $opts->{validate};
2484 1 50       4 local $VALIDATION = 0 if $opts->{'no-validate'};
2485 1 50       3 local $RECOVERING = 1 if $opts->{recover};
2486 1 50       2 local $RECOVERING = 0 if $opts->{'no-recover'};
2487 1 50       3 local $PARSER_EXPANDS_ENTITIES = 1 if $opts->{'expand-entities'};
2488 1 50       2 local $PARSER_EXPANDS_ENTITIES = 0 if $opts->{'no-expand-entities'};
2489 1 50       2 local $KEEP_BLANKS = 1 if $opts->{'keep-blanks'};
2490 1 50       3 local $KEEP_BLANKS = 0 if $opts->{'no-keep-blanks'};
2491 1 50       3 local $PEDANTIC_PARSER = 1 if $opts->{pedantic};
2492 1 50       13 local $PEDANTIC_PARSER = 0 if $opts->{'no-pedantic'};
2493 1 50       3 local $LOAD_EXT_DTD = 1 if $opts->{'load-ext-dtd'};
2494 1 50       3 local $LOAD_EXT_DTD = 0 if $opts->{'no-load-ext-dtd'};
2495 1 50       2 local $PARSER_COMPLETES_ATTRIBUTES = 1 if $opts->{'complete-attributes'};
2496 1 50       8 local $PARSER_COMPLETES_ATTRIBUTES = 0 if $opts->{'no-complete-attributes'};
2497 1 50       3 local $PARSER_EXPANDS_XINCLUDE = 1 if $opts->{'xinclude'};
2498 1 50       2 local $PARSER_EXPANDS_XINCLUDE = 0 if $opts->{'no-xinclude'};
2499              
2500 1         4 my ($source) = grep exists($opts->{$_}),qw(file pipe string);
2501 1         1 my $file;
2502 1 50       3 unless ($source eq 'string') {
2503 1         9 $file = _tilde_expand(_ev_string($src));
2504             # $file=~s{^(\~[^\/]*)}{(glob($1))[0]}eg;
2505 1 50 33     4 if ($source eq 'file' and !_is_absolute($file)) {
2506 0         0 $file = File::Spec->rel2abs($file);
2507             }
2508 1 50       3 print STDERR "open [$file]\n" if "$DEBUG";
2509 1 50       2 if ($file eq "") {
2510 0         0 die "filename is empty (hint: \$variable := open file-name)\n";
2511             }
2512             } else {
2513 0         0 $file = _ev_string($src);
2514 0 0       0 print STDERR "open []\n" if "$DEBUG";
2515 0 0       0 if ($file eq "") {
2516 0         0 die "string is empty\n";
2517             }
2518             }
2519              
2520 1 50 33     7 if (($source ne 'file') or
      33        
      0        
2521             (-f $file) or $file eq "-" or
2522             ($file=~/^[a-z]+:/)) {
2523 1 50       2 unless ("$QUIET") {
2524 1 50       3 if ($source eq 'string') {
2525 0         0 print STDERR "parsing string\n";
2526             } else {
2527 1         141 print STDERR "parsing $file\n";
2528             }
2529             }
2530 1         3 my $doc;
2531 1 50       5 if ($source eq 'pipe') {
    50          
2532 0   0     0 open my $F,"$file|" || die "Can't open pipe: $!\n";
2533 0 0       0 $F || die "Cannot open pipe to $file: $!\n";
2534 0         0 eval {
2535 0 0       0 if ($format eq 'xml') {
    0          
    0          
2536 0         0 $doc=$_xml_module->parse_fh($_parser,$F);
2537             } elsif ($format eq 'html') {
2538 0         0 $doc=$_xml_module->parse_html_fh($_parser,$F);
2539             } elsif ($format eq 'docbook') {
2540 0         0 $doc=$_xml_module->parse_sgml_fh($_parser,$F,$QUERY_ENCODING);
2541             }
2542             };
2543 0         0 close $F;
2544 0         0 _check_err($@,1,1);
2545             } elsif ($source eq 'string') {
2546 0         0 my $root_element=$file;
2547 0 0       0 $root_element="<$root_element/>" unless ($root_element=~/^\s*
2548 0         0 $root_element=~s/^\s+//;
2549 0         0 eval {
2550 0         0 $doc=xsh_parse_string($root_element,$format);
2551             };
2552 0         0 _check_err($@,1,1);
2553 0 0       0 die "Failed to parse string\n" unless (ref($doc));
2554 0         0 $_newdoc++;
2555             } else {
2556 1         2 eval {
2557 1 50       3 if ($format eq 'xml') {
    0          
    0          
2558 1         33 $doc=$_xml_module->parse_file($_parser,$file);
2559             } elsif ($format eq 'html') {
2560 0         0 $doc=$_xml_module->parse_html_file($_parser,$file);
2561             } elsif ($format eq 'docbook') {
2562 0         0 $doc=$_xml_module->parse_sgml_file($_parser,$file,$QUERY_ENCODING);
2563             }
2564             };
2565 1         397 _check_err($@,1,1);
2566 1 50       5 die "Failed to parse $file as $format\n" unless (ref($doc));
2567             }
2568 1 50       74 print STDERR "done.\n" unless "$QUIET";
2569 1 50       11 _set_context([$doc]) if $SWITCH_TO_NEW_DOCUMENTS;
2570 1         5 return $doc;
2571             } else {
2572 0         0 die "file doesn't exist: $file\n";
2573 0         0 return 0;
2574             }
2575             }
2576              
2577             sub open_io_file {
2578 0     0 0 0 my ($file)=@_;
2579 0 0       0 if ($file=~/^\s*[|>]/) {
    0          
2580 0         0 return IO::File->new($file);
2581             } elsif ($file=~/.gz\s*$/) {
2582 0         0 return IO::File->new("| gzip -c > $file");
2583             } else {
2584 0         0 return IO::File->new(">$file");
2585             }
2586             }
2587              
2588             sub is_xinclude {
2589 0     0 0 0 my ($node)=@_;
2590             return
2591 0   0     0 $_xml_module->is_xinclude_start($node) ||
2592             ($_xml_module->is_element($node) and
2593             $node->namespaceURI() eq $Xinclude_prefix and
2594             $node->localname() eq 'include');
2595             }
2596              
2597             sub xinclude_start_tag {
2598 0     0 0 0 my ($xi)=@_;
2599 0         0 my %xinc = map { $_->nodeName() => $_->value() } $xi->attributes();
  0         0  
2600 0 0       0 $xinc{parse}='xml' if ($xinc{parse} eq "");
2601 0         0 return "<".$xi->nodeName()." xmlns:".$xi->prefix()."=\"".$Xinclude_prefix."\" href=\"".$xinc{href}."\" parse=\"".$xinc{parse}."\">";
2602             }
2603              
2604             sub xinclude_end_tag {
2605 0     0 0 0 my ($xi)=@_;
2606 0         0 return "nodeName().">";
2607             }
2608              
2609             sub xinclude_print {
2610 0     0 0 0 my ($doc,$F,$node,$enc)=@_;
2611 0 0       0 return unless ref($node);
2612 0 0 0     0 if ($_xml_module->is_element($node) || $_xml_module->is_document($node)) {
2613 0 0       0 $F->print(fromUTF8($enc,start_tag($node))) if $_xml_module->is_element($node);
2614 0         0 my $child=$node->firstChild();
2615 0         0 while ($child) {
2616 0 0       0 if (is_xinclude($child)) {
    0          
2617 0         0 my %xinc = map { $_->nodeName() => $_->value() } $child->attributes();
  0         0  
2618 0   0     0 $xinc{parse}||='xml';
2619 0   0     0 $xinc{encoding}||=$enc; # may be used even to convert included XML
2620 0         0 my $elements=0;
2621 0         0 my @nodes=();
2622 0         0 my $node;
2623 0         0 my $expanded=$_xml_module->is_xinclude_start($child);
2624 0 0       0 if ($expanded) {
2625 0         0 $node=$child->nextSibling(); # in case of special XINCLUDE node
2626             } else {
2627 0         0 $node=$child->firstChild(); # in case of include element from XInclude NS
2628             }
2629 0         0 my $nested=0;
2630 0   0     0 while ($node and not($_xml_module->is_xinclude_end($node)
      0        
2631             and $nested==0
2632             and $expanded)) {
2633 0 0       0 if ($_xml_module->is_xinclude_start($node)) { $nested++ }
  0 0       0  
2634 0         0 elsif ($_xml_module->is_xinclude_end($node)) { $nested-- }
2635 0         0 push @nodes,$node;
2636 0 0       0 $elements++ if $_xml_module->is_element($node);
2637 0         0 $node=$node->nextSibling();
2638             }
2639 0 0 0     0 if ($nested>0) {
    0 0        
    0 0        
    0 0        
    0          
2640 0         0 print STDERR "Error: Unbalanced nested XInclude nodes.\n",
2641             " Ignoring this XInclude span!\n";
2642 0         0 $F->print("");
2643             } elsif (!$node and $_xml_module->is_xinclude_start($child)) {
2644 0         0 print STDERR "Error: XInclude end node not found.\n",
2645             " Ignoring this XInclude span!\n";
2646 0         0 $F->print("");
2647             } elsif ($xinc{parse} ne 'text' and $elements==0) {
2648 0         0 print STDERR "Warning: XInclude: No elements found in XInclude span.\n",
2649             " Ignoring whole XInclude span!\n";
2650 0         0 $F->print("");
2651             } elsif ($xinc{parse} ne 'xml' and $elements>1) {
2652 0         0 print STDERR "Error: XInclude: More than one element found in XInclude span.\n",
2653             " Ignoring whole XInclude span!\n";
2654 0         0 $F->print("");
2655             } elsif ($xinc{parse} eq 'text' and $elements>0) {
2656 0         0 print STDERR "Warning: XInclude: Element(s) found in textual XInclude span.\n",
2657             " Skipping whole XInclude span!\n";
2658 0         0 $F->print("");
2659             } else {
2660 0         0 $F->print(fromUTF8($enc,xinclude_start_tag($child)));
2661 0         0 save_xinclude_chunk($doc,\@nodes,$xinc{href},$xinc{parse},$xinc{encoding});
2662 0         0 $F->print(fromUTF8($enc,xinclude_end_tag($child)));
2663 0 0       0 $child=$node if ($expanded); # jump to XINCLUDE end node
2664             }
2665             } elsif ($_xml_module->is_xinclude_end($child)) {
2666 0         0 $F->print("");
2667             } else {
2668 0         0 xinclude_print($doc,$F,$child,$enc); # call recursion
2669             }
2670 0         0 $child=$child->nextSibling();
2671             }
2672 0 0       0 $F->print(fromUTF8($enc,end_tag($node))) if $_xml_module->is_element($node);
2673             } else {
2674 0         0 $F->print(fromUTF8($enc,$_xml_module->toStringUTF8($node,$INDENT)));
2675             }
2676             }
2677              
2678             sub _xml_decl {
2679 4     4   16 my ($doc,$version,$enc) = @_;
2680 4 50 33     17 $version=($doc->can('getVersion') ? $doc->getVersion() : '1.0')
    50          
2681             if ($doc and !defined $version);
2682 4 50 33     92 $enc=($doc->can('getEncoding') ? $doc->getEncoding() : undef)
    50          
2683             if ($doc and !defined $enc);
2684 4 50       162 return "" : "?>");
2685             }
2686              
2687             sub save_xinclude_chunk {
2688 0     0 0 0 my ($doc,$nodes,$file,$parse,$enc)=@_;
2689              
2690 0 0       0 return unless @$nodes>0;
2691              
2692 0 0       0 if ($BACKUPS) {
2693 0         0 eval { rename $file, $file."~"; };
  0         0  
2694 0         0 _check_err($@);
2695             }
2696 0         0 my $F=open_io_file($file);
2697 0 0       0 $F || die "Cannot open $file\n";
2698              
2699 0 0       0 if ($parse eq 'text') {
2700 0         0 foreach my $node (@$nodes) {
2701 0         0 $F->print(fromUTF8($enc,literal_value($node->to_literal)));
2702             }
2703             } else {
2704 0         0 $F->print(_xml_decl($doc,undef,$enc),"\n");
2705 0         0 foreach my $node (@$nodes) {
2706 0         0 xinclude_print($doc,$F,$node,$enc);
2707             }
2708 0         0 $F->print("\n");
2709             }
2710 0         0 $F->close();
2711             }
2712              
2713             # save a document
2714             sub save_doc {
2715 0     0 0 0 my ($opts,$exp)=@_;
2716 0         0 $opts = _ev_opts($opts);
2717 0         0 my ($doc,$node);
2718 0 0       0 if ($opts->{subtree}) {
2719 0   0     0 $exp ||= '.';
2720 0         0 ($node)=_ev_nodelist($exp)->pop();
2721 0 0       0 $doc = $_xml_module->owner_document($node) if $node
2722             } else {
2723 0         0 $node = $doc = _ev_doc($exp);
2724             }
2725 0 0       0 die "No document to save\n" unless ($node);
2726              
2727 0 0       0 $opts->{file} = _tilde_expand($opts->{file}) if exists($opts->{file});
2728 0 0       0 if (exists($opts->{file})+exists($opts->{pipe})+
2729             exists($opts->{print})+exists($opts->{string})>1) {
2730 0         0 die "'save' may have only one output flag: --file | ".
2731             "--pipe | --print | --string\n";
2732             }
2733 0         0 foreach my $o (qw(indent skip-dtd empty-tags skip-xmldecl backup)) {
2734             die "Can't use --$o and --no-$o together\n"
2735 0 0 0     0 if ($opts->{'no-'.$o} and $opts->{$o});
2736             }
2737              
2738 0 0       0 local $INDENT=1 if $opts->{indent};
2739 0 0       0 local $XML::LibXML::skipDTD = 1 if $opts->{'skip-dtd'};
2740 0 0       0 local $XML::LibXML::setTagCompression = 1 if $opts->{'empty-tags'};
2741 0 0       0 local $XML::LibXML::skipXMLDeclaration = 1 if $opts->{'skip-xmldecl'};
2742              
2743 0 0       0 local $INDENT=0 if $opts->{'no-indent'};
2744 0 0       0 local $XML::LibXML::skipDTD = 0 if $opts->{'no-skip-dtd'};
2745 0 0       0 local $XML::LibXML::setTagCompression = 0 if $opts->{'no-empty-tags'};
2746 0 0       0 local $XML::LibXML::skipXMLDeclaration = 0 if $opts->{'no-skip-xmldecl'};
2747              
2748 0 0       0 local $BACKUPS = 0 if $opts->{'no-backup'};
2749 0 0       0 local $BACKUPS = 1 if $opts->{'backup'};
2750              
2751             #__debug("$XML::LibXML::skipXMLDeclaration\n");
2752              
2753 0         0 my $format = $DEFAULT_FORMAT;
2754              
2755 0 0       0 if (exists($opts->{format})) {
2756 0         0 $format=lc($opts->{format});
2757             }
2758 0 0       0 if (exists($opts->{xinclude})) {
2759 0 0       0 if ($format eq 'html') {
2760 0         0 die "'save --xinclude' can only be used with XML format\n"
2761             }
2762 0         0 $format = 'xinclude';
2763             }
2764              
2765 0 0 0     0 die "'save --subtree' can't be used with HTML format\n" if ($format eq 'html' and $opts->{subtree});
2766              
2767 0         0 my ($target) = grep exists($opts->{$_}),qw(file pipe string print);
2768 0 0       0 $target = 'file' unless defined $target;
2769 0 0       0 my $file; $file = $opts->{$target} if $target;
  0         0  
2770 0 0       0 if ($target eq 'file') {
2771 0 0       0 if ($file eq "") {
2772 0         0 $file=$doc->URI;
2773             } else {
2774 0 0       0 $doc->setBaseURI($file) if $doc->can('setBaseURI');
2775             }
2776 0 0       0 if ($BACKUPS) {
2777 0         0 eval { rename $file, $file."~"; };
  0         0  
2778 0         0 _check_err($@);
2779             }
2780             }
2781              
2782 0   0     0 my $enc = $opts->{encoding} || $_xml_module->doc_encoding($doc) || 'utf-8';
2783 0 0       0 print STDERR "saving to $target $file as $format (encoding $enc)\n" if "$DEBUG";
2784              
2785 0 0       0 if ($format eq 'xinclude') {
    0          
2786 0 0       0 if ($target ne 'file') {
2787 0         0 die "Target '".uc($target)."' not supported with 'save --xinclude'\n";
2788             } else {
2789 0 0       0 if ($doc->{subtree}) {
2790 0         0 save_xinclude_chunk($doc,[$node],$file,'xml',$enc);
2791             } else {
2792 0         0 save_xinclude_chunk($doc,[$doc->childNodes()],$file,'xml',$enc);
2793             }
2794             }
2795             } elsif ($opts->{subtree}) {
2796 0 0       0 die "Unsupported format '$format'\n" unless $format eq 'xml';
2797 0 0 0     0 my $string =
2798             _xml_decl($doc,undef,$enc)."\n".
2799             (($target ne 'string' and lc($enc) =~ /^utf-?8$/i) ?
2800             $node->toString($INDENT) : fromUTF8($enc,$node->toString($INDENT)))."\n";
2801 0 0       0 if ($target eq 'file') {
    0          
    0          
    0          
2802 0   0     0 open my $F, '>', $file || die "Cannot open file $file\n";
2803 0         0 print {$F} ($string);
  0         0  
2804 0         0 close $F;
2805             } elsif ($target eq 'pipe') {
2806 0         0 $file=~s/^\s*\|?//g;
2807 0   0     0 open my $F,"| $file" || die "Cannot open pipe to $file\n";
2808 0         0 print {$F} ($string);
  0         0  
2809 0         0 close $F;
2810             } elsif ($target eq 'string') {
2811 0         0 return $string;
2812             } elsif ($target eq 'print') {
2813 0         0 out($string);
2814             }
2815             } else {
2816 0 0       0 if ($format eq 'xml') {
    0          
2817 0 0 0     0 if (lc($_xml_module->doc_encoding($doc)) ne lc($enc)
      0        
2818             and not($_xml_module->doc_encoding($doc) eq "" and
2819             lc($enc) eq 'utf-8')
2820             ) {
2821 0         0 $_xml_module->set_encoding($doc,$enc);
2822             }
2823 0 0       0 if ($target eq 'file') {
    0          
    0          
    0          
2824 0 0       0 if ($file=~/\.gz\s*$/) {
2825 0         0 $doc->setCompression(6);
2826             } else {
2827 0         0 $doc->setCompression(-1);
2828             }
2829 0 0       0 $doc->toFile($file,$INDENT)
2830             or _err("Saving $file failed!"); # should be document-encoding encoded
2831             # TODO: we should set the URL here
2832             } elsif ($target eq 'pipe') {
2833 0         0 $file=~s/^\s*\|?//g;
2834 0   0     0 open my $F,"| $file" || die "Cannot open pipe to $file\n";
2835 0         0 $doc->toFH($F,$INDENT);
2836 0         0 close $F;
2837             } elsif ($target eq 'string') {
2838 0         0 return toUTF8($_xml_module->doc_encoding($doc),
2839             $doc->toString($INDENT));
2840             } elsif ($target eq 'print') {
2841 0         0 out($doc->toString($INDENT));
2842             }
2843             } elsif ($format eq 'html') {
2844 0         0 my $F;
2845 0 0       0 if ($target eq 'string') {
2846 15     13   100 no strict qw(refs);
  15         378  
  15         19878  
2847 0         0 _assign($1,'');
2848 0 0       0 my $out=
2849             "
2850             "PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"
2851             unless ($_xml_module->has_dtd($doc));
2852 0         0 $out.=toUTF8($_xml_module->doc_encoding($doc), $doc->toStringHTML());
2853 0         0 return $out;
2854             } else {
2855 0 0       0 if ($target eq 'file') {
    0          
    0          
2856 0 0       0 ($F=open_io_file($file)) || die "Cannot open $file\n";
2857             # TODO: we should set the URL here
2858             } elsif ($target eq 'pipe') {
2859 0         0 $file=~s/^\s*\|?//g;
2860 0         0 open $F,"| $file";
2861 0 0       0 $F || die "Cannot open pipe to $file\n";
2862             } elsif ($target eq 'print') {
2863 0         0 $F=$OUT;
2864             }
2865 0 0       0 $F->print("\n")
2866             unless ($_xml_module->has_dtd($doc));
2867 0   0     0 $F->print(fromUTF8($enc, toUTF8($_xml_module->doc_encoding($doc) || 'utf-8',
2868             $doc->toStringHTML())));
2869              
2870 0 0       0 $F->close() unless $target eq 'print';
2871             }
2872             } else {
2873 0         0 die "Unknown format '$format'\n";
2874             }
2875             }
2876 0 0 0     0 print STDERR "Document saved into $target '$file'.\n" unless ($@ or $target eq 'print' or "$QUIET");
      0        
2877 0         0 return 1;
2878             }
2879              
2880              
2881             # create start tag for an element
2882              
2883             ###
2884             ### Workaround of a bug in XML::LibXML:
2885             ### getNamespaces, getName returns prefix only,
2886             ### prefix returns prefix not xmlns, getAttributes contains also namespaces
2887             ### findnodes('namespace::*') returns (namespaces,undef)
2888             ###
2889              
2890             sub start_tag {
2891 18     18 0 48 my ($element,$fold_attrs)=@_;
2892             return "<".$element->nodeName().
2893 0         0 ($fold_attrs ? ((grep { $_->nodeName() ne "xsh:fold" }
2894             $element->attributes()) ? " ..." : "") :
2895 18 0       193 join("",map { " ".$_->nodeName()."=\"".$_->nodeValue()."\"" }
  19 50       311  
    100          
2896             $element->attributes())
2897             )
2898             # findnodes('attribute::*'))
2899             # . join("",map { " xmlns:".$_->getName()."=\"".$_->nodeValue()."\"" }
2900             # $element->can('getNamespaces') ?
2901             # $element->getNamespaces() :
2902             # $element->findnodes('namespace::*')
2903             # )
2904             .($element->hasChildNodes() ? ">" : "/>");
2905             }
2906              
2907             # create close tag for an element
2908             sub end_tag {
2909 13     13 0 98 my ($element)=@_;
2910 13 50       159 return $element->hasChildNodes() ? "getName().">" : "";
2911             }
2912              
2913             # convert a subtree to an XML string to the given depth
2914             sub to_string {
2915 64     64 0 236 my ($node,$depth,$folding,$fold_attrs)=@_;
2916 64         165 my $result;
2917 64 50       635 if ($node) {
2918 64 50 66     1025 if (ref($node) and $_xml_module->is_element($node) and $folding and
      66        
      33        
2919             $node->hasAttributeNS($XML::XSH2::xshNS,'fold')) {
2920 0 0       0 if ($depth>=0) {
2921 0         0 $depth = min($depth,$node->getAttributeNS($XML::XSH2::xshNS,'fold'));
2922             } else {
2923 0         0 $depth = $node->getAttributeNS($XML::XSH2::xshNS,'fold');
2924             }
2925             }
2926              
2927 64 100 66     493 if ($depth<0 and $folding==0) {
    100 66        
    50 100        
    100 66        
      66        
      66        
2928 31 50       397 $result=ref($node) ? $_xml_module->toStringUTF8($node,$INDENT) : $node;
2929             } elsif (ref($node) and $_xml_module->is_element($node) and $depth==0) {
2930 7 100       48 $result=start_tag($node,$fold_attrs).
2931             ($node->hasChildNodes() ? "...".end_tag($node) : "");
2932             } elsif (ref($node) and $_xml_module->is_document($node) and $depth==0) {
2933 0         0 $result=to_string($node,1,$folding,$fold_attrs);
2934             } elsif ($depth>0 or $folding) {
2935 15 50       66 if (!ref($node)) {
    100          
    50          
2936 0         0 $result=$node;
2937             } elsif ($_xml_module->is_element($node)) {
2938             $result= start_tag($node).
2939 11         37 join("",map { to_string($_,$depth-1,$folding,$fold_attrs) } $node->childNodes).
  13         216  
2940             end_tag($node);
2941             } elsif ($_xml_module->is_document($node)) {
2942 4 50 33     90 if ($node->can('getVersion') and $node->can('getEncoding')) {
2943 4         42 $result=_xml_decl($node,undef,undef)."\n";
2944             }
2945             $result.=
2946 5         35 join("\n",map { to_string($_,$depth-1,$folding,$fold_attrs) }
2947 4 50       27 grep { $SKIP_DTD ? !$_xml_module->is_dtd($_) : 1 } $node->childNodes);
  5         147  
2948             } else {
2949 0         0 $result=$_xml_module->toStringUTF8($node,$INDENT);
2950             }
2951             } else {
2952 11 50       69 $result = ref($node) ? $_xml_module->toStringUTF8($node,$INDENT) : $node;
2953             }
2954             }
2955 64         1390 return $result;
2956             }
2957              
2958             # list nodes matching given XPath argument to a given depth
2959             sub list {
2960 33     33 0 180 my ($opts,$exp)=@_;
2961 33         338 my $opts = _ev_opts($opts);
2962 33 100       395 $opts->{depth} = ($exp eq '' ? 1 : -1) unless exists($opts->{depth});
    100          
2963 33 100       168 $exp = '.' if $exp eq '';
2964 33 0 33     143 if ($opts->{noindent} and $opts->{indent}) {
2965 0         0 die "Can't use --indent and --no-indent together\n";
2966             }
2967 33 50       108 local $INDENT=1 if $opts->{indent};
2968 33 50       105 local $INDENT=0 if $opts->{'no-indent'};
2969 33         288 my $ql=_ev_nodelist($exp);
2970 33         324 foreach my $node (@$ql) {
2971 46 50       245 print STDERR "checking for folding\n" if "$DEBUG";
2972             my $fold=$opts->{fold} &&
2973 46   0     168 ($_xml_module->is_element($node) || $_xml_module->is_document($node)) &&
2974             $node->findvalue("count(.//\@*[local-name()='fold' and namespace-uri()='$XML::XSH2::xshNS'])");
2975 46 50       122 print STDERR "folding: $fold\n" if "$DEBUG";
2976 46         360 out (to_string($node,$opts->{depth},$fold,$opts->{'fold-attrs'}),"\n");
2977             }
2978 33 50       210 print STDERR "\nFound ",scalar(@$ql)," node(s).\n" unless "$QUIET";
2979              
2980 33         346 return 1;
2981             }
2982              
2983             # list namespaces in scope of the given nodes
2984             sub list_namespaces {
2985 0     0 0 0 my ($opts,$exp) = @_;
2986 0         0 $opts = _ev_opts($opts);
2987 0 0 0     0 my $ql= ($opts->{registered} and $exp eq "") ? [] : _ev_nodelist(defined $exp ? $exp : '.');
    0          
2988 0         0 foreach my $node (@$ql) {
2989 0         0 my $n=$node;
2990 0         0 my %namespaces;
2991 0         0 while ($n) {
2992 0         0 foreach my $ns ($n->getNamespaces) {
2993             $namespaces{$ns->localname()}=$ns->value()
2994 0 0       0 unless (exists($namespaces{$ns->localname()}));
2995             }
2996 0         0 $n=$n->parentNode();
2997             }
2998 0         0 out(pwd($node),":\n");
2999 0         0 foreach (sort { $a cmp $b } keys %namespaces) {
  0         0  
3000             out(" xmlns", ($_ ne "" ? ":" : ""),
3001             $_,"=\"",
3002 0 0       0 $namespaces{$_},"\"\n");
3003             }
3004 0         0 out("\n");
3005             }
3006 0 0       0 if ($opts->{registered}) {
3007 0         0 for (sort keys(%_ns)) {
3008 0         0 out(qq(register-namespace $_ "$_ns{$_}";\n));
3009             }
3010             }
3011 0         0 return 1;
3012             }
3013              
3014             sub mark_fold {
3015 0     0 0 0 my ($opts,$exp)=@_;
3016 0         0 $opts = _ev_opts($opts);
3017 0 0       0 $opts->{depth} = 0 if $opts->{depth} eq "";
3018 0 0       0 $exp = "." if $exp eq "";
3019 0         0 foreach my $node (_ev_nodelist($exp)) {
3020 0 0       0 if ($_xml_module->is_element($node)) {
3021 0         0 my $doc=$node->ownerDocument;
3022 0 0       0 if ($doc) {
3023             # pre-declare xsh namespace
3024 0         0 my $root=$doc->getDocumentElement;
3025 0 0       0 $root->setAttribute('xmlns:xsh',$XML::XSH2::xshNS) if $root;
3026             }
3027 0         0 $node->setAttributeNS($XML::XSH2::xshNS,'xsh:fold',$opts->{depth});
3028             }
3029             }
3030 0         0 return 1;
3031             }
3032              
3033             sub mark_unfold {
3034 0     0 0 0 my ($opts,$exp)=@_;
3035 0         0 foreach my $node (_ev_nodelist($exp)) {
3036 0 0 0     0 if ($_xml_module->is_element($node) and $node->hasAttributeNS($XML::XSH2::xshNS,'fold')) {
3037 0         0 remove_node($node->getAttributeNodeNS($XML::XSH2::xshNS,'fold'));
3038             }
3039             }
3040 0         0 return 1;
3041             }
3042              
3043             # canonicalize nodes matching given XPath
3044             sub c14n {
3045 0     0 0 0 my ($opts,$exp)=@_;
3046 0         0 $opts = _ev_opts($opts);
3047 0   0     0 $exp ||= '.';
3048 0         0 my $ql = _ev_nodelist($exp);
3049 0         0 foreach my $node (@$ql) {
3050 0         0 out($node->toStringC14N($opts->{comments},$opts->{filter}),"\n");
3051             }
3052 0 0       0 print STDERR "\nFound ",scalar(@$ql)," node(s).\n" unless "$QUIET";
3053 0         0 return 1;
3054              
3055             }
3056              
3057             # print canonical xpaths identifying nodes matching given XPath
3058             sub locate {
3059 1     1 0 29 my ($opts,$exp)=@_;
3060 1         39 $opts = _ev_opts($opts);
3061 1         32 my $ql= _ev_nodelist($exp);
3062 1         16 foreach (@$ql) {
3063 9         65 out(pwd($_,$opts->{id}),"\n");
3064             }
3065 1 50       29 print STDERR "\nFound ",scalar(@$ql)," node(s).\n" unless "$QUIET";
3066 1         18 return 1;
3067             }
3068              
3069             # print line numbers of matching nodes
3070             sub print_lineno {
3071 0     0 0 0 my ($opts,$exp)=@_;
3072 0         0 $opts = _ev_opts($opts);
3073 0         0 my $ql=_ev_nodelist($exp);
3074 0         0 foreach (@$ql) {
3075 0         0 out($_->line_number,"\n");
3076             }
3077 0         0 return 1;
3078             }
3079              
3080             # remove nodes matching given XPath from a document and
3081             # remove all their descendants from all nodelists
3082             sub prune {
3083 5     5 0 26 my ($opts,$exp)=@_;
3084 5         25 my $i=0;
3085 5         39 my $ql=_ev_nodelist($exp);
3086 5         55 foreach my $node (@$ql) {
3087 8         47 remove_node($node,get_keep_blanks());
3088 8         26 $i++;
3089             }
3090 5 50       49 print STDERR "removed $i node(s)\n" unless "$QUIET";
3091 5         66 return $i;
3092             }
3093              
3094             # evaluate given perl expression
3095             sub eval_substitution {
3096 0     0 0 0 my ($val,$expr)=@_;
3097 0 0       0 local $_ = $val if defined($val);
3098              
3099 0         0 eval lexicalize("$expr");
3100 0 0       0 die $@ if $@; # propagate
3101 0         0 return $_;
3102             }
3103              
3104             # sort given nodelist according to the given xsh code and perl code
3105             sub perlsort {
3106 11     11 0 29 my ($opts,$exp)=@_;
3107 11         35 my $opts = _hash_opts($opts);
3108 11         35 my $list = _ev_nodelist($exp);
3109 11         71 my @list;
3110 11         34 my $old_context = _save_context();
3111 11         29 my $pos=1;
3112 11         31 my $rl = _prepare_result_nl();
3113 11 100       77 if ($opts->{compare}) {
3114 5         16 foreach (qw(numeric descending)) {
3115 10 50       31 die "sort cannot use --$_ with --compare at the same time\n" if (exists($opts->{$_}));
3116             }
3117             }
3118 11         22 eval {
3119 11 100       30 if (defined($opts->{key})) {
3120 9         29 foreach my $node (@$list) {
3121 36         126 _set_context([$node,0+@$list,$pos]);
3122 36         116 push @list,[$node, _ev_literal($opts->{key})];
3123 36         80 $pos++;
3124             }
3125             } else {
3126 2         6 @list = map { [$_,to_literal($_)] } @$list;
  8         18  
3127             }
3128 11 100       44 if ($opts->{numeric}) {
    100          
3129 1 50       6 if ($opts->{descending}) {
3130 0         0 @$rl = map { $_->[0] } sort { $b->[1] <=> $a->[1] } @list;
  0         0  
  0         0  
3131             } else {
3132 1         5 @$rl = map { $_->[0] } sort { $a->[1] <=> $b->[1] } @list;
  4         9  
  4         13  
3133             }
3134             } elsif ($opts->{compare}) {
3135 20         46 @$rl = map { $_->[0] }
3136             sort {
3137 5         29 local $XML::XSH2::Map::a = $a->[1];
  23         55  
3138 23         47 local $XML::XSH2::Map::b = $b->[1];
3139 23         72 my $result=eval lexicalize($opts->{compare});
3140 23 50       78 die $@ if ($@); # propagate
3141             $result;
3142             } @list;
3143             } else {
3144 5 100       13 if ($opts->{descending}) {
3145 1 50       5 if ($opts->{locale}) {
3146 15     13   4366 use locale;
  15         4495  
  15         98  
3147 0         0 @$rl = map { $_->[0] } sort { $b->[1] cmp $a->[1] } @list;
  0         0  
  0         0  
3148             } else {
3149 1         6 @$rl = map { $_->[0] } sort { $b->[1] cmp $a->[1] } @list;
  4         9  
  5         11  
3150             }
3151             } else {
3152 4 50       11 if ($opts->{locale}) {
3153 15     13   876 use locale;
  15         350  
  15         69  
3154 0         0 @$rl = map { $_->[0] } sort { $a->[1] cmp $b->[1] } @list;
  0         0  
  0         0  
3155             } else {
3156 4         21 @$rl = map { $_->[0] } sort { $a->[1] cmp $b->[1] } @list;
  16         30  
  19         40  
3157             }
3158             }
3159             }
3160             };
3161 11         28 my $err = $@;
3162 11         17 do {
3163 11         196 local $SIG{INT}=\&flagsigint;
3164 11         51 _set_context($old_context);
3165 11         30 propagate_flagsigint();
3166             };
3167 11 50       35 die $err if $err; # propagate
3168              
3169 11         82 return $rl;
3170             }
3171              
3172             # Evaluate given expression over every node matching given XPath
3173             # and substitute content with the result.
3174             # The element is passed to the expression by its name or value in the $_
3175             # variable.
3176             sub perlmap {
3177 2     2 0 9 my ($opts, $mapexp, $exp)=@_;
3178 2         8 $opts = _ev_opts($opts);
3179 2         9 my $ql=_ev_nodelist($exp);
3180 2         43 my $old_context = _save_context();
3181 2         6 my $pos=1;
3182 2         15 my $size = @$ql;
3183 2         16 my $in_place = $opts->{'in-place'};
3184 2 50       11 @$ql = reverse @$ql if $opts->{reverse};
3185 2         5 eval {
3186 2         15 foreach my $node (@$ql) {
3187 11         53 _set_context([$node,$size,$pos++]);
3188 11 100 33     54 if ($_xml_module->is_attribute($node)) {
    50          
    50          
3189 1         15 my $val = _ev_literal($mapexp, $node->getValue(),$in_place);
3190 1 50       13 $node->setValue($val) if defined $val;
3191             } elsif ($_xml_module->is_element($node)) {
3192 0         0 my $value = _ev($mapexp, $node->textContent(),$in_place);
3193 0 0       0 if (defined($value)) {
3194             # prune content
3195 0         0 for my $child ($node->childNodes()) {
3196 0         0 $child->unbindNode();
3197             }
3198 0 0       0 if (ref($value)) {
3199 0 0       0 if (UNIVERSAL::isa($value,'XML::LibXML::NodeList')) {
    0          
3200 0         0 foreach my $n (@$value) {
3201 0 0 0     0 if ($_xml_module->is_document_fragment($n) or
      0        
3202             $n->parentNode and
3203             $_xml_module->is_document_fragment($n->parentNode)) {
3204             # it's a fragment
3205 0         0 $node->appendChild($n);
3206             } else {
3207             # safely insert a copy
3208 0         0 insert_node($n,$node,undef,'into',undef,undef);
3209             }
3210             }
3211             } elsif (UNIVERSAL::isa($value,'XML::LibXML::Node')) {
3212 0         0 insert_node($value,$node,undef,'into',undef,undef);
3213             } else {
3214 0         0 $node->appendTextNode(to_literal($value));
3215             }
3216             } else {
3217 0         0 $node->appendTextNode($value);
3218             }
3219             }
3220             } elsif ($node->can('setData') and $node->can('getData')) {
3221 10         51 my $val = _ev_literal($mapexp, $node->getData(),$in_place);
3222 10 50       97 $node->setData($val) if defined $val;
3223             }
3224             }
3225             };
3226 2         6 my $err = $@;
3227             {
3228 2         2 local $SIG{INT}=\&flagsigint;
  2         36  
3229 2         10 _set_context($old_context);
3230 2         6 propagate_flagsigint();
3231             }
3232 2 50       9 die $err if $err; # propagate
3233              
3234 2         56 return 1;
3235             }
3236              
3237             sub hash {
3238 0     0 0 0 my ($opts, $mapexp, $exp)=@_;
3239 0         0 $opts = _ev_opts($opts);
3240 0         0 my $ql=_ev_nodelist($exp);
3241 0         0 my $old_context = _save_context();
3242 0         0 my $pos=1;
3243 0         0 my $size = @$ql;
3244 0         0 my $hash = {};
3245 0         0 eval {
3246 0         0 foreach my $node (@$ql) {
3247 0         0 _set_context([$node,$size,$pos++]);
3248 0         0 my $key = _ev($mapexp, $node);
3249 0 0       0 if (exists($hash->{$key})) {
3250 0         0 $hash->{$key}->push($node);
3251             } else {
3252 0         0 $hash->{$key} = XML::LibXML::NodeList->new($node);
3253             }
3254             }
3255             };
3256 0         0 my $err = $@;
3257             {
3258 0         0 local $SIG{INT}=\&flagsigint;
  0         0  
3259 0         0 _set_context($old_context);
3260 0         0 propagate_flagsigint();
3261             }
3262 0 0       0 die $err if $err; # propagate
3263              
3264 0         0 return $hash;
3265             }
3266              
3267              
3268             #
3269             sub _ev_namespace {
3270 87     87   253 my ($val)=@_;
3271 87 50       401 if (UNIVERSAL::isa($val,'XML::LibXML::NodeList')) {
    50          
3272 0         0 die "Namespace cannot be specified as a node-set!";
3273             } elsif (ref($val)) {
3274 0         0 return to_literal($val);
3275             } else {
3276 87         181 return $val;
3277             }
3278             }
3279              
3280             sub perlrename {
3281 4     4 0 13 my ($opts, $nameexp, $exp)=@_;
3282 4         14 $opts = _ev_opts($opts);
3283 4         17 my $ns = _ev_namespace($opts->{namespace});
3284 4         15 my $ql=_ev_nodelist($exp);
3285 4         32 my $old_context = _save_context();
3286 4         8 my $pos=1;
3287 4         10 my $size = @$ql;
3288 4 50       13 @$ql = reverse @$ql if $opts->{reverse};
3289 4         8 my $in_place = $opts->{'in-place'};
3290 4         7 eval {
3291 4         10 foreach my $node (@$ql) {
3292 14         54 _set_context([$node,$size,$pos++]);
3293 14 50 100     58 if ($_xml_module->is_attribute($node) ||
      66        
3294             $_xml_module->is_element($node) ||
3295             $_xml_module->is_pi($node)) {
3296 14 50       81 if ($node->can('setName')) {
3297 14         72 my $name=$node->getName();
3298 14         29 my $old_name = $name;
3299 14         30 $name = _ev_string($nameexp,$name,$in_place);
3300 14 50       37 if (defined $name) {
3301             # If it is an attribute, check there is no attribute
3302             # with the same name already.
3303 14 50 66     53 if ($_xml_module->is_attribute($node) and
      33        
      66        
3304             $old_name ne $name and
3305             $node->getOwnerElement()
3306             ->hasAttributeNS($ns || $node->namespaceURI(), $name)) {
3307 0         0 _err "Cannot rename attribute '$old_name' to '$name': ",
3308             "An attribute with same name already exists!";
3309             } else {
3310 14         99 $node->setName($name);
3311 14 50 33     58 if (defined($ns) && $node->nodeName=~/^([^:]+):(.*)$/) {
3312 0         0 $node->setNamespace($ns,$1,1);
3313             }
3314             }
3315             }
3316             } else {
3317 0         0 _err "Node renaming not supported by ",ref($node);
3318             }
3319             }
3320             }
3321             };
3322 4         24 my $err = $@;
3323             {
3324 4         15 local $SIG{INT}=\&flagsigint;
  4         69  
3325 4         21 _set_context($old_context);
3326 4         10 propagate_flagsigint();
3327             }
3328 4 50       17 die $err if $err; # propagate
3329              
3330 4         78 return 1;
3331             }
3332              
3333              
3334             ############### AUXILIARY FUNCTIONS ###############
3335              
3336             sub set_attr_ns {
3337 29     29 0 83 my ($node,$ns,$name,$value)=@_;
3338 29 50       68 if ($ns eq "") {
3339 29         87 $node->setAttribute($name,$value);
3340             } else {
3341 0         0 $node->setAttributeNS("$ns",$name,$value);
3342             }
3343             }
3344              
3345             # return NS prefix used in the given name
3346             sub name_prefix {
3347 30 50   30 0 164 if ($_[0]=~/^([^:]+):/) {
3348 0         0 return $1;
3349             }
3350             }
3351              
3352             # try to safely clone a node
3353             sub node_copy {
3354 97     97 0 211 my ($node,$ns,$dest_doc,$dest)=@_;
3355              
3356 97         147 my $copy;
3357 97 100 100     245 if ($_xml_module->is_element($node) and !$node->hasChildNodes) {
    50          
3358             # -- prepare NS
3359 25 50       144 $ns=$node->namespaceURI() if ($ns eq "");
3360 25         546 my $prefix = name_prefix($node->getName);
3361 25 50 33     121 if ($ns eq "" and $prefix ne "") {
3362 0         0 $ns=$dest->lookupNamespaceURI($prefix);
3363             }
3364             # --
3365             $copy=new_element($dest_doc,$node->getName(),$ns,
3366 25 50       148 [map { [$_->nodeName(),$_->nodeValue(),
  20         307  
3367             $_xml_module->is_attribute($_) ?
3368             $_->namespaceURI() : ""
3369             ] } $node->attributes],$dest);
3370             } elsif ($_xml_module->is_document_fragment($node)) {
3371 0         0 $copy=$_parser->parse_xml_chunk($node->toString());
3372             } else {
3373 72         234 $copy=$_xml_module->clone_node($dest_doc,$node);
3374             }
3375             }
3376              
3377             # get element-children of a node (e.g. of a document fragment)
3378             sub get_subelements {
3379 0     0 0 0 my ($docfrag)=@_;
3380 0         0 return grep { $_xml_module->is_element($_) } $docfrag->childNodes();
  0         0  
3381             }
3382              
3383             sub get_following_siblings {
3384 4     4 0 13 my ($node)=@_;
3385 4         8 my @siblings;
3386 4         25 $node=$node->nextSibling();
3387 4         64 while ($node) {
3388 2         13 push @siblings,$node;
3389 2         13 $node=$node->nextSibling();
3390             }
3391 4         12 return @siblings;
3392             }
3393              
3394             # create new document element before the given nodelist
3395             sub new_document_element {
3396 7     7 0 17 my ($doc,$node,@nodelist)=@_;
3397 7         38 $doc->setDocumentElement($node);
3398 7         107 foreach my $n (reverse @nodelist) {
3399 7         40 $doc->removeChild($n);
3400 7         59 $doc->insertAfter($n,$node);
3401             }
3402             }
3403              
3404             # replace document element with a new one
3405             sub replace_document_element {
3406 3     3 0 8 my ($old, $new)=@_;
3407 3         8 my $doc=$_xml_module->owner_document($old);
3408 3         25 my @after_nodes = $old->findnodes('following::node()');
3409 3         184 $old->unbindNode();
3410 3         9 new_document_element($doc,$new,@after_nodes);
3411             }
3412              
3413             # safely insert source node after, before or instead of the
3414             # destination node. Safety means here that nodes inserted on the
3415             # document level are given special care. the source node may only be
3416             # a document fragment, element, text, CDATA, Comment, Entity or
3417             # a PI (i.e. not an attribute).
3418              
3419             sub safe_insert {
3420 86     86 0 194 my ($source,$dest,$where) = @_;
3421 86         306 my $parent=$dest->parentNode();
3422 86 50       244 return unless $parent;
3423 86 100       558 if ($_xml_module->is_document($parent)) {
3424              
3425             # placing a node on the document-level
3426             # SOURCE: Element
3427 7 100 66     34 if ($_xml_module->is_element($source)) {
    50 33        
      33        
3428 4 50       24 if ($where eq 'after') {
    50          
    50          
3429 0 0       0 if ($parent->getDocumentElement()) {
3430 0         0 die("Error: cannot insert another element into /:\n",
3431             " there's one document element already!");
3432             } else {
3433 0         0 new_document_element($parent,$source,
3434             get_following_siblings($dest));
3435             }
3436 0         0 return 'keep';
3437             } elsif ($where eq 'before') {
3438 0 0       0 if ($parent->getDocumentElement()) {
3439 0         0 die("Error: cannot insert another element into /:\n",
3440             " there's one document element already!");
3441             } else {
3442 0         0 new_document_element($parent,$source,
3443             $dest,get_following_siblings($dest));
3444             }
3445 0         0 return 'keep';
3446             } elsif ($where eq 'replace') {
3447             # maybe we are loosing the document element here !
3448 4 100       21 if ($parent->getDocumentElement()) {
3449 2 50       11 if ($_xml_module->is_element($dest)) {
3450 2         19 my @nextnodes = get_following_siblings($dest);
3451 2         11 $dest->unbindNode();
3452 2         7 new_document_element($parent,$source, @nextnodes);
3453             } else {
3454 0         0 die("Error: cannot insert another element into /:\n",
3455             " there's one document element already!");
3456             }
3457             } else {
3458 2         11 new_document_element($parent,$source,
3459             $dest,get_following_siblings($dest));
3460             }
3461 4         12 return 'remove';
3462             }
3463             } # SOURCE: PI or Comment or DocFragment with PI's or Comments
3464             elsif ($_xml_module->is_pi($source) ||
3465             $_xml_module->is_comment($source) ||
3466             $_xml_module->is_entity_reference($source) ||
3467             $_xml_module->is_document_fragment($source)) {
3468             # placing a node into an element
3469 3 100       26 if ($where eq 'after') {
    100          
    50          
3470 1         10 $parent->insertAfter($source,$dest);
3471 1         8 return 'keep';
3472             } elsif ($where eq 'before') {
3473 1         12 $parent->insertBefore($source,$dest);
3474 1         8 return 'keep';
3475             } elsif ($where eq 'replace') {
3476             # maybe we are loosing the document element here !
3477 1         14 $parent->insertBefore($source,$dest);
3478 1         37 return 'remove';
3479             }
3480             } else {
3481 0         0 die("Error: cannot insert node ",ref($source)," on a document level");
3482             }
3483             } else {
3484 79 100       288 if ($where eq 'after') {
    50          
    50          
3485 51         303 $parent->insertAfter($source,$dest);
3486 51         189 return 'keep';
3487             } elsif ($where eq 'before') {
3488 0         0 $parent->insertBefore($source,$dest);
3489 0         0 return 'keep';
3490             } elsif ($where eq 'replace') {
3491 28         157 $parent->insertBefore($source,$dest);
3492 28         71 return 'remove';
3493             }
3494             }
3495             }
3496              
3497             sub _expand_fragment {
3498 0 0   0   0 return $_xml_module->is_document_fragment($_[0]) ?
3499             $_[0]->childNodes : $_[0];
3500             }
3501              
3502             sub _is_attached {
3503 0     0   0 my ($node)=@_;
3504 0   0     0 while ($node and !$_xml_module->is_document_fragment($node)
      0        
3505             and !$_xml_module->is_document($node)) {
3506 0         0 $node=$node->parentNode;
3507             }
3508 0   0     0 return $node && !$_xml_module->is_document_fragment($node);
3509             }
3510              
3511             sub set_namespace {
3512 0     0 0 0 my ($opts,$uri)=@_;
3513 0         0 $opts = _ev_opts($opts);
3514 0         0 $uri = _ev_string($uri);
3515 0         0 my $node = $_xpc->getContextNode;
3516 0         0 my $prefix = $opts->{prefix};
3517 0 0 0     0 unless ($_xml_module->is_element($node) ||
3518             $_xml_module->is_attribute($node)) {
3519 0         0 die "set_namespace: namespaces can only be set for element and attribute nodes\n";
3520             }
3521 0 0       0 if (defined $prefix) {
3522 0         0 my $declaredURI = $node->lookupNamespaceURI($prefix);
3523 0 0 0     0 if (defined $declaredURI and $declaredURI eq $uri) {
3524 0         0 return $node->setNamespace($uri,$prefix);
3525             } else {
3526 0         0 die "Namespace error: prefix '$prefix' already used for the namespace '$declaredURI'\n";
3527             }
3528             } else {
3529 0 0       0 if (defined($prefix = $node->lookupNamespacePrefix($uri))) {
3530 0         0 return $node->setNamespace($uri,$prefix);
3531             } else {
3532 0         0 die "Namespace error: use declare-ns command to declare a prefix for '$uri' first\n";
3533             }
3534             }
3535             }
3536              
3537             sub declare_namespace {
3538 0     0 0 0 my ($opts,$prefix,$uri)=@_;
3539 0         0 $prefix = _ev_string($prefix);
3540 0         0 $uri = _ev_string($uri);
3541 0         0 my $node = $_xpc->getContextNode;
3542 0 0       0 unless ($_xml_module->is_element($node)) {
3543 0         0 die "declare-ns: namespaces can only be declared on element nodes\n";
3544             }
3545 0         0 my $declaredURI = $node->lookupNamespaceURI($prefix);
3546 0 0 0     0 if (defined $declaredURI and $declaredURI ne $uri) {
3547 0         0 die "Namespace error: prefix '$prefix' already used for the namespace '$declaredURI'\n";
3548             }
3549 0         0 $node->setNamespace($uri,$prefix,0);
3550             }
3551              
3552             sub change_namespace_prefix {
3553 0     0 0 0 my ($opts,$new,$old)=@_;
3554 0 0       0 $old = _ev_string($old) if $old;
3555 0         0 $new = _ev_string($new);
3556 0         0 my $node = $_xpc->getContextNode;
3557 0 0 0     0 if ($node && $_xml_module->is_element($node)) {
3558 0 0       0 $old = $node->prefix unless defined $old;
3559 0         0 return $node->setNamespaceDeclPrefix($old,$new);
3560             } else {
3561 0         0 _err("The context node is not an element");
3562             }
3563             }
3564              
3565             sub change_namespace_uri {
3566 0     0 0 0 my ($opts,$uri,$prefix)=@_;
3567 0 0       0 $prefix = _ev_string($prefix) if $prefix;
3568 0         0 $uri = _ev_string($uri);
3569 0         0 my $node = $_xpc->getContextNode;
3570 0 0 0     0 if ($node && $_xml_module->is_element($node)) {
3571 0 0       0 $prefix = $node->prefix unless defined $prefix;
3572 0         0 return $node->setNamespaceDeclURI($prefix,$uri);
3573             } else {
3574 0         0 _err("The context node is not an element");
3575             }
3576             }
3577              
3578             # use the XPathToXML module to build
3579             # up a XML structure
3580             sub xpath_set {
3581 8     8 0 23 my ($opts,$exp,$value)=@_;
3582 8         1781 require XML::XSH2::XPathToXML;
3583 8         36 my $xtx = XML::XSH2::XPathToXML->new(namespaces => \%_ns,
3584             XPathContext => $_xpc,
3585             node => xsh_context_node(),
3586             );
3587 8         25 $value = _ev($value);
3588 8         25 $exp = _expand($exp);
3589 8 50 66     45 if (ref($value) and UNIVERSAL::isa($value,'XML::LibXML::NodeList')) {
3590 0         0 my $result = $xtx->createNode($exp);
3591 0 0       0 if ($_xml_module->is_element($result)) {
3592             # if it's an element, try to clone or attach given nodes
3593 0         0 foreach my $node (@$value) {
3594 0 0 0     0 if ($_xml_module->is_document_fragment($node) or
      0        
3595             $node->parentNode and
3596             $_xml_module->is_document_fragment($node->parentNode)) {
3597             # it's a fragment
3598 0         0 $result->appendChild($node);
3599             } else {
3600             # safely insert a copy
3601 0         0 insert_node($node,$result,undef,'into',undef,undef);
3602             }
3603             }
3604             } else {
3605 0         0 $result->setValue(to_literal($value));
3606             }
3607 0         0 return $result;
3608             } else {
3609 8         23 return $xtx->createNode($exp,to_literal($value));
3610             }
3611             }
3612              
3613             # insert given node to given destination performing
3614             # node-type conversion if necessary
3615             sub insert_node {
3616 134     134 0 373 my ($node,$dest,$dest_doc,$where,$ns,$rl)=@_;
3617 134 50       440 if ($_xml_module->is_document($node)) {
3618 0         0 die "Error: Can't insert/copy/move document nodes!\n";
3619             }
3620 134 50       352 if (!defined($dest_doc)) {
3621 134         416 $dest_doc = $_xml_module->owner_document($dest);
3622             }
3623             # destination: Attribute
3624 134 100 33     422 if ($_xml_module->is_attribute($dest)) {
    50 33        
    100 0        
    50 0        
3625             # source: Text, CDATA, Comment, Entity, Element
3626 26 100 66     63 if ($_xml_module->is_text($node) ||
    50 100        
      100        
      100        
3627             $_xml_module->is_cdata_section($node) ||
3628             $_xml_module->is_comment($node) ||
3629             $_xml_module->is_element($node) ||
3630             $_xml_module->is_pi($node)) {
3631 23 100       60 my $val = $_xml_module->is_element($node) ?
3632             $node->textContent() : $node->getData();
3633 23 50 33     196 if ($where eq 'replace' or $where eq 'into') {
    50 33        
    50 33        
3634 0         0 $val=~s/^\s+|\s+$//g;
3635             # xcopy will replace the value several times, which may not be intended
3636 0         0 set_attr_ns($dest->ownerElement(),$dest->namespaceURI(),$dest->getName(),$val);
3637 0 0       0 push @$rl,$dest->ownerElement()->getAttributeNodeNS($dest->namespaceURI(),$dest->getName()) if defined($rl);
3638 0         0 return 'keep'; # as opposed to 'remove'
3639             } elsif ($where eq 'before' or $where eq 'prepend') {
3640 0         0 $val=~s/^\s+//g;
3641 0         0 set_attr_ns($dest->ownerElement(),$dest->namespaceURI(),$dest->getName(),
3642             $val.$dest->getValue());
3643 0 0       0 push @$rl,$dest->ownerElement()->getAttributeNodeNS($dest->namespaceURI(),$dest->getName()) if defined($rl);
3644             } elsif ($where eq 'after' or $where eq 'append') {
3645 23         66 $val=~s/\s+$//g;
3646 23         200 set_attr_ns($dest->ownerElement(),$dest->namespaceURI(),$dest->getName(),
3647             $dest->getValue().$val);
3648 23 50       274 push @$rl,$dest->ownerElement()->getAttributeNodeNS($dest->namespaceURI(),$dest->getName()) if defined($rl);
3649             }
3650              
3651             }
3652             # source: Attribute
3653             elsif ($_xml_module->is_attribute($node)) {
3654 3         18 my $name=$node->getName();
3655 3         15 my $value = $node->getValue();
3656 3 50 33     34 if ($where eq 'replace' or $where eq 'after' or $where eq 'before') {
      33        
3657             # -- prepare NS
3658 3 50       17 $ns=$node->namespaceURI() if ($ns eq "");
3659 3 50 33     17 if ($ns eq "" and name_prefix($name) ne "") {
3660 0         0 $ns=$dest->lookupNamespaceURI(name_prefix($name))
3661             }
3662             # --
3663 3         14 my $elem=$dest->ownerElement();
3664 3         13 set_attr_ns($elem,"$ns",$name,$value);
3665 3 50       43 push @$rl,$elem->getAttributeNodeNS("$ns",$name) if defined($rl);
3666 3 50 33     13 if ($where eq 'replace' and $name ne $dest->getName()) {
3667 0         0 return 'remove'; # remove the destination node in the end
3668             } else {
3669 3         11 return 'keep'; # no need to remove the destination node
3670             }
3671             } else {
3672             # -- prepare NS
3673 0         0 $ns=$dest->namespaceURI(); # given value of $ns is ignored here
3674             # --
3675 0 0       0 if ($where eq 'append') {
    0          
    0          
3676 0         0 set_attr_ns($dest->ownerElement(),"$ns",$dest->getName,$dest->getValue().$value);
3677             } elsif ($where eq 'into') {
3678 0         0 set_attr_ns($dest->ownerElement(),"$ns",$dest->getName(),$value);
3679             } elsif ($where eq 'prepend') {
3680 0         0 set_attr_ns($dest->ownerElement(),"$ns",$dest->getName(),$value.$dest->getValue());
3681             }
3682 0 0       0 push @$rl,$dest->ownerElement()->getAttributeNodeNS("$ns",$dest->getName()) if defined($rl);
3683             }
3684             } else {
3685 0         0 _err("Warning: Ignoring incompatible nodes in insert/copy/move operation:\n",
3686             ref($node)," $where ",ref($dest),"!");
3687 0         0 return 1;
3688             }
3689             }
3690             # destination: Document
3691             elsif ($_xml_module->is_document($dest)) {
3692             # source: Attribute, Text, CDATA
3693 0 0 0     0 if ($_xml_module->is_attribute($node) or
    0 0        
3694             $_xml_module->is_text($node) or
3695             $_xml_module->is_cdata_section($node)
3696             ) {
3697 0         0 _err("Warning: Ignoring incompatible nodes in insert/copy/move operation:\n",
3698             ref($node)," $where ",ref($dest),"!");
3699 0         0 return 1;
3700             } elsif ($_xml_module->is_element($node)) {
3701             # source: Element
3702 0         0 my $copy=node_copy($node,$ns,$dest_doc,$dest);
3703 0         0 my $destnode;
3704             my $newwhere;
3705 0 0       0 if ($where =~ /^(?:after|append|into)/) {
    0          
    0          
3706 0         0 $newwhere='after';
3707 0         0 $destnode=$dest->lastChild();
3708             } elsif ($where =~ /^(?:before|prepend)/) {
3709 0         0 $newwhere='before';
3710 0         0 $destnode=$dest->firstChild();
3711             } elsif ($where eq 'replace') {
3712 0         0 _err("Warning: Ignoring incompatible nodes in insert/copy/move operation:\n",
3713             ref($node)," $where ",ref($dest),"!");
3714 0         0 return 1;
3715             }
3716 0 0       0 push @$rl,_expand_fragment($copy) if defined($rl);
3717 0 0       0 if ($destnode) {
3718 0         0 return safe_insert($copy,$destnode,$newwhere);
3719             } else {
3720 0         0 new_document_element($dest,$copy);
3721 0         0 return 1;
3722             }
3723             } else {
3724             # source: Chunk, PI, Comment, Entity
3725 0         0 my $copy=node_copy($node,$ns,$dest_doc,$dest);
3726 0 0       0 if ($where =~ /^(?:after|append|into)/) {
    0          
    0          
3727             # rather than appendChild which does not work
3728             # for Chunks!
3729 0         0 $dest->insertAfter($copy,$dest->lastChild());
3730             } elsif ($where =~ /^(?:before|prepend)/) {
3731 0         0 $dest->insertBefore($copy,$dest->firstChild());
3732             } elsif ($where eq 'replace') {
3733 0         0 _err("Warning: Ignoring incompatible nodes in insert/copy/move operation:\n",
3734             ref($node)," $where ",ref($dest),"!");
3735 0         0 return 1;
3736             }
3737 0 0       0 push @$rl,_expand_fragment($copy) if (defined($rl));
3738             }
3739             }
3740             # destination: Element
3741             elsif ($_xml_module->is_element($dest)) {
3742             # source: Attribute
3743 92 100       220 if ($_xml_module->is_attribute($node)) {
3744             # -- prepare NS
3745 2 50       18 $ns=$node->namespaceURI() if ($ns eq "");
3746 2 50 33     376 if ($ns eq "" and name_prefix($node->getName) ne "") {
3747 0         0 $ns=$dest->lookupNamespaceURI(name_prefix($node->getName))
3748             }
3749             # --
3750 2 50 33     16 if ($where eq 'into' or $where eq 'append' or $where eq 'prepend') {
    0 33        
3751 2         40 set_attr_ns($dest,"$ns",$node->getName(),$node->getValue());
3752 2 50       35 push @$rl,$dest->getAttributeNodeNS("$ns",$node->getName()) if defined($rl);
3753             } elsif ($where eq 'replace') {
3754 0         0 my $parent=$dest->parentNode();
3755 0 0       0 if ($_xml_module->is_element($parent)) {
3756 0         0 set_attr_ns($dest,"$ns",$node->getName(),$node->getValue());
3757 0 0       0 push @$rl,$dest->getAttributeNodeNS("$ns",$node->getName()) if defined($rl);
3758             } else {
3759 0         0 _err("Warning: Cannot replace ",ref($node)," with ",ref($parent),
3760             ": parent node is not an element!");
3761 0         0 return 1;
3762             }
3763 0         0 return 'remove';
3764             } else {
3765 0         0 _err("Warning: Ignoring incompatible nodes in insert/copy/move operation:\n",
3766             ref($node)," $where ",ref($dest),"!");
3767 0         0 return 1;
3768             # # converting attribute to element
3769             # my $new=new_element($dest_doc,$node->getName(),$ns,$dest);
3770             # $new->appendText($node->getValue());
3771             # my $parent=$dest->parentNode();
3772             # if ($_xml_module->is_element($parent)) {
3773             # if ($where eq 'before' or $where eq 'after') {
3774             # safe_insert($new,$dest,$where);
3775             # }
3776             # } elsif ($where eq 'append') {
3777             # $dest->appendChild($new);
3778             # } elsif ($where eq 'prepend') {
3779             # $dest->insertBefore($new,$dest->firstChild());
3780             # }
3781             }
3782             }
3783             # source: Any but Attribute
3784             else {
3785 90         302 my $copy=node_copy($node,$ns,$dest_doc,$dest);
3786 90 100 100     603 if ($where eq 'after' or $where eq 'before' or $where eq 'replace') {
    50 100        
    0 33        
3787 47 50       127 push @$rl,_expand_fragment($copy) if defined($rl);
3788 47         139 return safe_insert($copy,$dest,$where);
3789             } elsif ($where eq 'into' or $where eq 'append') {
3790 43         289 $dest->appendChild($copy);
3791 43 50       209 push @$rl,_expand_fragment($copy) if defined($rl);
3792             } elsif ($where eq 'prepend') {
3793 0 0       0 if ($dest->hasChildNodes()) {
3794 0         0 $dest->insertBefore($copy,$dest->firstChild());
3795             } else {
3796 0         0 $dest->appendChild($copy);
3797             }
3798 0 0       0 push @$rl,_expand_fragment($copy) if defined($rl);
3799             }
3800             }
3801             }
3802             # destination: Text, CDATA, Comment, PI
3803             elsif ($_xml_module->is_text($dest) ||
3804             $_xml_module->is_cdata_section($dest) ||
3805             $_xml_module->is_comment($dest) ||
3806             $_xml_module->is_pi($dest) ||
3807             $_xml_module->is_entity_reference($dest)
3808             ) {
3809 16 50 33     111 if ($where =~ /^(?:into|append|prepend)$/ and
      66        
3810             ($_xml_module->is_entity_reference($dest) ||
3811             $_xml_module->is_entity_reference($node))) {
3812 0         0 _err("Warning: Ignoring incompatible nodes in insert/copy/move operation:\n",
3813             ref($node)," $where ",ref($dest),"!");
3814 0         0 return 1;
3815             }
3816 16 50 33     62 if ($where eq 'into') {
    100          
    50          
    50          
3817 0 0       0 my $value=$_xml_module->is_element($node) ?
3818             $node->textContent() : $node->getData();
3819 0 0       0 $value = "" unless defined $value;
3820 0         0 $dest->setData($value);
3821 0 0       0 push @$rl,$dest if defined($rl);
3822             } elsif ($where eq 'append') {
3823 9 50       24 my $value=$_xml_module->is_element($node) ?
3824             $node->textContent() : $node->getData();
3825 9         62 $dest->setData($dest->getData().$value);
3826 9 50       34 push @$rl,$dest if defined($rl);
3827             } elsif ($where eq 'prepend') {
3828 0 0       0 my $value=$_xml_module->is_element($node) ?
3829             $node->textContent() : $node->getData();
3830 0         0 $dest->setData($value.$dest->getData());
3831 0 0       0 push @$rl,$dest if defined($rl);
3832             }
3833             # replace + source: Attribute
3834             elsif ($where eq 'replace' and $_xml_module->is_attribute($node)) {
3835 0         0 my $parent=$dest->parentNode();
3836             # -- prepare NS
3837 0 0       0 $ns=$node->namespaceURI() if ($ns eq "");
3838 0 0 0     0 if ($ns eq "" and name_prefix($node->getName) ne "") {
3839 0         0 $ns=$dest->lookupNamespaceURI(name_prefix($node->getName));
3840             }
3841             # --
3842 0 0       0 if ($_xml_module->is_element($parent)) {
3843 0         0 set_attr_ns($dest,"$ns",$node->getName(),$node->getValue());
3844 0 0       0 push @$rl,$dest->getAttributeNodeNS("$ns",$node->getName()) if defined($rl);
3845             }
3846 0         0 return 'remove';
3847             } else {
3848 7         26 my $parent=$dest->parentNode();
3849 7         15 my $new;
3850             # source: Attribute
3851 7 50       16 if ($_xml_module->is_attribute($node)) {
3852 0         0 _err("Warning: Ignoring incompatible nodes in insert/copy/move operation:\n",
3853             ref($node)," $where ",ref($dest),"!");
3854 0         0 return 1;
3855             # # implicit conversion of attribute to element
3856             # # -- prepare NS
3857             # $ns=$node->namespaceURI() if ($ns eq "");
3858             # if ($ns eq "" and name_prefix($node->getName) ne "") {
3859             # $ns=$parent->lookupNamespaceURI(name_prefix($node->getName));
3860             # }
3861             # # --
3862             # $new=new_element($dest_doc,$node->getName(),$ns,$dest);
3863             # $new->appendText($node->getValue());
3864             }
3865             # source: All other
3866             else {
3867 7         17 $new=node_copy($node,$ns,$dest_doc,$dest);
3868             }
3869 7 50       36 if ($where =~ /^(?:after|before|replace)$/) {
3870 7 50       30 push @$rl,_expand_fragment($new) if defined $rl;
3871 7         19 return safe_insert($new,$dest,$where);
3872             }
3873             }
3874             } else {
3875 0         0 print STDERR "Warning: unsupported/unknown destination type: ",ref($dest),"\n";
3876 0 0       0 print STDERR substr($node->toString(),0,200), substr($dest->toString(),0,200),"\n" if ref($dest);
3877             }
3878 77         1475 return 1;
3879             }
3880              
3881             # parse a string and create attribute nodes
3882             sub create_attributes {
3883 18     18 0 43 my ($str)=@_;
3884 18         34 my (@ret,$value,$name);
3885 18         88 while ($str!~/\G$/gsco) {
3886 27 50       139 if ($str=~/\G\s*([^ \n\r\t=]+)=/gsco) {
    0          
3887 27         63 my $name=$1;
3888 27 50       64 print STDERR "attribute_name=$1\n" if $DEBUG;
3889 27 50 100     245 if ($str=~/\G\"((?:[^\\\"]|\\.)*)\"/gsco or
    0 66        
3890             $str=~/\G\'((?:[^\\\']|\\.)*)\'/gsco or
3891             $str=~/\G(.*?)(?=\s+[^ \n\r\t=]+=|\s*$)/gsco) {
3892 27         61 $value=$1;
3893 27         62 $value=~s/\\(.)/$1/g;
3894 27 50       49 print STDERR "creating $name='$value' attribute\n" if $DEBUG;
3895 27         178 push @ret,[$name,$value];
3896             } elsif ($str=~/\G(\s*)$/gsco) {
3897 0         0 $value=$1;
3898 0 0       0 print STDERR "creating $name='$1' attribute\n" if $DEBUG;
3899 0         0 push @ret,[$name,$value];
3900 0         0 last;
3901             } else {
3902 0         0 die "Invalid attribute specification near '".substr($str,pos($str))."'\n";
3903             }
3904             } elsif ($str =~ /\G(.*)/gsco) {
3905 0         0 die "Invalid attribute specification near '$1'\n"
3906             } else {
3907 0         0 last;
3908             }
3909             }
3910 18         66 return @ret;
3911             }
3912              
3913             sub new_element {
3914 25     25 0 204 my ($doc,$name,$ns,$attrs,$dest)=@_;
3915 25         44 my $el;
3916 25         87 my ($prefix,$localname) = $name=~/^([^:>]+):(.*)$/;
3917 25 50 33     88 if ($prefix ne "" and $ns eq "") {
3918 0         0 die "Error: namespace error: undefined namespace prefix `$prefix'\n";
3919             }
3920 25 50 33     74 if ($dest && $_xml_module->is_element($dest)) {
    0          
3921 25 50       65 print STDERR "DEST is element\n" if $DEBUG;
3922 25         133 $el=$dest->addNewChild($ns,$name);
3923            
3924 25 50 33     256 if ($prefix eq "" and $ns eq "" and $dest->lookupNamespaceURI(undef) ne "") {
      33        
3925 0 0       0 print STDERR "CLEAR Default NS\n" if $DEBUG;
3926 0         0 $el->setNamespace('','',1);
3927             } else {
3928 25 50       68 print STDERR "prefix: $prefix, ns: $ns, lookup: ",$dest->lookupNamespaceURI(undef),".\n" if $DEBUG;
3929 25 50       56 print STDERR $dest->toString(1),"\n" if $DEBUG;
3930             }
3931 25         126 $el->unbindNode();
3932             } elsif ($ns ne '') {
3933 0 0       0 print STDERR "DEST is not element, NS: $ns\n" if $DEBUG;
3934 0         0 $el=$doc->createElementNS($ns,$name);
3935             } else {
3936 0 0       0 print STDERR "DEST is not element no NS\n" if $DEBUG;
3937 0         0 $el=$doc->createElement($name);
3938             }
3939 25 50       83 if (ref($attrs)) {
3940 25         63 foreach (@$attrs) {
3941 20 50 33     257 if ($ns ne "" and ($_->[0]=~/^\Q${prefix}\E:/)) {
    50          
    50          
    50          
3942 0 0       0 print STDERR "NS: $ns\n" if $DEBUG;
3943 0         0 $el->setAttributeNS($ns,$_->[0],$_->[1]);
3944             } elsif ($_->[0] =~ "xmlns:(.*)") {
3945 0 0       0 print STDERR "xmlns: $1\n" if $DEBUG;
3946             # don't redeclare NS if already declared on destination node
3947 0 0 0     0 unless ($_->[1] eq $ns or $dest->lookupNamespaceURI($1) eq $_->[2]) {
3948 0 0       0 $el->setNamespace($_->[1],$1,0) unless ($_->[1] eq $ns);
3949             }
3950             } elsif ($_->[0] eq "xmlns") {
3951 0 0       0 print STDERR "xmlns: @$_\n" if $DEBUG;
3952             # don't redeclare NS if already declared on destination node
3953 0 0 0     0 unless ($->[1] eq $ns or $dest->lookupNamespaceURI('') eq $_->[2]) {
3954 0 0       0 $el->setNamespace($_->[1],'',0) unless ($_->[1] eq $ns);
3955             }
3956             } elsif ($_->[0]=~/^([^:>]+):/) {
3957 0         0 my $lprefix=$1;
3958 0 0       0 if ($_->[2] ne "") {
3959 0         0 $el->setAttributeNS($_->[2],$_->[0],$_->[1]);
3960             } else {
3961             # add the attribute anyway (may have wrong qname!)
3962 0         0 $el->setAttribute($_->[0],$_->[1]);
3963             }
3964             } else {
3965 20 50 33     66 next if ($_->[0] eq "xmlns:$prefix" and $_->[1] eq $ns);
3966 20         56 $el->setAttribute($_->[0],$_->[1]); # what about other namespaces?
3967             }
3968             }
3969             }
3970 25         265 return $el;
3971             }
3972              
3973             # create nodes from their textual representation
3974             sub create_nodes {
3975 106     106 0 303 my ($type,$str,$doc,$ns)=@_;
3976 106         188 my @nodes=();
3977 106 50       270 die "No document for create $type $str for.\n" unless ref($doc);
3978 106 50 33     374 die "Can't create $type from empty specification.\n"
3979             if ($str eq "" and $type !~ /text|cdata|comment/);
3980             # return undef unless ($str ne "" and ref($doc));
3981 106 100       221 if ($type eq 'chunk') {
3982 9         63 @nodes=map {$_->childNodes()}
3983 9         68 grep {ref($_)} ($_parser->parse_xml_chunk($str));
  9         1756  
3984             } else {
3985 97 100       340 if ($type eq 'attribute') {
    100          
    100          
    50          
    50          
    100          
    50          
3986 5         19 foreach (create_attributes($str)) {
3987 5         9 my $at;
3988 5 50 33     38 if ($_->[0]=~/^([^:]+):/ and $1 ne 'xmlns') {
3989 0 0       0 $ns = get_registered_ns($1) if $ns eq "";
3990 0 0       0 die "Error: undefined namespace prefix `$1'\n" if ($ns eq "");
3991 0         0 $at=$doc->createAttributeNS($ns,$_->[0],$_->[1]);
3992             } else {
3993 5         69 $at=$doc->createAttribute($_->[0],$_->[1]);
3994             }
3995 5         18 push @nodes,$at;
3996             }
3997             } elsif ($type eq 'element') {
3998 57         107 my ($name,$attributes);
3999 57 50       394 if ($str=~/^\]+)(\s+.*)?(?:\/?\>)?\s*$/) {
4000 57 50       133 print STDERR "element_name=$1\n" if $DEBUG;
4001 57 50       124 print STDERR "attributes=$2\n" if $DEBUG;
4002 57         229 my ($elt,$att)=($1,$2);
4003 57         85 my $el;
4004 57 100 66     270 if ($elt=~/^([^:>]+):(.*)$/ or $ns ne "") {
4005 3 50       10 print STDERR "Name: $elt\n" if $DEBUG;
4006 3 50       10 if ($ns eq "") {
4007 0 0       0 print STDERR "NS prefix registered as: $ns\n" if $DEBUG;
4008 0 0       0 $ns = get_registered_ns($1) if $ns eq "";
4009             } else {
4010 3 50       8 print STDERR "NS: $ns\n" if $DEBUG;
4011             }
4012 3 50 33     19 die "Error: undefined namespace prefix `$1'\n" if ($1 ne "" and $ns eq "");
4013 3         32 $el=$doc->createElementNS($ns,$elt);
4014             } else {
4015 54         367 $el=$doc->createElement($elt);
4016             }
4017 57 100       163 if ($att ne "") {
4018 13         111 $att=~s/\/?\>?$//;
4019 13         45 foreach (create_attributes($att)) {
4020 22 50       181 print STDERR "atribute: ",$_->[0],"=",$_->[1],"\n" if $DEBUG;
4021 22 50 33     77 if ($elt=~/^([^:]+):/ and $1 ne 'xmlns') {
4022 0 0       0 print STDERR "NS: $ns\n" if $DEBUG;
4023 0 0       0 die "Error: undefined namespace prefix `$1'\n" if ($ns eq "");
4024 0         0 $el->setAttributeNS($ns,$_->[0],$_->[1]);
4025             } else {
4026 22         85 $el->setAttribute($_->[0],$_->[1]);
4027             }
4028             }
4029             }
4030 57         298 push @nodes,$el;
4031             # __debug("ns: $ns\n".$el->toString());
4032             } else {
4033 0 0       0 print STDERR "invalid element $str\n" unless "$QUIET";
4034             }
4035             } elsif ($type eq 'text') {
4036 19         127 push @nodes,$doc->createTextNode($str);
4037 19 50       51 print STDERR "text=$str\n" if $DEBUG;
4038             } elsif ($type eq 'entity_reference') {
4039 0         0 push @nodes,$doc->createEntityReference($str);
4040 0 0       0 print STDERR "entity_reference=$str\n" if $DEBUG;
4041             } elsif ($type eq 'cdata') {
4042 0         0 push @nodes,$doc->createCDATASection($str);
4043 0 0       0 print STDERR "cdata=$str\n" if $DEBUG;
4044             } elsif ($type eq 'pi') {
4045 8         63 my ($name,$data)=($str=~/^\s*(?:\<\?)?(\S+)(?:\s+(.*?)(?:\?\>)?)?$/);
4046 8 50       26 $data = "" unless defined $data;
4047 8         56 my $pi = $doc->createPI($name,$data);
4048 8 50       24 print STDERR "pi=\n" if $DEBUG;
4049 8         20 push @nodes,$pi;
4050             # print STDERR "cannot add PI yet\n" if $DEBUG;
4051             } elsif ($type eq 'comment') {
4052 8         55 push @nodes,$doc->createComment($str);
4053 8 50       23 print STDERR "comment=$str\n" if $DEBUG;
4054             } else {
4055 0         0 die "unknown type: $type\n";
4056             }
4057             }
4058 106         427 return @nodes;
4059             }
4060              
4061             sub run_editor {
4062 0     0 0 0 my ($data,$editor,$encoding)=@_;
4063 0         0 ($editor) = grep {$_ ne ""} $editor,$ENV{VISUAL},$ENV{EDITOR},'vi';
  0         0  
4064 0 0       0 $encoding = $QUERY_ENCODING unless $encoding;
4065 0         0 my $dir = tempdir( CLEANUP => 1 );
4066 0         0 my ($fh, $filename) = tempfile( DIR => $dir );
4067 0         0 binmode $fh,'bytes';
4068 0         0 $fh->print(fromUTF8($encoding,$data));
4069 0 0       0 $fh->flush if $fh->can('flush');
4070 0         0 close($fh);
4071 0 0       0 if (system($editor." ".$filename) == 0) {
4072 0         0 open $fh,$filename;
4073 0         0 binmode $fh,'bytes';
4074 0         0 $data= join "",map toUTF8($encoding,$_),<$fh>;
4075 0         0 close $fh;
4076             } else {
4077 0         0 $data=undef;
4078             }
4079 0         0 unlink $filename;
4080 0         0 unlink $dir;
4081 0         0 return $data;
4082             }
4083              
4084             sub ask_user {
4085 0     0 0 0 my ($question, $answers) = @_;
4086 0         0 print STDERR $question;
4087 0         0 STDERR->flush;
4088 0         0 my $reply = ;
4089 0         0 chomp $reply;
4090 0 0       0 if ($answers ne "") {
4091 0         0 while ($reply !~ /^$answers$/) {
4092 0         0 print STDERR "Answer ",join("/",split(/\|/,$answers)),": ";
4093 0         0 STDERR->flush;
4094 0         0 $reply = ;
4095 0         0 chomp $reply;
4096             }
4097             }
4098 0         0 return $reply;
4099             }
4100              
4101             ############### END OF AUXILIARY FUNCTIONS ###############
4102              
4103             sub edit {
4104 0     0 0 0 my ($opts,$exp,$variable)=@_;
4105 0         0 $opts = _ev_opts($opts);
4106 0         0 my $rl = _prepare_result_nl();
4107 0         0 my $ql;
4108 0 0       0 unless ($variable) {
4109 0 0       0 $exp = '.' if $exp eq '';
4110 0         0 $ql =_ev_nodelist($exp);
4111 0 0       0 unless (@$ql) {
4112 0         0 _warn("No nodes matching $exp");
4113 0         0 return $rl;
4114             }
4115             # prune nodes included in subtrees of already present nodes
4116             # cause they would get replaced anyway
4117 0         0 my %n;
4118             $ql = [ grep {
4119 0         0 my $d=$_; my $ret=1;
  0         0  
  0         0  
4120 0         0 while ($d) {
4121 0 0       0 if (exists ($n{$$d})) { $ret = 0; last; }
  0         0  
  0         0  
4122 0         0 else { $d=$d->parentNode; }
4123             }
4124 0         0 $n{$$_}=1; $ret } @$ql ];
  0         0  
4125             }
4126 0         0 my $data;
4127 0         0 my $node_idx = 0;
4128 0         0 my $fix;
4129             my $node;
4130 0 0       0 my $nodes = scalar(@$ql) unless $variable;
4131 0   0     0 while ($variable or ($node = $ql->[$node_idx++])) {
4132 0 0       0 if ($variable) {
4133 0         0 $data=_ev_literal($exp)
4134             } else {
4135 0         0 my $pwd = pwd($node);
4136 0 0       0 if ($fix) {
4137 0         0 undef $fix;
4138             } else {
4139 0 0 0     0 if ($_xml_module->is_attribute($node)) {
    0 0        
      0        
      0        
4140 0         0 $data=$node->value;
4141             } elsif ($_xml_module->is_element($node) or
4142             $_xml_module->is_document($node) or
4143             $_xml_module->is_text_or_cdata($node) or
4144             $_xml_module->is_comment($node) or
4145             $_xml_module->is_pi($node)) {
4146 0 0       0 $data=$_xml_module->toStringUTF8($node,$opts->{noindent} ? 0 :$INDENT);
4147             } else {
4148 0         0 die("Cannot edit ".ref($node)."\n");
4149             }
4150             }
4151             $data="\n"
4153 0 0       0 .$data unless $opts->{'no-comment'};
    0          
4154             }
4155 0         0 my $replacement = run_editor($data,$opts->{editor},$opts->{encoding});
4156 0 0       0 $replacement =~ s/^\s*[ \t]*\n?// unless $variable;
4157 0 0       0 chomp $replacement unless $variable;
4158 0   0     0 while ($replacement eq "" and not($opts->{'allow-empty'})) {
4159 0 0       0 if (-t) {
4160 0         0 my $response = ask_user("Result is empty! Is that correct? (yes/no/stop): ",
4161             "y|n|s|yes|no|stop");
4162 0 0       0 if ($response =~ /^y/) {
    0          
4163 0         0 last;
4164             } elsif ($response =~ /^s/) {
4165 0 0       0 return $variable ? $data : $rl;
4166             } else {
4167 0         0 $replacement = run_editor($data,$opts->{editor},$opts->{encoding});
4168 0         0 $replacement =~ s/^\s*[ \t]*\n?//;
4169             }
4170             } else {
4171 0         0 die("Result is empty, ignoring changes!\n".
4172             "Hint: use --allow-empty option or remove command.\n");
4173             }
4174             }
4175 0 0       0 if ($variable) {
    0          
4176 0 0       0 if ($exp) {
4177 0         0 _assign($exp,$replacement);
4178             }
4179 0         0 return $replacement;
4180             } elsif ($_xml_module->is_attribute($node)) {
4181 0 0       0 $node->setValue($replacement) if defined $replacement;
4182 0 0       0 push @$rl, $node if defined $rl;
4183             } else {
4184 0 0       0 local $RECOVERING=$opts->{recover} ? 1 : $RECOVERING;
4185 0 0       0 local $KEEP_BLANKS=$opts->{'keep-blanks'} ? 1 : !$INDENT;
4186 0         0 my $chunk;
4187 0 0       0 if ($_xml_module->is_document($node)) {
4188 0         0 $chunk = eval { $_xml_module->parse_string($_parser,$replacement) };
  0         0  
4189             } else {
4190 0         0 $chunk = eval { $_xml_module->parse_chunk($_parser,$replacement); };
  0         0  
4191             }
4192 0 0 0     0 if ($@ or not ref($chunk)) {
4193 0 0       0 if (-t) {
4194 0         0 my $c = ask_user("$@\n"."Parse error! Press:\n".
4195             " 1 - continue with next node\n".
4196             " 2 - fix the error in the editor\n".
4197             " 3 - restart editor on this node (discarding changes)\n".
4198             " 4 - stop\n\n".
4199             "Your choice: ","1|2|3|4");
4200 0 0       0 if ($c == 1) {
    0          
    0          
4201 0         0 next;
4202             } elsif ($c == 2) {
4203 0         0 $data = $replacement;
4204 0         0 $fix=1;
4205 0         0 redo;
4206             } elsif ($c == 3) {
4207 0         0 redo;
4208             } else {
4209 0         0 return $rl;
4210             }
4211             } else {
4212 0         0 die("$@"."Error parsing result, ignoring changes!\n");
4213             }
4214             }
4215 0 0       0 if ($_xml_module->is_document($node)) {
4216 0         0 foreach my $child ($node->childNodes()) {
4217 0         0 $child->unbindNode();
4218             }
4219 0         0 foreach my $child ($chunk->childNodes()) {
4220 0         0 $child->unbindNode();
4221 0 0       0 if ($_xml_module->is_element($child)) {
4222 0         0 $node->setDocumentElement($child);
4223 0         0 while (my $sibling = $child->nextSibling) {
4224 0         0 $sibling->unbindNode();
4225 0         0 $node->insertBefore($sibling);
4226             }
4227             } else {
4228 0         0 $node->appendChild($child);
4229             }
4230             }
4231 0 0       0 push @$rl, $chunk->childNodes() if defined $rl;
4232             } else {
4233 0 0       0 if (insert_node($chunk,$node,undef,'replace',undef,$rl) eq 'remove') {
4234 0         0 remove_node($node);
4235              
4236             }
4237             }
4238             }
4239             } continue {
4240 0 0 0     0 last if (not(exists($opts->{all})) or $variable);
4241             }
4242 0         0 return $rl;
4243             }
4244              
4245             # copy nodes matching one XPath expression to locations determined by
4246             # other XPath expression
4247             sub copy {
4248 27     27 0 106 my ($opts,$fexp,$where,$texp,$all_to_all)=@_;
4249 27         51 my $fl;
4250 27         81 $opts = _ev_opts($opts);
4251 27         69 $fl=_ev_nodelist($fexp);
4252 27 100       214 unless (@$fl) {
4253 1         10 _warn("No nodes matching $fexp");
4254 1         5 return 1;
4255             }
4256             # respective copying
4257 26         80 my $rl=_prepare_result_nl();
4258 26 100       81 if ($opts->{respective}) {
4259 2         6 my @rtl;
4260 2         31 my $old_context = _save_context();
4261 2         4 eval {
4262 2         6 my $pos=1;
4263 2         5 my $size = @$fl;
4264 2         6 foreach my $fp (@$fl) {
4265 10         88 _set_context([$fp,$size,$pos]);
4266 10         25 my $tl=_ev_nodelist($texp);
4267 10 50       66 unless (@$tl) {
4268 0 0       0 my $th = ($pos%10 == 1 ? "st" : $pos%10 == 2 ? "nd" : "th");
    0          
4269 0         0 _warn("No nodes matching $texp for $pos$th node matching $fexp\n");
4270             }
4271 10 50       21 unless ($all_to_all) { @$tl = ($tl->[0]) }
  0         0  
4272 10         18 push @rtl, $tl;
4273 10         20 $pos++;
4274             }
4275             };
4276 2         8 my $err = $@;
4277 2         3 do {
4278 2         35 local $SIG{INT}=\&flagsigint;
4279 2         12 _set_context($old_context);
4280 2         9 propagate_flagsigint();
4281             };
4282 2 50       8 die $err if $err; # propagate
4283 2   66     18 my $reverse = $opts->{'preserve-order'} && $where=~/^(after|prepend)$/;
4284 2 100       10 foreach my $fp ($reverse ? reverse @$fl : @$fl) {
4285 10 100       91 my $tl = $reverse ? pop(@rtl) : shift(@rtl);
4286 10         23 foreach my $tp (@$tl) {
4287 10         19 my $replace=0;
4288 10   33     28 $replace = ((insert_node($fp,$tp,undef,$where,undef,$rl)
4289             eq 'remove') || $replace);
4290 10 50       353 if ($replace) {
4291 0         0 remove_node($tp);
4292             }
4293             }
4294             }
4295             } else {
4296             # non-respective copying
4297 24         55 my $tl=_ev_nodelist($texp);
4298 24 50       173 unless (@$tl) {
4299 0         0 _warn("No nodes matching $texp");
4300 0         0 return $rl;
4301             }
4302 24 100       60 if ($all_to_all) {
4303 13         23 my $real_fl;
4304 13 100 66     54 if ($opts->{'preserve-order'} && $where=~/^(after|prepend)$/) {
4305 2         9 $real_fl = [ reverse @$fl ];
4306             } else {
4307 11         22 $real_fl = $fl;
4308             }
4309 13         33 foreach my $tp (@$tl) {
4310 13         18 my $replace=0;
4311 13         24 foreach my $fp (@$real_fl) {
4312 41   33     452 $replace = ((insert_node($fp,$tp,undef,$where,undef,$rl)
4313             eq 'remove') || $replace);
4314             }
4315 13 50       301 if ($replace) {
4316 0         0 remove_node($tp);
4317             }
4318             }
4319             } else {
4320 11 50       36 _warn("Different number of source and destination nodes.\n".
4321             "(Maybe you wanted to call xcopy/xmove?)\n".
4322             "Continuing anyway!") if (@$fl != @$tl);
4323 11   66     101 while (ref(my $fp=shift @$fl) and ref(my $tp=shift @$tl)) {
4324 11         70 my $replace=insert_node($fp,$tp,undef,$where,undef,$rl);
4325 11 100       80 if ($replace eq 'remove') {
4326 1         9 remove_node($tp);
4327             }
4328             }
4329             }
4330             }
4331 26         244 return $rl;
4332             }
4333              
4334              
4335             # create new nodes from an expression and insert them to locations
4336             # identified by XPath
4337             sub insert {
4338 60     60 0 190 my ($opts,$type,$str,$where,$exp,$to_all)=@_;
4339 60         167 $opts = _ev_opts($opts);
4340 60         170 $str = _ev_string($str);
4341 60         287 my $ns = _ev_namespace($opts->{namespace});
4342 60         183 my $tl=_ev_nodelist($exp);
4343 60 50       441 unless (@$tl) {
4344 0         0 _warn("Expression '$exp' returns empty node-list");
4345 0         0 return 1;
4346             }
4347 60         154 my $rl = _prepare_result_nl();
4348 60         97 my @nodes;
4349 60         320 @nodes=grep {ref($_)} create_nodes($type,$str,$_xml_module->owner_document($tl->[0]),$ns);
  71         195  
4350 60 50       258 unless (@nodes) {
4351 0         0 _warn("Expression generates no nodes to insert");
4352 0         0 return $rl;
4353             }
4354 60 100       366 if ($to_all) {
    50          
4355 1         42 foreach my $tp (@$tl) {
4356 2         19 my $replace=0;
4357 2         14 foreach my $node (@nodes) {
4358 2   33     30 $replace = (insert_node($node,$tp,undef,$where,undef,$rl) eq 'remove') || $replace;
4359             }
4360 2 50       121 if ($replace) {
4361 0         0 remove_node($tp);
4362             }
4363             }
4364             } elsif ($tl->[0]) {
4365 59         383 foreach my $node (@nodes) {
4366 70 50       310 if (ref($tl->[0])) {
4367 70 50       221 if (insert_node($node,$tl->[0],undef,$where,undef,$rl) eq 'remove') {
4368 0         0 remove_node($tl->[0]);
4369             }
4370             }
4371             }
4372             }
4373 60         631 return $rl;
4374             }
4375              
4376             # wrap nodes into a given element
4377             sub wrap {
4378 16     16 0 79 my ($opts,$str,$exp)=@_;
4379 16         54 $opts = _ev_opts($opts);
4380 16         88 my $ns = _ev_namespace($opts->{namespace});
4381 16         61 $str = _ev_string($str);
4382              
4383 16         69 my $rl=_prepare_result_nl();
4384 16         65 my $ql=_ev_nodelist($exp);
4385 16         92 my %moved;
4386 16         70 foreach my $node (@$ql) {
4387 47 100       372 next if $moved{$$node};
4388 33         151 my ($el) = create_nodes('element',$str,
4389             $_xml_module->owner_document($node),$ns);
4390 33 50       121 if ($opts->{inner}) {
4391 0 0 0     0 if ($opts->{while} or $opts->{until}) {
4392 0         0 die "wrap: cannot use --while or --until together with --inner\n";
4393             }
4394 0 0       0 if ($_xml_module->is_element($node)) {
4395 0         0 my @children = $node->childNodes;
4396 0         0 $node->appendChild($el);
4397 0         0 foreach my $child (@children) {
4398 0         0 $child->unbindNode();
4399 0         0 $el->appendChild($child);
4400             }
4401             }
4402             } else {
4403 33 100       120 if ($_xml_module->is_attribute($node)) {
4404 1 50 33     6 if ($opts->{while} or $opts->{until}) {
4405 0         0 _warn("wrap: ignoring --while or --until on an attribute");
4406             }
4407 1         4 my $parent=$node->ownerElement();
4408 1         15 $parent->insertBefore($el,$parent->firstChild());
4409 1         3 set_attr_ns($el,$node->namespaceURI(),
4410             $node->getName(),$node->getValue());
4411 1         18 $node->unbindNode();
4412             } else {
4413 32         147 my $parent = $node->parentNode();
4414 32         54 my $last = undef;
4415 32 50       91 unless ($parent) {
4416 0         0 die "wrap: cannot wrap node: ".pwd($node)." (node has no parent)\n";
4417             }
4418             # process --while and --until
4419 32 100 100     298 if (defined $opts->{while} or defined $opts->{until}) {
4420 17         32 my $while = $opts->{while};
4421 17         26 my $until = $opts->{until};
4422 17         28 my $skip_comments = $opts->{'skip-comments'};
4423 17         29 my $skip_ws = $opts->{'skip-whitespace'};
4424 17         28 my $skip_pi = $opts->{'skip-pi'};
4425 17         105 my $next = $node->nextSibling;
4426             # evaluate $opts->{while} in the context of the following sibling
4427 17 50       44 if ($next) {
4428 17         99 my $old_context = _save_context();
4429 17         58 eval {
4430             # what should the size be? guess number of all following siblings
4431 17         29 my $pos=1;
4432 17         76 my $size = $node->findvalue('count(following-sibling::node())');
4433 17         417 while ($next) {
4434 38 100 100     339 unless (($skip_ws and $_xml_module->is_text($next) and $next->getData =~ /^\s*$/) or
      66        
      100        
      100        
      100        
      100        
4435             ($skip_comments and $_xml_module->is_comment($next)) or
4436             ($skip_pi and $_xml_module->is_pi($next))) {
4437 31         111 _set_context([$next,$size,$pos]);
4438 31 100       84 if (defined $while) {
4439 23 100       48 last if !_ev_count($while);
4440             }
4441 19 100       272 if (defined $until) {
4442 8         16 my $res = _ev_count($until);
4443 8 100       54 last if $res;
4444             }
4445 18         45 $last = $next;
4446 18         79 $pos++;
4447             }
4448 25         116 $next = $next->nextSibling;
4449             }
4450             };
4451 17         86 my $err = $@;
4452 17         24 do {
4453 17         244 local $SIG{INT}=\&flagsigint;
4454 17         68 _set_context($old_context);
4455 17         42 propagate_flagsigint();
4456             };
4457             }
4458             }
4459 32         196 safe_insert($el,$node,'replace');
4460 32         587 $el->appendChild($node);
4461 32 100       57 if ($last) {
4462 9         149 my $next = $el->nextSibling;
4463 9         28 while ($next) {
4464 23         181 $next->unbindNode();
4465 23         67 $el->appendChild($next);
4466 23         49 $moved{$$next}=1;
4467 23 100       193 last if $next->isSameNode($last);
4468 14         50 $next = $el->nextSibling;
4469             }
4470 9 50       15 unless ($next) {
4471 0         0 _warn("wrap: something went wrong");
4472             }
4473             }
4474             }
4475             }
4476 33 100       560 push @$rl, $el if defined $rl;
4477             }
4478 16         278 return $rl;
4479             }
4480              
4481             # wrap span of nodes into a given element
4482             sub wrap_span {
4483 7     7 0 31 my ($opts,$str,$xp_start,$xp_end)=@_;
4484 7         20 $opts = _ev_opts($opts);
4485 7         19 $str = _ev_string($str);
4486 7         41 my $ns = _ev_namespace($opts->{namespace});
4487 7         25 my $rl=_prepare_result_nl();
4488 7         28 my $ql_start=_ev_nodelist($xp_start);
4489 7         39 my $ql_end=_ev_nodelist($xp_end);
4490 7 50       52 if (@$ql_start != @$ql_end) {
4491 0         0 die "Error: there are ".scalar(@$ql_start)." start nodes, ".
4492             " but ".scalar(@$ql_end)." end nodes!\n";
4493             }
4494 7         36 for (my $i=0; $i<=$#$ql_start; $i++) {
4495 13         96 my $node = $ql_start->[$i];
4496 13         21 my $end_node = $ql_end->[$i];
4497 13 50 33     76 if (not($node->parentNode()) or not($end_node->parentNode())) {
4498 0         0 die "Error: cannot wrap document node\n";
4499             }
4500 13         176 foreach my $n ($node,$end_node) {
4501 26 50       225 if ($_xml_module->is_attribute($n)) {
4502 0         0 die "Error: attribute node ".pwd($n).
4503             " cannot define a node span boundary\n";
4504             }
4505             }
4506 13 50       91 if (not $node->parentNode()->isSameNode($end_node->parentNode())) {
4507 0         0 die "Error: start node ".pwd($node)." and end node ".
4508             pwd($end_node)." have different parents\n";
4509             }
4510 13         45 my ($el) = create_nodes('element',$str,
4511             $_xml_module->owner_document($node),$ns);
4512 13         48 my $parent = $node->parentNode();
4513 13         22 my @span;
4514 13         19 my $n=$node;
4515 13         35 while ($n) {
4516 23         112 push @span,$n;
4517 23 100       85 last if ($n->isSameNode($end_node));
4518 10         6610 $n=$n->nextSibling();
4519             }
4520 13 50       28 die "Error: Node ".pwd($end_node).
4521             " isn't following sibling of ".pwd($node)."!\n" unless $n;
4522 13 100       75 if ($_xml_module->is_document($parent)) {
4523             # check that document element is within the span
4524 3         14 my $docel=$parent->getDocumentElement();
4525 3         7 my $found=0;
4526 3         6 foreach my $n (@span) {
4527 5 100       17 if ($n->isSameNode($docel)) {
4528 3         3 $found=1;
4529 3         6 last;
4530             }
4531             }
4532 3 50       9 die "Cannot wrap span: ".pwd($node).
4533             " .. ".pwd($end_node)." (document already has a root element)\n"
4534             unless $found;
4535 3         13 replace_document_element($docel,$el);
4536 3         8 foreach my $n (@span) {
4537 7         57 $n->unbindNode();
4538 7         36 $el->appendChild($n);
4539             }
4540             } else {
4541 10         53 $parent->insertBefore($el,$node);
4542 10         22 foreach my $n (@span) {
4543 16         171 $n->unbindNode();
4544 16         59 $el->appendChild($n);
4545             }
4546             }
4547 13 100       155 push @$rl, $el if defined $rl;
4548             }
4549 7         108 return $rl;
4550             }
4551              
4552              
4553             # normalize nodes
4554             sub normalize_nodes {
4555 0     0 0 0 my ($opts,$exp)=@_;
4556 0         0 my $ql=_ev_nodelist($exp);
4557 0         0 foreach (@$ql) {
4558 0         0 $_->normalize();
4559             }
4560 0         0 return 1;
4561             }
4562              
4563             sub _trim_ws {
4564 0     0   0 my ($text)=@_;
4565 0         0 $text=~s/^\s*//;
4566 0         0 $text=~s/\s*$//;
4567 0         0 return $text;
4568             }
4569              
4570             # strip whitespace from given nodes
4571             sub strip_ws {
4572 0     0 0 0 my ($opts,$exp)=@_;
4573 0         0 my $ql=_ev_nodelist($exp);
4574 0         0 foreach my $node (@$ql) {
4575 0 0 0     0 if ($_xml_module->is_text($node)
    0 0        
    0 0        
    0          
4576             or
4577             $_xml_module->is_cdata_section($node)
4578             or
4579             $_xml_module->is_comment($node)
4580             ) {
4581 0         0 my $data=_trim_ws($node->getData());
4582 0 0       0 if ($data ne "") {
4583 0 0       0 $data = "" unless defined $data;
4584 0         0 $node->setData($data);
4585             } else {
4586 0         0 $node->unbindNode();
4587             }
4588             } elsif ($_xml_module->is_pi($node)) {
4589 0         0 $node->setData(_trim_ws($node->getData($node)));
4590             } elsif ($_xml_module->is_attribute($node)) {
4591 0         0 $node->setValue(_trim_ws($node->getValue));
4592             } elsif ($_xml_module->is_element($node) or
4593             $_xml_module->is_document($node)) {
4594             # traverse children, skip comments, strip text nodes
4595             # until first element or PI or text node containing
4596             # a non-ws character
4597 0         0 my $child=$node->firstChild();
4598 0         0 while ($child) {
4599 0 0 0     0 if ($_xml_module->is_text($child) or
    0 0        
4600             $_xml_module->is_cdata_section($child)) {
4601 0         0 my $data=_trim_ws($child->getData());
4602 0 0       0 if ($data ne "") {
4603 0 0       0 $data = "" unless defined $data;
4604 0         0 $child->setData($data);
4605 0         0 last;
4606             } else {
4607 0         0 $child->unbindNode();
4608             }
4609             } elsif ($_xml_module->is_element($child) or
4610             $_xml_module->is_pi($child)) {
4611 0         0 last;
4612             }
4613 0         0 $child=$child->nextSibling();
4614             }
4615             # traverse children (upwards), skip comments, strip text nodes
4616             # until first element or PI or text node containing a non-ws
4617             # character
4618 0         0 my $child=$node->lastChild();
4619 0         0 while ($child) {
4620 0 0 0     0 if ($_xml_module->is_text($child) or
    0 0        
4621             $_xml_module->is_cdata_section($child)) {
4622 0         0 my $data=_trim_ws($child->getData());
4623 0 0       0 if ($data ne "") {
4624 0 0       0 $data = "" unless defined $data;
4625 0         0 $child->setData($data);
4626 0         0 last;
4627             } else {
4628 0         0 $child->unbindNode();
4629             }
4630             } elsif ($_xml_module->is_element($child) or
4631             $_xml_module->is_pi($child)) {
4632 0         0 last;
4633             }
4634 0         0 $child=$child->previousSibling();
4635             }
4636             }
4637             }
4638 0         0 return 1;
4639             }
4640              
4641             # fetch document's DTD
4642             sub get_dtd {
4643 1     1 0 11 my ($doc)=@_;
4644 1         25 my $dtd;
4645 1         36 $dtd=$_xml_module->get_dtd($doc,$QUIET);
4646              
4647 1         70 return $dtd;
4648             }
4649              
4650             # check document validity
4651             sub validate_doc {
4652 2     2 0 11 my ($opts,$exp)=@_;
4653 2         24 my $doc = _ev_doc($exp);
4654 2         40 $opts = _ev_opts($opts);
4655 2 50       22 if ($opts->{dtd}+$opts->{schema}+$opts->{relaxng}>1) {
4656 0         0 die "You can only specify one validation schema at a time\n";
4657             }
4658 2 50       18 if (grep(exists($opts->{$_}), qw(file doc string))>1) {
4659 0         0 die "You can only specify one of --file, --doc, --string at a time\n";
4660             }
4661 2 50 33     33 $opts->{dtd} = 1 unless $opts->{schema} or $opts->{relaxng};
4662 2 50 33     4276 if (exists($opts->{public}) ne "" and not $opts->{dtd}) {
4663 0         0 die "--public ID can only be used for DTD validation (--dtd)\n";
4664             }
4665 2 50       21 $opts->{file} = _tilde_expand($opts->{file}) if exists($opts->{file});
4666 2         10 my $ret = 0;
4667 2 50       31 if ($doc->can('is_valid')) {
4668 2 50 33     80 if (!$opts->{dtd} or exists($opts->{file}) or exists($opts->{string}) or
      33        
      33        
      33        
4669             exists($opts->{doc}) or exists($opts->{public})) {
4670 0 0       0 if ($opts->{dtd}) {
    0          
    0          
4671 0         0 my $dtd;
4672 0 0       0 eval { XML::LibXML::Dtd->can('new') } ||
  0         0  
4673             die "DTD validation not supported by your version of XML::LibXML\n";
4674 0 0 0     0 if (exists($opts->{file}) or exists($opts->{public})) {
    0          
4675 0         0 $dtd=XML::LibXML::Dtd->new($opts->{public},$opts->{file});
4676             } elsif (exists($opts->{string})) {
4677 0         0 $dtd=XML::LibXML::Dtd->parse_string($opts->{string});
4678             } else {
4679 0         0 die "Can't use --doc with DTD validation\n";
4680             }
4681 0 0       0 if ($opts->{yesno}) {
4682 0         0 $ret = $doc->is_valid($dtd);
4683 0 0       0 out(($ret ? "yes\n" : "no\n"));
4684             } else {
4685 0         0 $doc->validate($dtd);
4686 0         0 $ret = 1;
4687             }
4688             } elsif ($opts->{relaxng}) {
4689 0 0       0 eval { XML::LibXML::RelaxNG->can('new') } ||
  0         0  
4690             die "RelaxNG validation not supported by your version of XML::LibXML\n";
4691 0         0 my $rng;
4692 0 0       0 if (exists($opts->{file})) {
    0          
    0          
4693 0         0 $rng=XML::LibXML::RelaxNG->new(location => $opts->{file});
4694             } elsif (exists($opts->{string})) {
4695 0         0 $rng=XML::LibXML::RelaxNG->new(string => $opts->{string});
4696             } elsif (exists($opts->{doc})) {
4697 0         0 my $rngdoc=_doc($opts->{doc});
4698 0 0       0 unless (ref($rngdoc)) {
4699 0         0 die "--doc argument doesn't evaluate to a document!\n";
4700             }
4701 0         0 $rng=XML::LibXML::RelaxNG->new(DOM => $rngdoc);
4702             } else {
4703 0         0 die "No RelaxNG schema specified\n";
4704             }
4705 0         0 eval { $rng->validate($doc) };
  0         0  
4706 0 0       0 $ret = $@ ? 0 : 1;
4707 0 0       0 if ($opts->{yesno}) {
4708 0 0       0 out($ret ? "yes\n" : "no\n");
4709             } else {
4710 0 0       0 die "$@\n" if $@;
4711             }
4712             } elsif ($opts->{schema}) {
4713 0 0       0 eval { XML::LibXML::Schema->can('new') } ||
  0         0  
4714             die "Schema validation not supported by your version of XML::LibXML\n";
4715 0         0 my $xsd;
4716 0 0       0 if (exists($opts->{file})) {
    0          
    0          
4717 0         0 $xsd=XML::LibXML::Schema->new(location => $opts->{file});
4718             } elsif (exists($opts->{string})) {
4719 0         0 $xsd=XML::LibXML::Schema->new(string => $opts->{string});
4720             } elsif ($opts->{doc}) {
4721 0         0 my $xsddoc=_doc($opts->{doc});
4722 0 0       0 unless (ref($xsddoc)) {
4723 0         0 die "--doc argument doesn't evaluate to a document!\n";
4724             }
4725 0         0 $xsd=XML::LibXML::Schema->new(string => $xsddoc->toString());
4726             } else {
4727 0         0 die "No XSD schema specified\n";
4728             }
4729 0         0 eval { $xsd->validate($doc) };
  0         0  
4730 0 0       0 $ret = $@ ? 0 : 1;
4731 0 0       0 if ($opts->{yesno}) {
4732 0 0       0 out($ret ? "yes\n" : "no\n");
4733             } else {
4734 0 0       0 die "$@\n" if $@;
4735             }
4736             }
4737             } else {
4738 2 100       12 if ($opts->{yesno}) {
4739 1         123 $ret = $doc->is_valid();
4740 1 50       30 out(($ret ? "yes\n" : "no\n"));
4741             } else {
4742 1         35 $doc->validate();
4743 1         5 $ret = 1;
4744             }
4745             }
4746             } else {
4747 0         0 die("Vaidation not supported by ",ref($doc));
4748             }
4749 2         67 return $ret;
4750             }
4751              
4752             # process XInclude elements in a document
4753             sub process_xinclude {
4754 0     0 0 0 my ($opts, $exp)=@_;
4755 0         0 my $doc = _ev_doc($exp);
4756 0 0       0 if ($doc) {
4757 0         0 $_xml_module->doc_process_xinclude($_parser,$doc);
4758             }
4759 0         0 return 1;
4760             }
4761              
4762             # print document's DTD
4763             sub list_dtd {
4764 1     1 0 23 my $opts = shift;
4765 1         43 my $doc = _ev_doc($_[0]);
4766 1 50       18 if ($doc) {
4767 1         39 my $dtd=get_dtd($doc);
4768 1 50       72 if ($dtd) {
4769 1         60 out($_xml_module->toStringUTF8($dtd),"\n");
4770             }
4771             }
4772 1         18 return 1;
4773             }
4774              
4775             # set document's DTD
4776             sub set_dtd {
4777 0     0 0 0 my $opts = _ev_opts($_[0]);
4778 0         0 my $doc = _ev_doc($_[1]);
4779              
4780 0 0       0 if ($doc) {
4781 0         0 my $root = $opts->{name};
4782 0         0 my $public = $opts->{public};
4783 0         0 my $system = $opts->{system};
4784 0 0 0     0 if ((defined ($public) or defined ($system)) and !defined($root)) {
      0        
4785 0 0       0 if ($doc->getDocumentElement) {
4786 0         0 $root = $doc->getDocumentElement->nodeName();
4787             } else {
4788 0         0 die "No --name not specified and document has no root element\n"
4789             }
4790             }
4791 0 0       0 if ($doc->internalSubset) {
4792 0         0 $doc->removeInternalSubset();
4793             }
4794 0 0       0 if ($doc->externalSubset) {
4795 0         0 $doc->removeExternalSubset();
4796             }
4797 0 0 0     0 return 1 unless (defined $root or defined $public or defined $system);
      0        
4798 0 0       0 if ($opts->{internal}) {
4799 0         0 $doc->setInternalSubset($doc->createInternalSubset($root, $public,
4800             $system));
4801             } else {
4802 0         0 $doc->setInternalSubset($doc->createInternalSubset($root, $public,
4803             $system));
4804             }
4805             }
4806 0         0 return 1;
4807             }
4808              
4809              
4810             # print document's encoding
4811             sub print_enc {
4812 0     0 0 0 my ($opts,$doc)=@_;
4813 0         0 my $doc = _ev_doc($doc);
4814 0 0       0 if ($doc) {
4815 0         0 out($_xml_module->doc_encoding($doc),"\n");
4816             }
4817 0         0 return 1;
4818             }
4819              
4820             sub set_doc_enc {
4821 0     0 0 0 my $opts = shift;
4822 0         0 my ($encoding,$doc)=(_ev_literal($_[0]),_ev_doc($_[1]));
4823 0 0       0 if ($doc) {
4824 0         0 $_xml_module->set_encoding($doc,$encoding);
4825             }
4826 0         0 return 1;
4827             }
4828              
4829             sub set_doc_standalone {
4830 0     0 0 0 my $opts = shift;
4831 0         0 my ($standalone,$doc)=(_ev_literal($_[0]),_ev_doc($_[1]));
4832 0 0       0 $standalone=1 if $standalone=~/yes/i;
4833 0 0       0 $standalone=0 if $standalone=~/no/i;
4834 0         0 $_xml_module->set_standalone($doc,$standalone);
4835 0         0 return 1;
4836             }
4837              
4838             sub doc_info {
4839 0     0 0 0 my $opts = shift;
4840 0         0 my $doc = _ev_doc($_[0]);
4841 0 0       0 if ($doc) {
4842             # out("type=",$doc->nodeType,"\n");
4843 0         0 out("version=",$doc->version(),"\n");
4844 0         0 out("encoding=",$doc->encoding(),"\n");
4845 0         0 out("standalone=",$doc->standalone(),"\n");
4846 0         0 out("compression=",$doc->compression(),"\n");
4847 0         0 out("URI=",$doc->URI(),"\n");
4848             }
4849             }
4850              
4851             # create an identical copy of a document
4852             sub clone {
4853 1     1 0 9 my ($opts,$exp)=@_;
4854 1         13 my $doc = _ev_doc($exp);
4855 1 50       9 if ($doc) {
4856 1         33 return _clone_xmldoc($doc);
4857             } else {
4858 0         0 return undef;
4859             }
4860             }
4861              
4862             # test if $nodea is an ancestor of $nodeb
4863             sub is_ancestor_or_self {
4864 39     39 0 94 my ($nodea,$nodeb)=@_;
4865 39         125 while ($nodeb) {
4866 73 100       624 if ($_xml_module->xml_equal($nodea,$nodeb)) {
4867 1         18 return 1;
4868             }
4869 72         156 $nodeb=tree_parent_node($nodeb);
4870             }
4871             }
4872              
4873             # remove node and all its surrounding whitespace textual siblings
4874             # from a document; remove all its descendant from all nodelists
4875             # change current element to the nearest ancestor
4876             sub remove_node {
4877 39     39 0 99 my ($node,$trim_space)=@_;
4878 39 100       115 if (is_ancestor_or_self($node,xsh_context_node())) {
4879 1         34 _set_context([tree_parent_node($node)]);
4880             }
4881 39         77 my $doc;
4882 39         139 $doc=$_xml_module->owner_document($node);
4883 39 100       109 if ($trim_space) {
4884 8         77 my $sibling=$node->nextSibling();
4885 8 50 66     51 if ($sibling and
      33        
4886             $_xml_module->is_text($sibling) and
4887             $sibling->getData =~ /^\s+$/) {
4888             # remove_node_from_nodelists($sibling,$doc);
4889 0         0 $_xml_module->remove_node($sibling);
4890             }
4891             }
4892             # remove_node_from_nodelists($node,$doc);
4893 39         152 $_xml_module->remove_node($node);
4894             }
4895              
4896             # move nodes matching one XPath expression to locations determined by
4897             # other XPath expression
4898             sub move {
4899 10     10 0 45 my $exp=$_[1]; #source xpath
4900 10         64 my $sourcenodes=_ev_nodelist($exp);
4901 10         134 my $res=copy(@_);
4902 10         229 foreach my $node (@$sourcenodes) {
4903 30         88 remove_node($node);
4904             }
4905 10         83 return $res;
4906             }
4907              
4908             # call a shell command and print out its output
4909             sub sh_noev {
4910 1     1 0 4592 system($_[0]);
4911 1         364 return 1;
4912             }
4913              
4914             sub sh {
4915 1     1 0 3 my $opts = shift;
4916 1         3 my $cmd=join " ",map { _ev_string($_) } @_;
  2         7  
4917 1         6 return system(fromUTF8($ENCODING, $cmd));
4918             }
4919              
4920             # print the result of evaluating an XPath expression in scalar context
4921             sub print_count {
4922 52     52 0 191 my $opts = _ev_opts(shift);
4923 52         196 my $count=count_xpath(@_);
4924 52 50       771 out("$count\n") unless $opts->{quiet};
4925 52         616 return $count;
4926             }
4927              
4928             sub perl_eval_command {
4929 229     229 0 382 shift; # opts
4930 229         525 &perl_eval;
4931             }
4932              
4933             sub perl_eval {
4934 319     319 0 698 my ($exp,$map,$in_place)=@_;
4935 319         958 select $OUT;
4936 15     13   100659 use utf8;
  15         51  
  15         137  
4937 319 100       1026 if (wantarray) {
    100          
4938 6         25 my @result=eval(lexicalize($exp));
4939 6 50       27 die $@ if $@;
4940 6         28 return @result;
4941             } elsif (defined $map) {
4942 25 50       61 if (ref($map)) {
4943 0         0 $map = to_literal($map);
4944             }
4945 25 100       51 if ($in_place) {
4946 3         18 local $_ = $map;
4947 6     9   34 eval(lexicalize($exp));
  6     9   189  
  6         191  
  6         23  
  6         25  
  5         54  
  3         12  
4948 3 50       14 die $@ if $@;
4949 3         13 return $_;
4950             } else {
4951             # abraka dabra: some magic to make $_ read only
4952 22         1553 local *_ = eval "\\'$map'";
4953 5     9   31 my $result=eval(lexicalize($exp));
  5     9   148  
  5     9   159  
  5     9   21  
  5     8   26  
  5     8   57  
  5     8   21  
  5     8   146  
  5     7   118  
  5     7   26  
  5     6   22  
  5     6   34  
  4     5   17  
  4     5   281  
  4     4   69  
  4     4   13  
  4     4   20  
  4     4   29  
  4     4   13  
  4     4   157  
  4     4   57  
  4     4   11  
  4     4   20  
  4     4   30  
  4     4   22  
  4     4   139  
  4     4   67  
  4     4   13  
  4     4   25  
  4     4   38  
  4     4   15  
  4     4   126  
  4     4   77  
  4     4   15  
  4     4   22  
  4     4   28  
  4     4   14  
  4     4   120  
  4     4   63  
  4     4   12  
  4         24  
  4         25  
  4         15  
  4         157  
  4         81  
  4         11  
  4         16  
  4         45  
  4         15  
  4         139  
  4         78  
  4         22  
  4         19  
  4         37  
  4         16  
  4         120  
  4         65  
  4         13  
  4         14  
  4         27  
  4         18  
  4         133  
  4         89  
  4         15  
  4         16  
  4         31  
  4         14  
  4         151  
  4         67  
  4         19  
  4         24  
  4         32  
  4         13  
  4         162  
  4         83  
  4         19  
  4         22  
  4         37  
  4         13  
  4         132  
  4         66  
  4         19  
  4         20  
  4         32  
  4         21  
  4         177  
  4         69  
  4         14  
  4         22  
  2         11  
  2         16  
  2         47  
  2         40  
  2         7  
  2         6  
  2         11  
  2         9  
  2         55  
  2         60  
  2         9  
  2         8  
  2         10  
  2         9  
  2         32  
  2         55  
  2         9  
  2         14  
  2         17  
  2         9  
  2         45  
  2         52  
  2         10  
  2         15  
  2         9  
  2         10  
  2         55  
  2         42  
  2         7  
  2         6  
  2         11  
  22         105  
4954 22 50       78 die $@ if $@;
4955 22         94 return $result;
4956             }
4957             } else {
4958 10     14   76 my $result=eval(lexicalize($exp));
  10     14   155  
  10     2   452  
  10     2   58  
  10         37  
  10         76  
  288         861  
  2         21  
  2         5  
  2         119  
  2         13  
  2         4  
  2         13  
4959 288 100       1865 die $@ if $@;
4960 283         1118 return $result;
4961             }
4962             }
4963              
4964             # evaluate a perl expression
4965             sub print_eval {
4966 0     0 0 0 my ($expr)=@_;
4967 0         0 perl_eval($expr);
4968 0         0 return 1;
4969             }
4970              
4971             # change current directory
4972             sub cd {
4973 0     0 0 0 my $dir = _tilde_expand(_ev_string($_[0]));
4974 0 0       0 unless (chdir $dir) {
4975 0         0 print STDERR "Can't change directory to $dir\n";
4976 0         0 return 0;
4977             } else {
4978 0 0       0 out("$dir\n") unless "$QUIET";
4979             }
4980 0         0 return 1;
4981             }
4982              
4983             # call methods from a list
4984             sub run_commands {
4985 1036 100   1036 0 2950 return 0 unless ref($_[0]) eq "ARRAY";
4986 1033         1336 my @cmds=@{$_[0]};
  1033         2465  
4987 1033         1703 my $top_level=$_[1];
4988 1033         1500 my $want_returns=$_[2];
4989 1033         1452 my $trapsignals=$top_level;
4990 1033         1717 my $result=undef;
4991              
4992 1033         2129 my ($cmd,@params);
4993              
4994             # make sure errors throw exceptions
4995 1033 100       2301 local $_die_on_err=1 unless ($top_level);
4996 1033 100       1898 local $_want_returns=1 if ($want_returns);
4997              
4998 1033         2936 store_variables(1);
4999 1033         2399 store_lex_variables(1);
5000 15     13   3534 no strict qw(refs);
  15         338  
  15         4251  
5001 1033         1705 eval {
5002 1033 100       8305 local $SIG{INT}=\&sigint if $trapsignals;
5003 1033 100       6526 local $SIG{PIPE}=\&sigpipe if $trapsignals;
5004 1033         2286 foreach my $run (@cmds) {
5005 1494 100 100     7807 if (ref($run) eq 'ARRAY' or ref($run) eq 'XML::XSH2::Command') {
5006 1482         5747 ($RT_LINE,$RT_COLUMN,$RT_OFFSET,$RT_SCRIPT,$cmd,@params)=@$run;
5007 1482 100       3151 if ($cmd eq "test-mode") { $TEST_MODE=1; $result=1; next; }
  1         3  
  1         2  
  1         2  
5008 1481 100       2789 if ($cmd eq "run-mode") { $TEST_MODE=0; $result=1; next; }
  1         3  
  1         3  
  1         20  
5009 1480 100       2718 next if $TEST_MODE;
5010 1479 50       7430 $result=$cmd->(@params) if defined($cmd);
5011             } else {
5012 12         250 $result=0;
5013             }
5014             }
5015             };
5016 1033         4665 my $err = $@;
5017 1033         1470 do {
5018 1033         14332 local $SIG{INT}=\&flagsigint;
5019 1033         3758 restore_lex_variables();
5020 1033         3199 restore_variables();
5021 1033         2042 propagate_flagsigint();
5022             };
5023 1033 50 66     5131 if (!$trapsignals and $err =~ /^SIGINT|^SIGPIPE/) {
5024 0         0 die $err
5025             } else {
5026 1033         2352 _check_err($err,1);
5027             }
5028 970         7221 return $result;
5029             }
5030              
5031             sub run_string {
5032 208 50   208 0 592 xsh_rd_parser_init() unless $_xsh;
5033 208         1550 my $pt = $_xsh->startrule($_[0]);
5034 208         849 post_process_parse_tree($pt);
5035 208         796 return run_commands($pt,0);
5036             }
5037              
5038             sub run_exp {
5039 25     25 0 81 my ($opts,$exp)=@_;
5040 25         101 local $SCRIPT="";
5041 25         79 run_string(_ev_literal($exp));
5042             }
5043              
5044             # redirect output and call methods from a list
5045             sub pipe_command {
5046 20 50   20 0 81 return 1 if $TEST_MODE;
5047              
5048 20     0   408 local $SIG{PIPE}=sub { };
5049 20         134 my ($cmd,$pipe)=@_;
5050              
5051 20 50       153 return 0 unless (ref($cmd) eq 'ARRAY');
5052              
5053 20 50       225 if ($^O eq 'MSWin32') {
5054 0         0 _warn("Output redirection not supported on Win32 - ignoring pipe!");
5055 0         0 return run_commands($cmd);
5056             }
5057 20         144 $pipe = expand($pipe);
5058 20 50       73 if ($pipe eq '') {
5059 0         0 die "Error: empty redirection\n";
5060             }
5061 20         41 my $out=$OUT;
5062 20 50       52 print STDERR "openning pipe $pipe\n" if $DEBUG;
5063 20         32 my $pid;
5064 20         38 eval {
5065 15     13   4484 use IPC::Open2;
  15         26868  
  15         26235  
5066             {
5067 20         37 local *O = *$out;
  20         376  
5068 20         91 my $P;
5069 20   50     142 $pid = open2('>&O',$P,$pipe) || die "cannot open pipe $pipe\n";
5070 20         100815 $OUT=$P;
5071             ## this is an approach to locate a bug in perl
5072             # local *NEWIN;
5073             # $pid = open2('>&O',\*NEWIN,$pipe) || die "cannot open pipe $pipe\n";
5074             # $OUT=\*NEWIN; #$P;
5075             # my $STDOUT=\*STDOUT;
5076             # for ($STDOUT,$OUT,$out) {
5077             # print STDERR "$_ => ".$_->fileno."\n";
5078             # }
5079             ## print $OUT "FOO\n";
5080 20         534 run_commands($cmd);
5081             }
5082             };
5083             # print STDERR "FILENO:",$OUT->fileno,"\n";
5084             # print STDERR `ls -l /proc/$$/fd/`;
5085 20         189 my $err=$@;
5086 20         79 do {
5087 20         325 local $SIG{INT}=\&flagsigint;
5088 20 50       432 if (UNIVERSAL::can($OUT,'flush')) {
5089 20         250 flush $OUT;
5090 20         100 flush $OUT;
5091             }
5092 20         736 close $OUT;
5093 20         5522 waitpid($pid,0);
5094 20         434 $OUT=$out;
5095 20 50       447 flush $OUT if UNIVERSAL::can($OUT,'flush');
5096 20         101 propagate_flagsigint();
5097             };
5098 20 50       133 die $err if $err; # propagate
5099 20         1635 return 1;
5100             }
5101              
5102             # redirect output to a string and call methods from a list
5103             sub string_pipe_command {
5104 5     5 0 11 my ($cmd,$name)=@_;
5105 5 50       13 return 0 unless (ref($cmd) eq 'ARRAY');
5106 5 50       12 if ($name ne '') {
5107 5         8 my $out=$OUT;
5108 5 50       9 print STDERR "Pipe to $name\n" if $DEBUG;
5109 5         769 require IO::Scalar;
5110 5         4087 $OUT=new IO::Scalar;
5111 5         254 eval {
5112 5         12 run_commands($cmd);
5113             };
5114 5         14 my $err;
5115 5         8 do {
5116 5         51 local $SIG{INT}=\&flagsigint;
5117 5 50       14 _assign($name,${$OUT->sref}) unless $@;
  5         16  
5118 5         19 $OUT=$out;
5119 5         66 propagate_flagsigint();
5120             };
5121 5 50       18 die $err if $err; # propagate
5122             }
5123 5         15 return 0;
5124             }
5125              
5126              
5127             # call methods as long as given XPath returns positive value
5128             sub while_statement {
5129 7     7 0 25 my ($exp,$command)=@_;
5130 7         17 my $result=1;
5131 7         16 my $res;
5132 7         30 while ($res=_ev_count($exp)) {
5133 29         182 eval {
5134 29   33     106 $result = run_commands($command) && $result;
5135             };
5136 29 100 66     189 if (ref($@) and UNIVERSAL::isa($@,'XML::XSH2::Internal::LoopTerminatingException')) {
    50          
5137 14 50 33     45 if ($@->label =~ /^(?:next|last|redo)$/ and $@->[1]>1) {
5138 0         0 $@->[1]--;
5139 0         0 die $@; # propagate to a higher level
5140             }
5141 14 100       44 if ($@->label eq 'next') {
    100          
    50          
5142 6         19 next;
5143             } elsif ($@->label eq 'last') {
5144 2         4 last;
5145             } elsif ($@->label eq 'redo') {
5146 6         14 redo;
5147             } else {
5148 0         0 die $@; # propagate
5149             }
5150             } elsif ($@) {
5151 0         0 die $@; # propagate
5152             }
5153             }
5154 7         173 return $result;
5155             }
5156              
5157             sub throw_exception {
5158 5     5 0 14 my $opts = shift;
5159 5         24 die _ev_literal($_[0])."\n";
5160             }
5161              
5162             sub try_catch {
5163 6     6 0 18 my ($try,$catch,$var)=@_;
5164 6         12 my $result;
5165 6         12 eval {
5166 6         15 local $TRAP_SIGPIPE=1;
5167 6         86 local $SIG{INT}=\&sigint;
5168 6         79 local $SIG{PIPE}=\&sigpipe;
5169             # local $_die_on_err=1; # make sure errors cause an exception
5170 6         44 $result = run_commands($try);
5171             };
5172 6 50 33     60 if (ref($@) and UNIVERSAL::isa($@,'XML::XSH2::Internal::UncatchableException')) {
    50          
5173 0         0 die $@; # propagate
5174             } elsif ($@) {
5175 6         15 my $err=$@;
5176 6 50       31 if ($err =~ /^SIGINT/) {
5177 0         0 die $err; # propagate sigint
5178             } else {
5179 6 50       26 chomp($err) unless ref($err);
5180 6 50 33     36 if (ref($var) and @{$var}>1) {
  6         28  
5181 6         25 create_block_var(@$var);
5182 6         28 _assign($var->[0],$err);
5183 6         10 eval {
5184 6         21 $result = run_commands($catch);
5185             };
5186 6         17 $err = $@;
5187 6         16 do {
5188 6         73 local $SIG{INT}=\&flagsigint;
5189 6         36 destroy_block_var($var->[1]);
5190 6         18 propagate_flagsigint();
5191             };
5192 6 50       27 die $err if $err; # propagate
5193             } else {
5194 0 0       0 _assign($var->[0],$@) if ref($var);
5195 0         0 $result = run_commands($catch);
5196             }
5197             }
5198             }
5199 6         25 return $result;
5200             }
5201              
5202             sub loop_next {
5203 12     12 0 23 my $opts = shift;
5204 12         34 die XML::XSH2::Internal::LoopTerminatingException->new('next',_ev_literal(@_));
5205             }
5206             sub loop_prev {
5207 0     0 0 0 my $opts = shift;
5208 0         0 die XML::XSH2::Internal::LoopTerminatingException->new('prev',_ev_literal(@_));
5209             }
5210             sub loop_redo {
5211 12     12 0 23 my $opts = shift;
5212 12         35 die XML::XSH2::Internal::LoopTerminatingException->new('redo',_ev_literal(@_));
5213             }
5214             sub loop_last {
5215 4     4 0 12 my $opts = shift;
5216 4         16 die XML::XSH2::Internal::LoopTerminatingException->new('last',_ev_literal(@_));
5217             }
5218              
5219             sub _save_context {
5220 56 50   56   196 return [xsh_context_node(),
5221             $_xpc->can('setContextSize') ?
5222             ($_xpc->getContextSize(),$_xpc->getContextPosition()) :
5223             (undef,undef)];
5224             }
5225              
5226             sub _set_context {
5227 300     300   744 my ($node,$size,$pos)=@{$_[0]};
  300         1688  
5228 300 50       1719 if ($node) {
5229 300         2275 $_xpc->setContextNode($node);
5230 300 50 66     1962 if (defined($size) and defined($pos) and $_xpc->can('setContextSize')) {
      66        
5231 218 50       508 die "invalid size $size\n" if ($size < -1);
5232 218         530 $_xpc->setContextSize($size);
5233 218 50 33     725 die "invalid position $pos (size is $size)\n" if ($pos < -1 or $pos>$size);
5234 218         535 $_xpc->setContextPosition($pos);
5235             }
5236             } else {
5237 0         0 die "Trying to change current node to an undefined value\n";
5238             }
5239             }
5240              
5241              
5242             # call methods on every node matching an XPath
5243             sub foreach_statement {
5244 14     14 0 52 my ($exp,$command,$v)=@_;
5245 14 100       56 my ($var,$local) = ref($v) ? @$v : ();
5246 14         65 my $old_context = _save_context();
5247 14 100       68 create_block_var($var,$local) if $var ne "";
5248 14         31 eval {
5249 14 100       72 my @ql = ($var ne "") ? _ev_list($exp) : _ev_nodelist($exp);
5250 14         33 my $pos=1;
5251 14         26 my $size = @ql;
5252 14         50 foreach my $node (@ql) {
5253 57 100       192 if ($var ne "") {
5254 1         5 _assign($var,$node);
5255             } else {
5256 56         187 _set_context([$node,$size,$pos]);
5257             }
5258 57         139 eval {
5259 57         142 run_commands($command);
5260             };
5261 57 100 66     396 if (ref($@) and UNIVERSAL::isa($@,'XML::XSH2::Internal::LoopTerminatingException')) {
    50          
5262 14 50 33     56 if ($@->label =~ /^(?:next|last|redo)$/ and $@->[1]>1) {
5263 0         0 $@->[1]--;
5264 0         0 die $@; # propagate to a higher level
5265             }
5266 14 100       42 if ($@->label eq 'next') {
    100          
    50          
5267 6         11 $pos++;
5268 6         31 next;
5269             } elsif ($@->label eq 'last') {
5270 2         9 last;
5271             } elsif ($@->label eq 'redo') {
5272 6         16 redo;
5273             } else {
5274 0         0 die $@; # propagate
5275             }
5276             } elsif ($@) {
5277 0         0 die $@; # propagate
5278             }
5279 43         138 $pos++;
5280             }
5281             };
5282 14         341 my $err = $@;
5283 14         28 do {
5284 14         201 local $SIG{INT}=\&flagsigint;
5285 14         75 _set_context($old_context);
5286 14 100       61 destroy_block_var($local) if ($var ne "");
5287 14         39 propagate_flagsigint();
5288             };
5289 14 50       67 die $err if $err; # propagate
5290 14         260 return 1;
5291             }
5292              
5293             # run commands if given XPath holds
5294             sub if_statement {
5295 4     4 0 13 my @cases=@_;
5296 4         16 foreach (@cases) {
5297 5         28 my ($exp,$command)=@$_;
5298 5 100 100     39 if (!defined($exp) or _ev_count($exp)) {
5299 2         15 return run_commands($command);
5300             }
5301             }
5302 2         41 return 1;
5303             }
5304              
5305             # run commands unless given XPath holds
5306             sub unless_statement {
5307 255     255 0 682 my ($exp,$command,$else)=@_;
5308 255 100       820 unless (_ev_count($exp)) {
5309 25         288 return run_commands($command);
5310             } else {
5311 230 100       2569 return ref($else) ? run_commands($else->[1]) : 1;
5312             }
5313             }
5314              
5315             sub _clone_xmldoc {
5316 1     1   4 my ($doc)=@_;
5317 1 50       63 if (XML::LibXML::Document->can('cloneNode') ==
5318             XML::LibXML::Node->can('cloneNode')) {
5319             # emulated clone
5320 0         0 return $_xml_module->new_parser->parse_string($doc->toString());
5321             } else {
5322              
5323             # native clone (if my patch ever gets into LibXML)
5324 1         112 return $doc->cloneNode(1);
5325             }
5326             }
5327              
5328             sub xslt_compile {
5329 0     0 0 0 my ($as_doc,$stylefile,$want_doc)=@_;
5330 0         0 my $styledoc;
5331 0 0       0 if ($as_doc) {
5332 0         0 $styledoc = _ev_doc($stylefile);
5333 0 0       0 die "No XSL document: $stylefile\n" unless $styledoc;
5334             } else {
5335 0         0 $stylefile = _tilde_expand(_ev_string($stylefile));
5336 0 0 0     0 if ((-f $stylefile) or ($stylefile=~/^[a-z]+:/)) {
5337 0         0 $styledoc = XML::LibXML->new()->parse_file($stylefile);
5338             } else {
5339 0         0 die "File not exists '$stylefile'\n";
5340             }
5341             }
5342 0         0 require XML::LibXSLT;
5343 0         0 my $xsltparser=XML::LibXSLT->new();
5344 0         0 my $st = $xsltparser->parse_stylesheet($styledoc);
5345 0 0       0 if ($want_doc) {
5346 0         0 return ($st, $styledoc);
5347             } else {
5348 0         0 return $st;
5349             }
5350             }
5351              
5352             # transform a document with an XSLT stylesheet
5353             # and create a new document from the result
5354             sub xslt {
5355 0     0 0 0 my ($opts,$stylefile)=(shift,shift);
5356 0         0 my $opts = _ev_opts($opts);
5357              
5358 0         0 my $st;
5359 0 0       0 if ($opts->{compile}) {
5360 0 0 0     0 if (@_ or $opts->{'precompiled'}) {
5361 0         0 _warn("Document argument or --precompiled flag given. Ignoring --compile flag!");
5362             } else {
5363 0         0 return xslt_compile($opts->{'doc'},$stylefile);
5364             }
5365             }
5366              
5367 0 0       0 if ($opts->{'precompiled'}) {
5368 0 0       0 if ($stylefile =~ /^{/) {
    0          
5369 0         0 $st = _ev($stylefile);
5370             } elsif ($stylefile =~ /^\$/) {
5371 0         0 $st = var_value($stylefile);
5372             } else {
5373 0         0 die "Pre-compiled XSLT stylesheet can't be given only as perl expression or a variable: $stylefile\n";
5374             }
5375 0 0 0     0 unless (ref($st) and UNIVERSAL::isa($st,'XML::LibXSLT::Stylesheet') or
      0        
5376             UNIVERSAL::isa($st,'XML::LibXSLT::StylesheetWrapper')) {
5377 0         0 die "Pre-compiled XSLT stylesheet doesn't appear to be a XML::LibXSLT::Stylesheet object: $stylefile\n";
5378             }
5379             } else {
5380 0         0 print STDERR "compiling\n";
5381 0         0 ($st,my $styledoc) = xslt_compile($opts->{doc},$stylefile,1);
5382 0 0       0 die "No XSLT document: $stylefile\n" unless $st;
5383 0         0 $stylefile = $styledoc->URI;
5384             }
5385              
5386 0         0 my $source;
5387 0 0       0 if (@_) {
5388 0         0 $source = shift;
5389             } else {
5390 0         0 $source = '.';
5391             }
5392              
5393 0         0 my $doc = _ev_doc($source);
5394              
5395 0         0 my @params = expand(@_);
5396 0 0       0 print STDERR "running xslt on $doc stylesheet $stylefile params @params\n" if "$DEBUG";
5397 0 0       0 die "No document to process with XSLT: $source\n" unless $doc;
5398 0         0 my %params;
5399 0         0 foreach my $p (@params) {
5400 0         0 $p=$1 while $p=~/^\s*\((.*)\)\s*$/;
5401 0 0       0 if ($p=~/^\s*(\S+?)\s*=\s*(.*?)\s*$/) {
5402 0         0 $params{$1}=$2;
5403             } else {
5404 0         0 die("Malformed XSLT parameter $p");
5405             }
5406             }
5407 0 0       0 if ($DEBUG) {
5408 0         0 print STDERR map { "$_ -> $params{$_} " } keys %params;
  0         0  
5409 0         0 print STDERR "\n";
5410             }
5411              
5412 0 0       0 my $rl = $opts->{'string'} ? undef : _prepare_result_nl();
5413 0 0       0 if ($st) {
5414 0         0 $stylefile=~s/\..*$//;
5415 0         0 my $result = eval {
5416 0         0 $st->transform(_clone_xmldoc($doc),%params);
5417             };
5418 0 0       0 if ($result) {
5419 0 0       0 _warn $@ if $@;
5420             } else {
5421 0 0       0 die $@."\n" if $@;
5422             }
5423 0 0       0 if ($opts->{'string'}) {
5424 0         0 return $st->output_string($result);
5425             } else {
5426 0         0 set_doc_URI($result,
5427             _base_filename($stylefile).
5428             "_transformed_".
5429             _base_filename($result->URI()));
5430 0 0       0 push @$rl,$result if defined $rl;
5431             }
5432             } else {
5433 0         0 die "Failed to parse stylesheet '$stylefile'\n";
5434             }
5435 0         0 return $rl;
5436             }
5437              
5438             # perform xupdate processing over a document
5439             sub xupdate {
5440 0     0 0 0 my ($opts,$xupdate_doc,$doc)=map { _ev_doc($_) } @_;
  0         0  
5441 0 0 0     0 if ($xupdate_doc and $doc) {
5442 0         0 require XML::XUpdate::LibXML;
5443 0         0 require XML::Normalize::LibXML;
5444 0         0 my $xupdate = XML::XUpdate::LibXML->new();
5445 0         0 $XML::XUpdate::LibXML::debug=1;
5446 0         0 $xupdate->process($doc->getDocumentElement(),$xupdate_doc);
5447             } else {
5448 0 0       0 if ($xupdate_doc) {
5449 0         0 die "Expression '$_[0]' returns empty nodeset\n";
5450             } else {
5451 0         0 die "Expression '$_[1]' returns empty nodeset\n";
5452             }
5453 0         0 return 0;
5454             }
5455             }
5456              
5457             sub call_return {
5458 0     0 0 0 my $opts = shift;
5459 0         0 die XML::XSH2::Internal::SubTerminatingException->new('return',_ev($_[0]));
5460             }
5461              
5462             sub call_command {
5463 86     86 0 270 my ($opts,$exp,@args)=@_;
5464 86         319 my $name = _ev_string($exp);
5465 86         354 call($opts,1,$name, @args);
5466             }
5467              
5468             # call a named set of commands
5469             sub call {
5470 254     254 0 660 my ($opts,$eval_args, $name, @args)=@_;
5471 254         599 my $def = $_defs{$name};
5472 254 50       571 if (defined $def) {
5473 254         927 my @vars = @$def[2..$#$def];
5474 254 50       978 if (@vars < @args) {
    50          
5475 0         0 _err("too many arguments [".join(";\n",@args)."] for subroutine '$name @vars'");
5476             } elsif (@vars > @args) {
5477 0         0 _err("too few arguments for subroutine '$name @vars'");
5478             }
5479 254         451 my $result;
5480             my %vars;
5481 254         504 foreach (@args) {
5482 386 50       1904 $vars{ shift(@vars) }=$eval_args ? _ev($_) : $_;
5483             }
5484 254         2035 my $prev_lex_context = $lexical_variables;
5485 254         487 $lexical_variables = $def->[1];
5486 254         968 store_lex_variables(1,keys(%vars));
5487 254         425 eval {
5488 254         621 foreach (keys(%vars)) {
5489 386         994 _assign($_,$vars{$_});
5490             }
5491 254         807 $result = run_commands($def->[0]);
5492             };
5493 254         537 my $err = $@;
5494 254         383 do {
5495 254         2783 local $SIG{INT}=\&flagsigint;
5496 254         867 restore_lex_variables();
5497 254         1386 $lexical_variables=$prev_lex_context;
5498 254         2042 propagate_flagsigint();
5499             };
5500 254 50 33     927 if (ref($err) and UNIVERSAL::isa($err,'XML::XSH2::Internal::SubTerminatingException')) {
5501 0         0 my $ret = $err->[1];
5502 0         0 undef $err;
5503 0         0 return $ret;
5504             }
5505 254 100       632 die $err if $err; # propagate
5506 247         4702 return $result;
5507             } else {
5508 0         0 die "ERROR: $name not defined\n";
5509             }
5510             }
5511              
5512             sub undefine {
5513 0     0 0 0 my ($name)=@_;
5514 0 0       0 if ($name =~ /^\s*\$(.*)$/) {
5515 0         0 _undef($name);
5516 0         0 my $lex = lex_var($1);
5517 0 0       0 if ($lex) {
5518 0         0 undef $$lex;
5519             } else {
5520 15     14   94 no strict qw(refs);
  15         348  
  15         30422  
5521 0         0 undef ${"XML::XSH2::Map::".$1};
  0         0  
5522             }
5523             } else {
5524 0         0 delete $_defs{$name};
5525             }
5526 0         0 return 1;
5527             }
5528              
5529             # define a named set of commands
5530             sub def {
5531 10     10 0 28 my ($name,$command,$args)=@_;
5532 10         83 $_defs{$name} = [ $command, [ @$lexical_variables ], @$args ];
5533 10         124 return 1;
5534             }
5535              
5536             # return a list of all definined subroutines
5537             sub defs {
5538 0     0 0 0 return sort keys %_defs;
5539             }
5540              
5541             # list all defined subroutines
5542             sub list_defs {
5543 1     1 0 3 my $opts = shift;
5544 1         5 foreach (sort keys (%_defs)) {
5545 1         3 out(join(" ",$_,@{ $_defs{$_} }[2..$#{ $_defs{$_} }] ),"\n" );
  1         11  
  1         3  
5546             }
5547 1         4 return 1;
5548             }
5549              
5550             # load a file
5551             sub load {
5552 0     0 0 0 my ($file,$enc)=@_;
5553 0         0 my $l;
5554 0 0       0 print STDERR "loading file $file\n" unless "$QUIET";
5555 0   0     0 $enc ||= $QUERY_ENCODING;
5556 0 0 0     0 if (-f $file and open my $f,"$file") {
5557 0 0       0 if ($] >= 5.008) {
5558 0         0 binmode $f,":encoding($enc)";
5559             }
5560 0         0 return join "",<$f>;
5561             } else {
5562 0         0 die "ERROR: couldn't open input file $file\n";
5563             }
5564             }
5565              
5566             # call XSH to evaluate commands from a given file
5567             sub include {
5568 0     0 0 0 my ($opts,$f,$conditionally)=@_;
5569 0         0 $f=_tilde_expand(_ev_string($f));
5570             # File should be relative to the current script URI.
5571 0         0 $f = XML::XSH2::Map::resolve_uri(URI::file->new($f),
5572             URI::file->new($SCRIPT))->file;
5573 0         0 $opts=_ev_opts($opts);
5574 0 0 0     0 if (!$conditionally || !$_includes{$f}) {
5575 0         0 $_includes{$f}=1;
5576 0         0 my $l=load($f,$opts->{encoding});
5577 0         0 local $SCRIPT = $f;
5578 0         0 return run($l);
5579             }
5580             }
5581              
5582             # print help
5583              
5584             sub apropos {
5585 0     0 0 0 my ($opts,$query)=@_;
5586 0         0 $query = expand($query);
5587 0         0 $opts=_ev_opts($opts);
5588 0 0       0 if ($opts->{fulltext}) {
5589 0         0 foreach my $k (sort keys %XML::XSH2::Help::HELP) {
5590 0 0       0 if ($opts->{regexp}) {
5591 0 0       0 out("$k\n") if ($XML::XSH2::Help::HELP{$k}->[0]=~/$query/i);
5592             } else {
5593 0 0       0 out("$k\n") if ($XML::XSH2::Help::HELP{$k}->[0]=~/\b\Q$query\E\b/i);
5594             }
5595             }
5596             } else {
5597 0         0 foreach my $k (sort keys %$XML::XSH2::Help::Apropos) {
5598 0 0       0 if ($opts->{regexp}) {
5599 0 0       0 out("$k\n") if (($k." - ".$XML::XSH2::Help::Apropos->{$k})=~/$query/i);
5600             } else {
5601 0 0       0 out("$k\n") if (($k." - ".$XML::XSH2::Help::Apropos->{$k})=~/\b\Q$query\E\b/i);
5602             }
5603             }
5604             }
5605             }
5606              
5607             sub help {
5608 0     0 0 0 my $opts = shift;
5609 0         0 my ($command)=expand @_;
5610 0 0       0 if ($command) {
5611 0 0       0 if (exists($XML::XSH2::Help::HELP{$command})) {
5612 0         0 out($XML::XSH2::Help::HELP{$command}->[0]);
5613             } else {
5614             my @possible =
5615 0         0 grep { index($_,$command)==0 }
  0         0  
5616             keys(%XML::XSH2::Help::HELP);
5617 0         0 my %h = map { $XML::XSH2::Help::HELP{$_} => $_ } @possible;
  0         0  
5618 0 0       0 if (keys(%h) == 1) {
    0          
5619 0         0 out($XML::XSH2::Help::HELP{$possible[0]}->[0]);
5620 0         0 return 1;
5621             } elsif (keys(%h) > 1) {
5622 0         0 out("No help available on $command\n");
5623 0         0 out("Did you mean some of ", join(', ',@possible)," ?\n");
5624             } else {
5625 0         0 out("No help available on $command\n");
5626 0         0 return 0;
5627             }
5628             }
5629             } else {
5630 0         0 out($XML::XSH2::Help::HELP);
5631             }
5632 0         0 return 1;
5633             }
5634              
5635             # load catalog file to the parser
5636             sub load_catalog {
5637 0     0 0 0 my $opts = shift;
5638 0         0 $_xml_module->load_catalog($_parser,_tilde_expand(_ev_string($_[0])));
5639 0         0 return 1;
5640             }
5641              
5642             sub stream_process_node {
5643 4     4 0 6174 my ($node,$command,$input)=@_;
5644 4         19 my $old_context = _save_context();
5645 4         10 eval {
5646 4         8 foreach (1) {
5647 4         13 _set_context([$node,1,1]);
5648 4         8 eval {
5649 4         22 run_commands($command);
5650             };
5651 4 50 33     20 if (ref($@) and UNIVERSAL::isa($@,'XML::XSH2::Internal::LoopTerminatingException')) {
    50          
5652 0 0 0     0 if ($@->label =~ /^(?:next|redo)$/ and $@->[1]>1) {
5653 0         0 $@->[1]--;
5654 0         0 die $@; # propagate to a higher level
5655             }
5656 0 0       0 if ($@->label eq 'next') {
    0          
5657 0         0 last;
5658             } elsif ($@->label eq 'redo') {
5659 0         0 redo;
5660             } else {
5661 0         0 die $@; # propagate
5662             }
5663             } elsif ($@) {
5664 0         0 die $@; # propagate
5665             }
5666             }
5667             };
5668 4         6 my $err = $@;
5669 4         5 do {
5670 4         43 local $SIG{INT}=\&flagsigint;
5671 4         15 _set_context($old_context);
5672 4         9 propagate_flagsigint();
5673             };
5674 4 50       20 die $err if $err; # propagate
5675             }
5676              
5677             sub stream_process {
5678 2     2 0 7 my ($opts, $process)=@_;
5679 2         7 $opts = _ev_opts($opts);
5680              
5681 2         669 require XML::Filter::DOMFilter::LibXML;
5682 2         28792 require XML::LibXML::SAX;
5683 2         1806 require XML::SAX::Writer;
5684              
5685 2 50       5575 if (grep {/^input-/} keys %$opts>1) {
  2         10  
5686 0         0 die "Only one --input-xxxx parameter can be specified\n";
5687             }
5688 2 50       4 if (grep {/^output-/} grep { !/^output-encoding/ } keys %$opts>1) {
  2         8  
  2         5  
5689 0         0 die "Only one --output-xxxx parameter can be specified\n";
5690             }
5691 2 50 33     23 if ($opts->{'no-output'} && grep /^output-/, keys %$opts) {
5692 0         0 die "Can't combine --no-output with --output-xxxx\n";
5693             }
5694              
5695 2         5 my $out;
5696             my $termout;
5697 2 50       7 $opts->{'input-file'} = _tilde_expand($opts->{'input-file'}) if exists($opts->{'input-file'});
5698 2 50       6 $opts->{'output-file'} = _tilde_expand($opts->{'output-file'}) if exists($opts->{'output-file'});
5699             my $output = $opts->{'output-string'} || $opts->{'output-pipe'} ||
5700 2   50     25 $opts->{'output-file'} || undef;
5701             my $input = $opts->{'input-string'} || $opts->{'input-pipe'} ||
5702 2   0     9 $opts->{'input-file'} || '-';
5703              
5704 2 50       10 if (exists $opts->{'output-file'}) {
    50          
    50          
5705 0   0     0 open $out,'>'.$output || die "Cannot open output file ".$output."\n";
5706 0 0       0 if ($] >= 5.008) {
5707             binmode ($out,
5708             ($opts->{'output-encoding'} ?
5709 0 0       0 ":encoding(".$opts->{'output-encoding'}.")" : ":utf8"));
5710             }
5711             } elsif (exists $opts->{'output-pipe'}) {
5712 0   0     0 open $out,'| '.$output || die "Cannot open pipe to ".$output."\n";
5713 0 0       0 if ($] >= 5.008) {
5714             binmode ($out,
5715             ($opts->{'output-encoding'} ?
5716 0 0       0 ":encoding(".$opts->{'output-encoding'}.")" : ":utf8"));
5717             }
5718             } elsif (exists $opts->{'output-string'}) {
5719 0         0 my $output = $opts->{'output-string'};
5720 0 0       0 if ($output =~ /^\$(\$?[a-zA-Z_][a-zA-Z0-9_]*)$/) {
    0          
5721 0         0 $out = _get_var_ref($output);
5722             } elsif (ref($OUT)=~/Term::ReadLine/) {
5723 0         0 $out = *$OUT;
5724 0         0 $termout=1;
5725             } else {
5726 0         0 $out = $OUT;
5727 0         0 $termout=1;
5728             }
5729             } else {
5730 2         5 $out = $output;
5731             }
5732             my $parser=XML::LibXML::SAX
5733             ->new( Handler =>
5734             XML::Filter::DOMFilter::LibXML
5735             ->new($opts->{'no-output'} ? ()
5736             : (Handler => XML::SAX::Writer::XML
5737             ->new(
5738             Output => $out,
5739             Writer => 'XML::SAX::Writer::XMLEnc'
5740             )),
5741             XPathContext => $_xpc,
5742             Process => [
5743             map {
5744 2 50       9 $_->[0] => [\&stream_process_node,$_->[1],
  2         23  
5745             $input] }
5746             @$process
5747             ]
5748             )
5749             );
5750 2         155 my $old_context = _save_context();
5751 2         2 my $error;
5752 2 50       4 eval {
5753 2 100       6 if (exists $opts->{'input-pipe'}) {
    50          
5754 1         4373 open my $F,"$input|";
5755 1 50       67 $F || die "Cannot open pipe to $input: $!\n";
5756 1         101 $parser->parse_file($F);
5757 1         296 close $F;
5758             } elsif (exists $opts->{'input-string'}) {
5759 1         6 $parser->parse_string($input);
5760             } else { #file
5761 0         0 $parser->parse_uri($input);
5762             }
5763 2 50       366 if (exists $opts->{'output-pipe'}) {
5764 0         0 close($out);
5765             }
5766 2 50       7 if ($termout) { out("\n"); }
  0         0  
5767 2         6 1 } or $error = $@;
5768 2         7 _set_context($old_context);
5769 2 50       5 die $error if $error;
5770              
5771 2         83 return 1
5772             }
5773              
5774             sub iterate {
5775 0     0 0 0 my ($code,$axis,$nodefilter,$filter)=@_;
5776              
5777 0         0 $axis =~ s/::$//;
5778 0         0 $axis=~s/-/_/g;
5779              
5780 0 0       0 $filter =~ s/^\[\s*((?:.|\n)*?)\s*\]$/$1/ if defined $filter;
5781 0         0 my $test;
5782 0 0       0 if ($nodefilter eq "comment()") {
5783 0         0 $test = q{ $_xml_module->is_comment($_[0]) }
5784 0 0       0 } if ($nodefilter eq "text()") {
    0          
    0          
    0          
5785 0         0 $test = q{ $_xml_module->is_text_or_cdata($_[0]) }
5786             } elsif ($nodefilter =~ /processing-instruction\((\s*['"]([^'"]+)['"]\s*)?\)$/) {
5787 0         0 $test = q{ $_xml_module->is_pi($_[0]) };
5788 0 0       0 $test .= qq{ && (\$_[0]->nodeName eq '$1') } if $1 ne "";
5789             } elsif ($nodefilter eq 'node()') {
5790 0         0 $test = '1 ';
5791             } elsif ($nodefilter =~ /^(?:([^:]+):)?(.+)$/) {
5792 0         0 $test = q{ $_xml_module->is_element($_[0]) };
5793 0 0       0 $test .= qq{ && (\$_[0]->getLocalName() eq '$2') } unless ($2 eq '*');
5794 0 0       0 if ($1 ne "") {
5795 0         0 my $ns = xsh_context_node()->lookupNamespaceURI($1);
5796 0 0       0 die("Unrecognized namespace prefix '$1:'!") if ($ns eq "");
5797 0         0 $test .= qq{ && (\$_[0]->namespaceURI() eq '$ns') };
5798             }
5799             }
5800              
5801 0 0       0 die("Position index filter not supported for iteration ([$filter])") if $filter =~ /^\d+$/;
5802 0 0       0 if ($filter ne '') {
5803 0         0 $filter =~ s/\\/\\\\/g;
5804 0         0 $filter =~ s/'/\\'/g;
5805 0         0 $test .= qq{ && \$_xpc->find('$filter',\$_[0]) };
5806             }
5807 0 0       0 $test = "1" if $test eq "";
5808              
5809 0         0 my $filter_sub = eval "sub { $test }";
5810 0 0       0 die $@ if $@;
5811 0         0 my $iterator;
5812 0         0 do {
5813 0         0 my $start=xsh_context_node();
5814 0         0 $iterator=XML::XSH2::Iterators->create_iterator($start,$axis,$filter_sub);
5815             };
5816 0 0       0 return 1 unless defined $iterator;
5817              
5818 0         0 my $old_context=_save_context();
5819              
5820 0         0 my $count = 1;
5821 0         0 my $pos = 1;
5822 0         0 eval {
5823 0         0 ITER: while ($iterator->current()) {
5824 0         0 _set_context([$iterator->current(),$count,$pos]);
5825 0         0 eval {
5826 0         0 run_commands($code);
5827             };
5828 0 0 0     0 if (ref($@) and UNIVERSAL::isa($@,'XML::XSH2::Internal::LoopTerminatingException')) {
    0          
5829 0 0 0     0 if ($@->label =~ /^(?:next|last|redo|prev)$/ and $@->[1]>1) {
5830 0         0 $@->[1]--;
5831 0         0 die $@; # propagate to a higher level
5832             }
5833 0 0       0 if ($@->label eq 'next') {
    0          
    0          
    0          
5834 0         0 $count ++; $pos ++;
  0         0  
5835 0 0       0 $iterator->next() || last;
5836 0         0 next;
5837             } elsif ($@->label eq 'prev') {
5838 0         0 $pos --;
5839 0 0       0 $iterator->prev() || die("No previous node to iterate to!");
5840 0         0 next;
5841             } elsif ($@->label eq 'last') {
5842 0         0 last;
5843             } elsif ($@->label eq 'redo') {
5844 0         0 redo;
5845             } else {
5846 0         0 die $@; # propagate
5847             }
5848             } elsif ($@) {
5849 0         0 die $@; # propagate
5850             }
5851 0         0 $count ++; $pos ++;
  0         0  
5852 0 0       0 $iterator->next() || last;
5853             }
5854             };
5855 0         0 my $err = $@;
5856 0         0 do {
5857 0         0 local $SIG{INT}=\&flagsigint;
5858 0         0 _set_context($old_context);
5859 0         0 propagate_flagsigint();
5860             };
5861 0 0       0 die $err if $err; # propagate
5862 0         0 return 1;
5863             }
5864              
5865             # quit
5866             sub quit {
5867 0     0 0 0 my $opts = shift;
5868 0 0       0 if (ref($_on_exit)) {
5869 0         0 &{$_on_exit->[0]}($_[0],@{$_on_exit}[1..$#$_on_exit]); # run on exit hook
  0         0  
  0         0  
5870             }
5871 0         0 exit(int($_[0]));
5872             }
5873              
5874             sub register_ns {
5875 1     1 0 2 my $opts = shift;
5876 1         3 my $prefix = _ev_string($_[0]);
5877 1         3 my $ns = _ev_string($_[1]);
5878              
5879 1 50       9 unless ($prefix=~m{^[-_.[:alpha:]][-_.[:alnum:]]*$}) {
5880 0         0 die "Invalid namespace prefix '$prefix'\n";
5881             }
5882 1         2 $_ns{$prefix}=$ns;
5883 1         11 $_xpc->registerNs($prefix,$ns);
5884 1         2 return 1;
5885             }
5886              
5887             sub register_xsh_ns {
5888 0     0 0 0 my $opts = shift;
5889 0         0 register_ns($opts,_ev_string($_[0]),$XML::XSH2::xshNS);
5890             }
5891              
5892             sub register_xhtml_ns {
5893 0     0 0 0 my $opts = shift;
5894 0         0 register_ns($opts,_ev_string($_[0]),'http://www.w3.org/1999/xhtml');
5895             }
5896              
5897             sub unregister_ns {
5898 0     0 0 0 my ($opts,$exp)=@_;
5899 0         0 my $prefix = _ev_string($exp);
5900 0         0 delete $_ns{$prefix};
5901 0         0 $_xpc->unregisterNs($prefix);
5902 0         0 return 1;
5903             }
5904              
5905             sub get_registered_ns {
5906 0     0 0 0 return $_ns{$_[0]};
5907             }
5908              
5909             sub get_registered_prefix {
5910 0     0 0 0 my %r = reverse %_ns;
5911 0         0 return $r{$_[0]};
5912             }
5913              
5914             sub register_func {
5915 0     0 0 0 my ($opts,$name,$code)=@_;
5916 0         0 $name=_ev_string($name);
5917 0         0 my $sub;
5918 0 0       0 if ($code =~ /^\s*{/) {
    0          
5919 0         0 my $lex = lexicalize("sub $code");
5920 0         0 $sub = eval($lex);
5921             } elsif ($code =~/^\s*([A-Za-z_][A-Za-z_0-9]*(::[A-Za-z_][A-Za-z_0-9]*)*)\s*$/) {
5922 0 0       0 if ($2 ne "") {
5923 0         0 $sub=\&{"$1"};
  0         0  
5924             } else {
5925 0         0 $sub=\&{"XML::XSH2::Map::$1"};
  0         0  
5926             }
5927             } else {
5928 0         0 $sub = eval(lexicalize("sub \{ $code \}"));
5929             }
5930 0 0       0 die $@ if $@;
5931 0 0       0 if ($name =~ /^([^:]+):(.*)$/) {
5932 0 0       0 if (exists($_ns{$1})) {
5933 0         0 $_func{"$2\n$_ns{$1}"}=$sub;
5934 0         0 $_xpc->registerFunctionNS($2, $_ns{$1}, $sub);
5935             } else {
5936 0         0 die "Registration failed: unknown namespace prefix $1!\n";
5937             }
5938             } else {
5939 0         0 $_func{$name}=$sub;
5940 0         0 $_xpc->registerFunction($name, $sub);
5941             }
5942 0         0 return 1;
5943             }
5944              
5945             sub unregister_func {
5946 0     0 0 0 my ($opts,$name)=@_;
5947              
5948 0 0       0 if ($name =~ /^([^:]+):(.*)$/) {
5949 0 0       0 if (exists($_ns{$1})) {
5950 0         0 delete $_func{"$2\n$_ns{$1}"};
5951 0         0 $_xpc->unregisterFunctionNS($2, $_ns{$1});
5952             } else {
5953 0         0 die "Registration failed: unknown namespace prefix $1!\n";
5954             }
5955             } else {
5956 0         0 delete $_func{$name};
5957 0         0 $_xpc->unregisterFunction($name);
5958             }
5959 0         0 return 1;
5960             }
5961              
5962             sub node_type {
5963 1     1 0 4 my ($node)=@_;
5964 1 50       3 return undef unless $node;
5965 1 50       19 if ($_xml_module->is_element($node)) {
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
5966 0         0 return 'element';
5967             } elsif ($_xml_module->is_attribute($node)) {
5968 1         10 return 'attribute';
5969             } elsif ($_xml_module->is_text($node)) {
5970 0         0 return 'text';
5971             } elsif ($_xml_module->is_cdata_section($node)) {
5972 0         0 return 'cdata';
5973             } elsif ($_xml_module->is_pi($node)) {
5974 0         0 return 'pi';
5975             } elsif ($_xml_module->is_entity_reference($node)) {
5976 0         0 return 'entity_reference';
5977             } elsif ($_xml_module->is_document($node)) {
5978 0         0 return 'document';
5979             } elsif ($_xml_module->is_document_fragment($node)) {
5980 0         0 return 'chunk';
5981             } elsif ($_xml_module->is_comment($node)) {
5982 0         0 return 'comment';
5983             } elsif ($_xml_module->is_namespace($node)) {
5984 0         0 return 'namespace';
5985             } else {
5986 0         0 return 'unknown';
5987             }
5988             }
5989              
5990             #######################################################################
5991             #######################################################################
5992              
5993              
5994             package XML::XSH2::Map;
5995              
5996             BEGIN {
5997 15     14   2680 import XML::XSH2::Functions ':param_vars';
5998            
5999              
6000 15         90 *fromUTF8 = *XML::XSH2::Functions::fromUTF8;
6001 15         7300 *toUTF8 = *XML::XSH2::Functions::toUTF8;
6002             }
6003              
6004             sub call {
6005 0     0   0 XML::XSH2::Functions::call({},0,@_);
6006             }
6007              
6008             sub serialize {
6009 19     19   89 my $exp=$_[0];
6010 19         93 my $ql;
6011 19 100       68 if (ref($exp)) {
6012 14 50       55 if (UNIVERSAL::isa($exp,'XML::LibXML::NodeList')) {
    0          
6013 14         26 $ql=$exp;
6014             } elsif (UNIVERSAL::isa($exp,'XML::LibXML::Node')) {
6015 0         0 $ql=[$exp];
6016             } else {
6017 0         0 $ql=&XML::XSH2::Functions::_ev_nodelist($exp);
6018             }
6019             } else {
6020 5         19 $ql=&XML::XSH2::Functions::_ev_nodelist($exp);
6021             }
6022 19         65 my $result='';
6023 19         50 foreach (@$ql) {
6024 21         494 $result.=$_->toString();
6025             }
6026 19         336 return $result;
6027             }
6028              
6029             sub literal {
6030 0   0 0   0 my $xp=$_[0] || current();
6031 0 0       0 return XML::XSH2::Functions::to_literal(ref($xp) ? $xp : XML::XSH2::Functions::_ev($xp));
6032             }
6033              
6034             sub type {
6035 0     0   0 my ($xp)=@_;
6036 0         0 my $ql;
6037 0 0       0 unless (ref($xp)) {
    0          
6038 0 0       0 $xp='.' if $xp eq "";
6039 0         0 $ql = &XML::XSH2::Functions::_ev_nodelist($xp);
6040 0 0       0 } elsif (ref($xp) eq 'ARRAY' or
6041             UNIVERSAL::isa($xp,'XML::LibXML::NodeList')) {
6042 0         0 $ql = $xp;
6043             } else {
6044 0         0 $ql = [$xp];
6045             }
6046 0         0 my @result;
6047 0         0 foreach (@$ql) {
6048 0         0 push @result,&XML::XSH2::Functions::node_type($_);
6049 0 0       0 return $result[0] unless (wantarray);
6050             }
6051 0         0 return @result;
6052             }
6053              
6054             sub nodelist {
6055             return XML::LibXML::NodeList->new(map {
6056 0     0   0 XML::XSH2::Functions::cast_value_to_objects($_)
  0         0  
6057             } @_);
6058             }
6059              
6060             sub xpath {
6061 0     0   0 my ($exp, $node) = @_;
6062 0 0       0 $node = $node->[0] if (UNIVERSAL::isa($node,'XML::LibXML::NodeList'));
6063 0         0 my $r = $XML::XSH2::Functions::_xpc->find($exp,$node);
6064             # my $r = XML::XSH2::Functions::_ev($_[0]);
6065 0 0 0     0 if (wantarray and ref($r) and UNIVERSAL::isa($r,'XML::LibXML::NodeList')) {
      0        
6066 0         0 return @$r;
6067             } else {
6068 0         0 return $r;
6069             }
6070             }
6071              
6072             *echo = *XML::XSH2::Functions::out;
6073              
6074             sub xsh {
6075 183     183   2726 my ($p, $s, $l)=caller;
6076 183         604 local $XML::XSH2::Functions::SCRIPT="";
6077 183         618 XML::XSH2::Functions::run_string(join "",XML::XSH2::Functions::cast_objects_to_values(@_));
6078             }
6079              
6080             sub current {
6081 8     8   26 return XML::XSH2::Functions::xsh_context_node();
6082             }
6083              
6084             sub position {
6085 0 0   0   0 if ($XML::XSH2::Functions::_xpc->can('getContextPosition')) {
6086 0         0 return $XML::XSH2::Functions::_xpc->getContextPosition();
6087             } else {
6088 0         0 die "Sorry, installed XML::LibXML::XPathContext version doesn't support proximity position\n";
6089             }
6090             }
6091              
6092             *count = *XML::XSH2::Functions::count_xpath;
6093             *xml_list = *serialize;
6094              
6095             sub resolve_uri {
6096 0     0   0 my ($rel,$base)=@_;
6097 0 0       0 if (defined $base) {
6098 0         0 return URI->new_abs($rel,URI->new($base)->abs(URI::file->cwd));
6099             } else {
6100 0         0 return URI->new_abs($rel,URI::file->cwd);
6101             }
6102             }
6103              
6104             #######################################################################
6105             #######################################################################
6106              
6107             package XML::XSH2::Internal::Exception;
6108              
6109             sub new {
6110 28   33 28   111 my $class=(ref($_[0]) || $_[0]);
6111 28         51 shift;
6112 28         181 return bless [@_], $class;
6113             }
6114              
6115             sub set_label {
6116 0     0   0 my ($label)=@_;
6117 0         0 return $_[0]->[0]=$label;
6118             }
6119              
6120             sub label {
6121 84     84   470 return $_[0]->[0];
6122             }
6123              
6124             sub value {
6125 0     0   0 my ($index)=@_;
6126 0         0 return $_[0]->[$index];
6127             }
6128              
6129             sub set_value {
6130 0     0   0 my ($index,$value)=@_;
6131 0         0 return $_[0]->[$index]=$value;
6132             }
6133              
6134             package XML::XSH2::Internal::UncatchableException;
6135 15     14   96 use vars qw(@ISA);
  15         345  
  15         595  
6136             @ISA=qw(XML::XSH2::Internal::Exception);
6137              
6138             package XML::XSH2::Internal::LoopTerminatingException;
6139 15     14   89 use vars qw(@ISA);
  15         63  
  15         603  
6140             @ISA=qw(XML::XSH2::Internal::UncatchableException);
6141              
6142             package XML::XSH2::Internal::SubTerminatingException;
6143 15     15   68 use vars qw(@ISA);
  15         332  
  15         581  
6144             @ISA=qw(XML::XSH2::Internal::UncatchableException);
6145              
6146              
6147             #######################################################################
6148             #######################################################################
6149              
6150             package # hide from PAUSE
6151             XML::SAX::Writer::XMLEnc;
6152 15     15   66 use vars qw(@ISA);
  15         52  
  14         1082  
6153             @ISA=qw(XML::SAX::Writer::XML);
6154              
6155             sub xml_decl {
6156 0     0   0 my ($self,$data) = @_;
6157 0 0       0 if ($data->{Encoding}) {
6158 0         0 $self->{EncodeTo}=$data->{Encoding};
6159 0         0 $self->setConverter();
6160             }
6161 0         0 $self->SUPER::xml_decl($data);
6162             }
6163              
6164              
6165             # taken from Variable::Alias
6166             package XML::XSH2::VarAlias;
6167 14     15   78 use strict;
  14         288  
  14         249  
6168 14     15   61 use warnings;
  14         183  
  13         351  
6169 13     15   4380 use Tie::Scalar;
  13         4575  
  13         263  
6170 13     15   69 use vars qw(@ISA);
  13         82  
  12         684  
6171             @ISA=qw(Tie::StdScalar);
6172              
6173             sub TIESCALAR {
6174 364     364   6386 return bless $_[1], $_[0];
6175             }
6176              
6177             1;
6178