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   131 use strict;
  16         35  
  16         633  
9 16     16   88 no warnings;
  16         34  
  16         511  
10              
11 15     15   9065 use XML::XSH2::Help;
  15         64  
  15         1005  
12 15     15   16300 use XML::XSH2::Iterators;
  15         40  
  15         327  
13 15     15   778 use IO::File;
  15         9455  
  15         1408  
14 15     15   101 use File::Spec;
  15         32  
  15         320  
15 17     17   129 use Scalar::Util;
  17         36  
  17         1011  
16 17     17   6966 use File::Temp qw(tempfile tempdir);
  17         97197  
  17         648  
17 15     15   124 use Carp;
  15         34  
  15         720  
18 15     15   4616 use URI;
  15         38455  
  15         372  
19 17     17   4608 use URI::file;
  17         40839  
  17         780  
20              
21 17     17   122 use Exporter;
  17         41  
  17         722  
22 15         6292 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   120 /;
  15         38  
42              
43             BEGIN {
44 15     15   85 $VERSION='2.2.9'; # VERSION TEMPLATE
45 15         30 $REVISION=q($Revision: 2.49 $);
46 15         198 @ISA=qw(Exporter);
47 14         133 @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         47 *XSH_NS=*XML::XSH2::xshNS;
76 14         328 *XML::XSH2::Map::XSH_NS=*XML::XSH2::xshNS;
77 14         57 *XML::XSH2::Map::OUT=\$OUT;
78 14         35 *EMPTY_TAGS=*XML::LibXML::setTagCompression;
79 14         52 *SKIP_DTD=*XML::LibXML::skipDTD;
80 14         67 *XML::XSH2::Map::PROGRAM_NAME=\$RT_SCRIPT;
81 14         126 @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         436 %EXPORT_TAGS = (
91             default => [@EXPORT_OK],
92             param_vars => [@PARAM_VARS]
93             );
94              
95 14         69 $TRAP_SIGINT=0;
96 14         28 $_xml_module='XML::XSH2::LibXMLCompat';
97 14         57 $INDENT=1;
98 14         67 $EMPTY_TAGS=1; # no effect (reseted by XML::LibXML)
99 14         28 $SKIP_DTD=0; # no effect (reseted by XML::LibXML)
100 14         314 $BACKUPS=1;
101 14         75 $SWITCH_TO_NEW_DOCUMENTS=1;
102 14         31 $ENCODING='utf-8';
103 14         54 $QUERY_ENCODING='utf-8';
104 14         68 $QUIET=0;
105 14         43 $DEBUG=0;
106 14         303 $TEST_MODE=0;
107 14         55 $VALIDATION=0;
108 14         28 $RECOVERING=0;
109 14         47 $PARSER_EXPANDS_ENTITIES=1;
110 14         74 $KEEP_BLANKS=1;
111 14         36 $PEDANTIC_PARSER=0;
112 14         333 $LOAD_EXT_DTD=0;
113 14         91 $PARSER_COMPLETES_ATTRIBUTES=1;
114 14         37 $PARSER_EXPANDS_XINCLUDE=0;
115 14         50 $XPATH_COMPLETION=1;
116 14         68 $XPATH_AXIS_COMPLETION='always'; # never / when-empty
117 14         28 $DEFAULT_FORMAT='xml';
118 14         332 $LINE_NUMBERS=1;
119 14         54 $WARNINGS=1;
120 14         27 $ERRORS=1;
121 14         50 $BENCHMARK=0;
122 13         61 $MAXPRINTLENGTH=256;
123 13         56 $HISTFILE="$ENV{HOME}/.xsh2_history";
124 13         302 $STRICT_PWD=1;
125 13         49 $PROMPT='%p> ';
126 13         28 *XML::XSH2::Map::CURRENT_SCRIPT=\$RT_SCRIPT;
127              
128 13         61 $_newdoc=1;
129 13         84 $_die_on_err=1;
130 13         31 $_want_returns=0;
131              
132 13         395 autoflush STDOUT;
133 13         539 autoflush STDERR;
134 13         232 $lexical_variables = [];
135 13         47 $Xinclude_prefix = 'http://www.w3.org/2001/XInclude';
136 13         14102 require XML::XSH2::Commands;
137             }
138              
139             sub VERSION {
140 1 0   3 0 7 shift if $_[0] eq __PACKAGE__;
141 1         44 my $ver = shift;
142 1 0       6 if (defined($ver)) {
143 1         2 my @V = split /\./,$VERSION;
144 1         9 my @v = split /\./,$ver;
145 1         11 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         37 return $VERSION;
151             }
152              
153              
154 1 0   3 0 8 sub min { $_[0] > $_[1] ? $_[1] : $_[0] }
155              
156             sub out {
157 141 100   143 0 762 if (ref($OUT) eq 'IO::Scalar') {
158 6         28 $OUT->print(@_);
159             } else {
160 136         804 foreach (map(fromUTF8($ENCODING,$_), @_)) {
161 198         378 my $l = length;
162 198         305 my $i = 1;
163 198         597 while ($l > $i*$MAXPRINTLENGTH) {
164 6         168 print $OUT (substr($_,($i-1)*$MAXPRINTLENGTH,$MAXPRINTLENGTH));
165 6         26 $i++;
166             }
167 198         2395 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   37 _err("BUG: ",@_);
178             }
179              
180             sub _tilde_expand {
181 2     4   8 my ($filename)=@_;
182 2         7 $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         12 return $filename;
192             }
193              
194             sub _ev_opts {
195 266     266   642 my ($opts)=@_;
196 266 50       841 return {} unless ref($opts);
197 266 50       701 return $opts if ref($opts) eq 'HASH';
198 266         503 my %o;
199 266         500 my @opts = @$opts;
200 266         707 while (@opts) {
201 27         164 my ($t,$n)=split /\//,shift(@opts);
202 27         176 my $v=shift @opts;
203 27 100 100     185 if ($t eq '' or $t eq 'exp') {
    50          
    100          
204 20         90 $o { $n } = _ev($v);
205             } elsif ($t eq 'var') {
206 1         38 $o { $n } = $v;
207             } elsif ($t eq 'xpath') {
208 6         17 utf8::upgrade($v);
209 6         15 $o { $n } = _expand($v);
210             } else { # string
211 1         5 $o { $n } = _ev_string($v);
212             }
213             }
214 265         894 return \%o;
215             }
216              
217             sub _hash_opts {
218 11     12   28 my ($opts)=@_;
219 11 50       42 return {} unless ref($opts);
220 11         22 my %o;
221 11         36 my @opts = @$opts;
222 11         34 while (@opts) {
223 16         74 my ($t,$n)=split /\//,shift(@opts);
224 16         42 my $v=shift @opts;
225 16         64 $o { $n } = $v;
226             }
227 11         29 return \%o;
228             }
229              
230             sub alias_sr {
231 364     365 0 983 my($src, $dest)=@_;
232 364         1647 tie($$dest, 'XML::XSH2::VarAlias', $src);
233             }
234              
235             sub lexicalize {
236 342     343 0 742 my $p="package XML::XSH2::Map; no strict qw(vars); \$Lexical::Alias::SWAP=0; use utf8;";
237 342         549 my %seen;
238 342         1146 for (my $i=$#$lexical_variables; $i>=0; $i--) {
239 904         1256 foreach my $v (keys %{$lexical_variables->[$i]}) {
  904         2944  
240 364 50       928 next if $seen{$v};
241 364         664 $seen{$v}=1;
242 364         1645 $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         28820 return $p.$_[0];
247             }
248              
249             # initialize XSH and XML parsers
250             sub xsh_init {
251 15     16 0 62 my $module=shift;
252 15 50       52 shift unless ref($_[0]);
253 15 50       55 if (ref($_[0])) {
254 0         0 $OUT=$_[0];
255             } else {
256 15 50       476 if (open $OUT, '>&', \*STDOUT) {
257 15         72 binmode $OUT;
258 15         98 binmode $OUT, ':bytes';
259             } else {
260 0         0 $OUT = \*STDOUT;
261             }
262             }
263 15         82 set_encoding({},$ENCODING);
264 15 50       50 $_xml_module=$module if $module;
265 15         853 eval("require $_xml_module;");
266 15 50       84 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         108 my $mod=$_xml_module->module();
277 15 50       68 if ($] >= 5.008) {
278 15         78 require Encode;
279 15         114 *encodeToUTF8=*Encode::decode;
280 15         44 *decodeFromUTF8=*Encode::encode;
281             } else {
282 13     14   89 no strict 'refs';
  13         239  
  13         11777  
283 0         0 *encodeToUTF8=*{"$mod"."::encodeToUTF8"};
  0         0  
284 0         0 *decodeFromUTF8=*{"$mod"."::decodeFromUTF8"};
  0         0  
285             }
286 15         64 $_parser = $_xml_module->new_parser();
287              
288 15         372 xpc_init();
289             # xsh_rd_parser_init();
290              
291             # create a first document so that we always have non-empty context
292 15         68 create_doc('$scratch',"scratch",'xml','scratch.xml');
293 15         52 set_local_xpath({},'/');
294             }
295              
296             sub xsh_rd_parser_init {
297 8 50   9 0 32 unless ($_xsh) {
298 8 50       18 if (eval { require XML::XSH2::Parser; }) {
  8         39962  
299 8         56 $_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         61 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 18 sub set_indent { shift if @_>1; $INDENT=$_[0]; 1; }
  2         7  
  2         42  
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 47 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 49 $_xpc=new_xpath_context();
361 15         60 $_ns{xsh}=$XML::XSH2::xshNS;
362             }
363              
364             sub init_XPATH_funcs {
365 16     16 0 50 my ($xpc,$ns)=@_;
366 16         55 foreach my $name (get_XPATH_extensions()) {
367 784         1201 my $func=$name; $func =~ s/-/_/g;
  784         1238  
368 784         931 $xpc->registerFunctionNS($name,$ns,\&{"XPATH_$func"});
  784         3931  
369             }
370             }
371              
372             sub new_xpath_context {
373 15     15 0 28 my $xpc;
374 15 50       28 unless (eval { require XML::LibXML::XPathContext;
  15         65  
375 15         665 $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         843 $xpc = XML::LibXML::XPathContext->new();
383 15         110 $xpc->registerVarLookupFunc(\&xpath_var_lookup,undef);
384 15         78 $xpc->registerNs('xsh',$XML::XSH2::xshNS);
385 15         73 init_XPATH_funcs($xpc,$XML::XSH2::xshNS);
386 15         174 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         6 init_XPATH_funcs($_xpc,shift);
409 1         28 return 1;
410             }
411              
412             # ===================== XPATH EXT FUNC ================
413              
414             sub get_XPATH_extensions {
415 16     16 0 191 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   5933 use utf8;
  13         170  
  13         89  
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   1358 use utf8;
  13         298  
  13         63  
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   2320 use utf8;
  13         46  
  13         74  
474 0   0     0 return XML::LibXML::Literal->new($node->baseURI() || '');
475             }
476              
477              
478             sub XPATH_var {
479 4 50   4 0 364 die "Wrong number of arguments for function xsh:var(id)!\n" if (@_!=1);
480 4         18 my ($id)=literal_value($_[0]);
481 4         17 return var_value($id);
482             }
483              
484             sub XPATH_matches {
485 6 50 66 6 0 638 die "Wrong number of arguments for function xsh:matches(string,regexp)!\n" if (@_!=2 and @_!=3);
486 13     14   1293 use utf8;
  13         228  
  13         82  
487 6         18 my ($string,$regexp,$ignore_case)=@_;
488 6         21 $string=literal_value($string);
489 6         14 $regexp=literal_value($regexp);
490 6         13 $ignore_case=literal_value($ignore_case);
491 6 100       122 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 595 die "Wrong number of arguments for function xsh:substr(string,position,[length])!\n" if (@_<2 or @_>3);
500 13     14   1202 use utf8;
  13         43  
  13         90  
501 7         21 my ($str,$pos,$len)=@_;
502 7 100       31 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       21 $result = "" unless defined ($result);
509 7         22 return $result;
510             }
511              
512             sub XPATH_reverse {
513 3 50   3 0 274 die "Wrong number of arguments for function xsh:reverse(string)!\n" if (@_!=1);
514 13     14   1313 use utf8;
  13         243  
  13         122  
515 3         13 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   749 use utf8;
  13         42  
  13         79  
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   683 use utf8;
  13         251  
  13         66  
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   696 use utf8;
  13         42  
  13         72  
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   694 use utf8;
  13         277  
  13         69  
539 0         0 return ucfirst(literal_value($_[0]));
540             }
541              
542             sub XPATH_grep {
543 13 50   13 0 1325 die "Wrong number of arguments for function xsh:grep(list,regexp)!\n" if (@_!=2);
544 13         40 my ($nodelist,$regexp)=@_;
545 13 50 33     90 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   964 use utf8;
  13         47  
  13         79  
548 13         42 [grep { $_->to_literal=~m{$regexp} } @$nodelist];
  26         427  
549             }
550              
551             sub XPATH_same {
552 13 50   13 0 1199 die "Wrong number of arguments for function xsh:same(node,node)!\n" if (@_!=2);
553 13         39 my ($nodea,$nodeb)=@_;
554 13 50 33     123 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     88 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     64 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 622 my $r;
564 7         23 foreach (cast_objects_to_values(@_)) {
565 17 50       110 next unless /^\s*(-\s*)?(\d+(\.\d*)?|\.\d+)\s*$/;
566 17 100       41 $r = $_ unless defined($r);
567 17 100       45 $r = $_>$r ? $_ : $r;
568             }
569 7         25 ; 0+$r;
570             }
571              
572             sub XPATH_strmax {
573 4     4 0 408 my $r;
574 4         14 foreach (cast_objects_to_values(@_)) {
575 9 100       25 $r = $_ unless defined($r);
576 9 100       27 $r = $_ ge $r ? $_ : $r;
577             }
578 4 50       23 ; defined($r) ? $r : "";
579             }
580              
581             sub XPATH_min {
582 9     9 0 849 my $r;
583 9         29 foreach (cast_objects_to_values(@_)) {
584 21 50       148 next unless /^\s*(-\s*)?(\d+(\.\d*)?|\.\d+)\s*$/;
585 21 100       58 $r = $_ unless defined($r);
586 21 100       60 $r = $_ < $r ? $_ : $r;
587             }
588             ;
589 9         35 return 0+$r;
590             }
591              
592             sub XPATH_strmin {
593 4     4 0 373 my $r;
594 4         14 foreach (cast_objects_to_values(@_)) {
595 9 100       24 $r = $_ unless defined($r);
596 9 100       26 $r = $_ le $r ? $_ : $r;
597             }
598 4 50       17 ; defined($r) ? $r : "";
599             }
600              
601             sub XPATH_sum {
602 9     9 0 940 my $r=0;
603 9         35 foreach (cast_objects_to_values(@_)) {
604 29         65 $r += $_;
605             }
606 9         34 ; $r;
607             }
608              
609             sub XPATH_join {
610 4     4 0 431 my $j=literal_value(shift @_);
611 4         14 join $j,cast_objects_to_values(@_);
612             }
613              
614             sub XPATH_serialize {
615 21     21 0 2032 my $result="";
616 21         63 foreach my $obj (@_) {
617 23 50 33     212 if (ref($obj) and
618             UNIVERSAL::isa($obj,'XML::LibXML::NodeList')) {
619 23         68 foreach my $node (@$obj) {
620 41         942 $result.=$node->toString();
621             }
622             } else {
623 0         0 $result.=literal_value($obj);
624             }
625             }
626 21         82 $result;
627             }
628              
629             sub XPATH_subst {
630 11 50 66 11 0 1429 die "Wrong number of arguments for function xsh:subst(string,regexp,replacement,[options])!\n" if (@_!=3 and @_!=4);
631 13     13   6519 use utf8;
  13         314  
  13         77  
632 11         39 my ($string,$regexp,$replace,$options)=@_;
633 11         37 $string=literal_value($string);
634 11         28 $regexp=literal_value($regexp);
635 11 50       39 return $string unless $regexp ne "";
636 11         24 $replace=literal_value($replace);
637 11         21 $options=literal_value($options);
638 11 50       62 die "Invalid options: $options (should only consist of 'egimsx')!\n"
639             unless ($options =~ /^[egimsx]*$/);
640 11         69 $replace =~ s{\\(.)|(/)|(\\)$}{\\$1$2$3}gs;
641 11         1164 eval "\$string=~s/\$regexp/$replace/$options";
642 11         69 return $string;
643             }
644              
645             sub XPATH_parse {
646 13     13   1813 use utf8;
  13         63  
  13         95  
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 779 die "Wrong number of arguments for function xsh:sprintf(format-string,...)!\n" if (@_<1);
658 13     13   1241 use utf8;
  13         322  
  13         81  
659 7         24 my @args=map { literal_value($_) } @_;
  16         42  
660 7         102 return sprintf(shift(@args),@args);
661             }
662              
663             sub XPATH_current {
664 6 50   6 0 447 die "Wrong number of arguments for function xsh:current()!\n" if (@_);
665 6         18 my $ln = xsh_context_node();
666 6 50       38 return XML::LibXML::NodeList->new($ln ? $ln : ());
667             }
668              
669             sub XPATH_path {
670 5 50   5 0 409 die "Wrong number of arguments for function xsh:path(nodeset)!\n" if (@_!=1);
671 5 50 33     52 die "Wrong type of argument 1 for xsh:path(nodeset)!\n" unless (ref($_[0]) and UNIVERSAL::isa($_[0],'XML::LibXML::NodeList'));
672 5 50       27 return "" unless $_[0][0];
673             return
674 5         54 XML::LibXML::Literal->new(pwd($_[0][0]));
675             }
676              
677             sub XPATH_node_type {
678 1 50   1 0 67 die "Wrong number of arguments for function xsh:node-type(node-set)!\n" if (@_!=1);
679 1 50 33     9 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       45 return "" unless $_[0][0];
681             return
682 1         12 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 1005 die "Wrong number of arguments for function xsh:map(nodeset,string)!\n"
729             if (@_!=2);
730 7 50 33     96 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         27 my ($nl,$xpath)=@_;
733 7         31 my $res = XML::LibXML::NodeList->new();
734 7 50 33     44 unless (@{$nl} and $xpath ne "") { return $res; }
  7         59  
  0         0  
735 7 50       33 return $res if $xpath eq "";
736             # my $xpc = clone_xpc();
737 7         12 my $res_el;
738 7 50       76 $_xpc->setContextSize(0+@{$nl}) if $_xpc->can('setContextSize');
  7         33  
739 7         11 my $pos=1;
740 7         20 foreach my $node (@{$nl}) {
  7         30  
741 9 50       152 $_xpc->setContextPosition($pos++) if $_xpc->can('setContextSize');
742 9         13 my $val;
743 9         20 eval { $val = $_xpc->find($xpath,$node); };
  9         44  
744 9 50       500 return XML::LibXML::NodeList->new() if $@;
745 9 50       35 next unless (ref($val));
746 9         39 push @$res,cast_value_to_objects($val,undef,1);
747             }
748 7         110 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   7791 use utf8;
  13         51  
  13         98  
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 297 die "Wrong number of arguments for function xsh:new-attributes(string, string, [string, string,...])!\n"
799             unless (@_ and (scalar(@_) % 2 == 0));
800 3         8 my %attr=map { literal_value($_) } @_;
  10         21  
801 3         11 my $doc = $_xpc->getContextNode;
802 3 50 33     35 unless (ref($doc) and ref($doc = $doc->ownerDocument())) {
803 0         0 die "No context document\n";
804             }
805 3         12 return XML::LibXML::NodeList->new(map {$doc->createAttribute($_,$attr{$_})} keys %attr);
  5         48  
806             }
807              
808             sub XPATH_new_element {
809 2 50   2 0 177 die "Wrong number of arguments for function xsh:new-element(string, [string,string,...])!\n"
810             unless (scalar(@_)%2);
811 2         6 my ($name,%attrs)=map {literal_value($_)} @_;
  6         11  
812 2         9 my $doc = $_xpc->getContextNode;
813 2 50 33     21 unless (ref($doc) and ref($doc = $doc->ownerDocument())) {
814 0         0 die "No context document\n";
815             }
816 2         17 my $e = $doc->createElement($name);
817 2         9 foreach my $aname (keys %attrs) {
818 2         28 $e->setAttribute($aname,$attrs{$aname});
819             }
820 2         15 return XML::LibXML::NodeList->new($e);
821             }
822              
823             sub XPATH_new_element_ns {
824 1 50 33 1 0 188 die "Wrong number of arguments for function xsh:new-element-ns(string, string, [string,string])!\n"
825             unless (@_ and (scalar(@_)+1)%2);
826 1         4 my ($name,$ns,%attrs)=map {literal_value($_)} @_;
  6         11  
827 1         10 my $doc = $_xpc->getContextNode;
828 1 50 33     14 unless (ref($doc) and ref($doc = $doc->ownerDocument())) {
829 0         0 die "No context document\n";
830             }
831             # __debug("ns: $ns");
832 1         13 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         6 foreach my $aname (keys %attrs) {
837 2         19 $e->setAttribute($aname,$attrs{$aname});
838             }
839 1         16 return XML::LibXML::NodeList->new($e);
840             }
841              
842              
843             sub XPATH_new_text {
844 1 50   1 0 63 die "Wrong number of arguments for function xsh:new-text(string)!\n"
845             if (@_!=1);
846 1         5 my $text=literal_value(shift);
847 1         5 my $doc = $_xpc->getContextNode;
848 1 50 33     12 unless (ref($doc) and ref($doc = $doc->ownerDocument())) {
849 0         0 die "No context document\n";
850             }
851 1         10 my $t=$doc->createTextNode($text);
852 1         5 return XML::LibXML::NodeList->new($t);
853             }
854              
855             sub XPATH_new_comment {
856 1 50   1 0 64 die "Wrong number of arguments for function xsh:new-comment(string)!\n"
857             if (@_!=1);
858 1         4 my $text=literal_value(shift);
859 1         5 my $doc = $_xpc->getContextNode;
860 1 50 33     12 unless (ref($doc) and ref($doc = $doc->ownerDocument())) {
861 0         0 die "No context document\n";
862             }
863 1         10 my $t=$doc->createComment($text);
864 1         5 return XML::LibXML::NodeList->new($t);
865             }
866              
867             sub XPATH_new_cdata {
868 2 50   2 0 242 die "Wrong number of arguments for function xsh:new-cdata(string)!\n"
869             if (@_!=1);
870 2         12 my $name=literal_value(shift);
871 2         12 my $doc = $_xpc->getContextNode;
872 2 50 33     38 unless (ref($doc) and ref($doc = $doc->ownerDocument())) {
873 0         0 die "No context document\n";
874             }
875 2         24 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 170 die "Wrong number of arguments for function xsh:new-pi(string,[ string])!\n"
881             if (!@_ or @_>2);
882 1         4 my ($name,$value)=map { literal_value($_) } @_;
  2         7  
883 1         6 my $doc = $_xpc->getContextNode;
884 1 50 33     22 unless (ref($doc) and ref($doc = $doc->ownerDocument())) {
885 0         0 die "No context document\n";
886             }
887 1         15 my $pi = $doc->createPI($name => $value);
888 1         10 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   17378 no strict 'refs';
  13         280  
  13         4264  
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   75 no strict qw(refs);
  13         59  
  13         366  
1071 13     13   6191 use Data::Dumper;
  13         57789  
  13         1180  
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   91 no strict qw(refs);
  13         668  
  13         404  
1080 13     13   64 use Data::Dumper;
  13         265  
  13         10000  
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 849 my $res=eval { encodeToUTF8($_[0],$_[1]) };
  383         2244  
1111 383 50       36868 if ($@ =~ /^SIGINT/) {
1112 0         0 die $@
1113             } else {
1114 383         836 undef $@;
1115             }
1116 383 100       2216 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 440 my $res=eval { decodeFromUTF8($_[0],$_[1]) };
  214         1081  
1124 214 50       9522 if ($@ =~ /^SIGINT/) {
1125 0         0 die $@
1126             } else {
1127 214         366 undef $@;
1128             }
1129 214 100       8390 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 33 my ($test)=@_;
1135 10         61 foreach my $cmd (@XML::XSH2::CompletionList::XSH_COMMANDS) {
1136 1860 50       3127 return 1 if $cmd eq $test;
1137             }
1138 10         52 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 67297 print STDERR "Benchmark: running script $SCRIPT\n" if $BENCHMARK;
1154 367 50       898 require Benchmark if $BENCHMARK;
1155 367         675 my ($t0,$t1);
1156 367 50       853 $t0 = Benchmark->new() if $BENCHMARK;
1157 367 100       1286 unless (ref($_xsh)) {
1158 8         29 xsh_init();
1159 8         37 xsh_rd_parser_init();
1160             }
1161 367 50       1136 $t1 = Benchmark->new() if $BENCHMARK;
1162 367 50       1170 print STDERR "Benchmark: init xsh took:",benchtime($t1,$t0),"\n" if $BENCHMARK;
1163 367 50       1352 if (ref($_xsh)) {
1164 367         1729 my $code=join "",map toUTF8($QUERY_ENCODING,$_),@_;
1165 367         1317 return run($code);
1166             } else {
1167 0         0 die "XSH init failed!\n";
1168             }
1169             }
1170              
1171             sub run {
1172 367     367 0 944 my ($code) = @_;
1173 367 100       2544 return 1 if ($code=~/^\s*$/);
1174 366 50       1168 require Benchmark if $BENCHMARK;
1175 366 50       1143 my $t0 = Benchmark->new() if $BENCHMARK;
1176 366         3335 my $pt = $_xsh->startrule($code);
1177 366 50       1156 my $t1 = Benchmark->new() if $BENCHMARK;
1178 366 50       959 print STDERR "Benchmark: xsh parsing took:",benchtime($t1,$t0),"\n" if $BENCHMARK;
1179              
1180             # __debug "Post processing parse tree";
1181 366 50       918 $t0 = Benchmark->new() if $BENCHMARK;
1182 366         1271 post_process_parse_tree($pt);
1183 366 50       1019 $t1 = Benchmark->new() if $BENCHMARK;
1184 366 50       888 print STDERR "Benchmark: compile took:",benchtime($t1,$t0),"\n" if $BENCHMARK;
1185 366 50       896 dump_parse_tree($pt) if defined $DUMP;
1186 366 50       783 $t0 = Benchmark->new() if $BENCHMARK;
1187 366         1109 my $result=run_commands($pt,1);
1188 366 50       979 $t1 = Benchmark->new() if $BENCHMARK;
1189 366 50       860 print STDERR "Benchmark: execution took:",benchtime($t1,$t0),"\n" if $BENCHMARK;
1190 366         3429 return $result;
1191             }
1192              
1193             sub dump_parse_tree {
1194 0     0 0 0 my ($pt) = shift;
1195 13     13   84 use Data::Dumper;
  13         44  
  13         25430  
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 9277 my ($pt)=@_;
1260             # __debug "[ $pt";
1261 6007 100       16021 if (ref($pt) eq 'XML::XSH2::Command') {
    100          
1262             # __debug "COMMAND: @$pt\n";
1263 669         2677 my ($line,$column,$offset,$script,$cmd,@args)=@$pt;
1264 669 50       1650 unless (ref($cmd)) {
1265 669         1932 my $spec = $COMMANDS{$cmd};
1266 669 100 100     2823 $spec = $COMMANDS{$spec} if ($spec and !ref($spec));
1267 669 100       1314 if ($spec) {
1268 540         1769 my ($func,$minargs,$maxargs,$optspec,@extraargs)=@$spec;
1269 540         860 my @opts;
1270             # print STDERR ("matched $cmd\n");
1271 540         1353 while (@args) {
1272 540 100 100     2412 if ($optspec and $args[0]=~/^--(.*)$|^:(.)$/) {
1273 35 100       205 my $opt = defined($1) ? $1 : $optspec->{$2}; # resolve short opt
1274 35 50       112 if (exists($optspec->{$opt})) {
1275 35         67 shift(@args);
1276 35 100       112 if ($optspec->{$opt} ne '') {
1277 22         76 push @opts, $optspec->{$opt}.'/'.$opt;
1278 22         61 push @opts,shift(@args);
1279             } else {
1280 13         63 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         834 last;
1287             }
1288             }
1289 540 50 66     2600 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         1154 foreach (@args) {
1295 725 100       1664 if (ref eq 'ARRAY') {
1296 1         3 post_process_parse_tree($_);
1297             }
1298             }
1299 540         3924 @$pt=($line,$column,$offset,$script,$func,\@opts,@args,@extraargs);
1300             } else {
1301             # print STDERR ("$cmd is a sub call?\n");
1302 129         891 @$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         3247 for(my $i=0;$i<=$#$pt;$i++) {
1308 5432 50       11528 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       688 if (ref($_)) {
1332 74 50       538 UNIVERSAL::can($_,'textContent') ? $_->textContent() : $_->value();
1333 208         907 } else { $_ }
1334             } map {
1335 220 100   220 0 521 UNIVERSAL::isa($_,'XML::LibXML::NodeList') ? @$_ : $_;
  245         2010  
1336             } @_;
1337             }
1338              
1339             sub create_xsh_result_element {
1340 12     12 0 77 my $res_doc=XML::LibXML::Document->new();
1341 12         137 my $res_el=$res_doc->createElementNS($XML::XSH2::xshNS,'xsh:result');
1342 12         77 $res_doc->setDocumentElement($res_el);
1343 12         219 return $res_el;
1344             }
1345              
1346             sub cast_value_to_objects {
1347 286     286 0 638 my ($val, $res_el, $wrap)=@_;
1348 286 100       838 if (UNIVERSAL::isa($val,"XML::LibXML::NodeList")) {
    100          
1349 266         1199 return @$val;
1350             } elsif (UNIVERSAL::isa($val,"XML::LibXML::Node")) {
1351 8         17 return ($val);
1352             }
1353 12 50       50 $res_el = create_xsh_result_element() unless (ref($res_el));
1354 12         55 my $res_doc = $res_el->ownerDocument;
1355 12         29 my $el;
1356             my $res;
1357 12 100       79 if (!ref($val)) {
    100          
    50          
    50          
    0          
1358 7 100       45 if ($val =~ /^\s*[+-]?(\d+(\.\d*)?|\.\d+)\s*$/) {
1359 6         45 $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         35 $el->appendText($val);
1364 7         30 $res = $el->firstChild;
1365             } elsif (UNIVERSAL::isa($val,"XML::LibXML::Literal")) {
1366 4         28 $el = $res_doc->createElementNS($XML::XSH2::xshNS,'xsh:string');
1367 4         15 $el->appendText($val->value);
1368 4         64 $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         12 $el = $res_doc->createElementNS($XML::XSH2::xshNS,'xsh:number');
1375 1         16 $el->appendText($val->value);
1376 1         26 $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         87 $res_el->appendChild($el);
1386 12 100       51 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   3268 my ($exp,$map,$in_place)=@_;
1397 1532 100       3099 return undef unless defined $exp;
1398 1529 100       5292 utf8::upgrade($exp) unless ref($exp);
1399 1529 100       10998 if (ref($exp) eq 'ARRAY') {
    100          
    100          
    100          
    50          
1400 3         13 return run_commands($exp,0,1);
1401             } elsif ($exp =~ /^<<(.)/) {
1402             # inline document
1403 1 50       7 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         385 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         2292 my $ret = eval { $_xpc->find(_expand($exp)); };
  1355         3544  
1426 1355         60942 _check_err($@,1,1);
1427 1355 100 66     8130 if (ref($ret) and UNIVERSAL::isa($ret,'XML::LibXML::Literal')) {
1428 624         2009 return $ret->value;
1429             } else {
1430 731         1960 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   810 my @res = map { cast_value_to_objects($_) } _ev($_[0]);
  277         1003  
1443 267 100       867 if (wantarray) {
1444 13         45 return @res;
1445             } else {
1446 254         690 XML::LibXML::NodeList->new(@res);
1447             }
1448             }
1449              
1450             sub _ev_list {
1451 1     1   2 my $exp = $_[0];
1452 1 50       4 if (ref($exp) eq 'ARRAY') {
1453 0         0 $exp = run_commands($exp, 0, 1);
1454 1 50       6 } 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         3 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   584 my ($exp,$map,$in_place)=@_;
1482 254 100       726 return "" if $exp eq "";
1483 226         537 my $val = _ev($exp,$map,$in_place);
1484 226         1535 return to_literal($val);
1485             }
1486              
1487             sub to_literal {
1488 242     242 0 505 my ($obj)=@_;
1489 242 100       550 if (!ref($obj)) {
1490 211         365 my $r=$obj;
1491 211         585 utf8::upgrade($r);
1492 211         673 return $r;
1493             } else {
1494 31 50       261 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         58 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         48 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   676 my ($exp)=@_;
1522 318 50       830 return "" if $exp eq "";
1523 318         748 my $result = _ev($exp);
1524 318 100       1005 if (!ref($result)) {
1525 50         181 return $result;
1526             } else {
1527 268 100       2440 if (UNIVERSAL::isa($result,'XML::LibXML::NodeList')) {
    50          
    50          
1528 61         181 return $result->size();
1529             } elsif (UNIVERSAL::isa($result,'XML::LibXML::Node')) {
1530 0         0 return 1;
1531             } elsif(ref($result)=~/^XML::LibXML/) {
1532 207         707 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   27 my ($exp)=@_;
1543 5 100       46 $exp = "." if $exp eq "";
1544 5         37 my ($node)=_ev_nodelist($exp)->pop();
1545 5 50       109 if ($node) {
1546 5         89 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   700 my ($exp,$map,$in_place)=@_;
1564 247 100       1466 if ($exp=~/^\s*(?:&|<<|{)|[\'\"\[\]\(\)\@]|::|\$/) {
1565 140         420 return _ev_literal($exp,$map,$in_place);
1566             } else {
1567 107         318 return _expand($exp);
1568             }
1569             }
1570              
1571             sub xsh_parse_string {
1572 53   66 53 0 177 my $format=$_[1] || $DEFAULT_FORMAT;
1573 53         102 local $VALIDATION=0;
1574 53 50       150 if ($format eq 'xml') {
    0          
    0          
1575 53         80 my $xmldecl;
1576 53 100       254 $xmldecl="" unless $_[0]=~/^\s*\<\?xml /;
1577 53         428 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   3 my @ret;
1614 13     13   132 no strict 'refs';
  13         320  
  13         3755  
1615             my %seen;
1616 1         3 foreach my $var (keys %{"XML::XSH2::Map::"}) {
  1         109  
1617 53         53 my $value = ${"XML::XSH2::Map::".$var};
  53         194  
1618 53 100       92 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     36 if (UNIVERSAL::isa($value,'XML::LibXML::Node') and
      66        
1622             $_xml_module->is_document($value) and !exists($seen{$$value})) {
1623 2         42 push @ret, [$value, $var];
1624 2         9 $seen{$$value}=undef;
1625             }
1626             }
1627             }
1628 1         9 my $cur_doc = $_xml_module->owner_document(xsh_context_node());
1629 1 50       9 if (!exists($seen{$$cur_doc})) {
1630 0         0 push @ret, [$cur_doc, undef];
1631             }
1632 1         10 return @ret;
1633             }
1634              
1635              
1636             sub files {
1637 1     1 0 4 my $opts = shift;
1638 1         5 for my $f (_files) {
1639 2 50       16 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 57 my ($doc,$value)=@_;
1650 34 100       73 if (ref($value)) {
1651 6 50 100     78 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         10 undef $value;
1658             }
1659             }
1660 34         89 return $value;
1661             }
1662              
1663             sub close_doc {
1664 1     1 0 21 my ($opts,$exp)=@_;
1665 1         24 my $doc = _ev_doc($exp);
1666 13     13   78 no strict 'refs';
  13         59  
  14         1770  
1667 1         7 foreach my $var (keys %{"XML::XSH2::Map::"}) {
  1         120  
1668 55         68 my $value = ${"XML::XSH2::Map::".$var};
  55         156  
1669 55 100       101 next unless defined $value;
1670 34 100       68 undef ${"XML::XSH2::Map::".$var}
  1         12  
1671             unless defined(close_undef_value($doc,$value));
1672             }
1673 1         15 foreach my $lex_context (@$lexical_variables) {
1674 1         11 my ($name,$value);
1675 1         22 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         32 return 1;
1684             }
1685              
1686             sub xpath_var_lookup {
1687 334     334 0 11342 my ($data,$name,$ns)=@_;
1688 14     13   83 no strict;
  14         340  
  14         3732  
1689 334         641 my $res;
1690 334 50       1045 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         808 my $lex = lex_var($name);
1697 334 100       750 if ($lex) {
    50          
1698 37         78 $res = $$lex
1699 297         2103 } elsif (defined(${"XML::XSH2::Map::$name"})) {
1700 297         498 $res = ${"XML::XSH2::Map::$name"};
  297         1550  
1701             } else {
1702 0         0 die "Undefined variable '\$$name'\n";
1703             }
1704             }
1705 334 100 100     2158 if (ref($res) and UNIVERSAL::isa($res,'XML::LibXML::Node')) {
1706 26         378 return XML::LibXML::NodeList->new($res);
1707             } else {
1708 308         3217 return $res;
1709             }
1710             }
1711              
1712             sub lex_var {
1713 1165     1165 0 3748 my ($n)=@_;
1714 1165         5561 for (my $i=$#$lexical_variables; $i>=0; $i--) {
1715 2293 100       6553 return \$lexical_variables->[$i]{$n} if exists($lexical_variables->[$i]{$n});
1716             }
1717 693         2364 return undef;
1718             }
1719              
1720             # return a value of the given XSH variable
1721             sub var_value {
1722 91     91 0 232 my ($var) = @_;
1723 91 50       523 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   85 no strict qw(refs);
  14         54  
  14         1059  
1730 91         220 my $lex = lex_var($1);
1731 91 100       224 if ($lex) {
    100          
1732 48         224 return $$lex
1733 43         378 } elsif (defined(${"XML::XSH2::Map::$1"})) {
1734 36         70 return ${"XML::XSH2::Map::$1"};
  36         228  
1735             }
1736             } else {
1737 0         0 return undef;
1738             }
1739             }
1740              
1741             sub string_vars {
1742 14     13   79 no strict qw(refs);
  14         329  
  14         786  
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   68 no strict 'refs';
  14         52  
  15         19663  
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 34 my $opts = _ev_opts(shift);
1771             my $val = join(($opts->{nospace} ? "" : " ") ,
1772 10 50       58 (map _ev_string($_),@_)).($opts->{nonl} ? "" : "\n");
    50          
1773 10 50       142 $opts->{stderr} ? (print STDERR $val) : out($val);
1774 10         42 return 1;
1775             }
1776 14 100   14 0 973 sub set_quiet { shift if @_>1; $QUIET=$_[0]; return 1; }
  14         40  
  14         77  
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 46 my ($enc)=@_;
1782 16 50 33     61 if (
1783             defined(toUTF8($enc,'')) and defined(fromUTF8($enc,''))
1784             ) {
1785             # print STDERR "OK\n";
1786 16         50 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 61 shift if @_>1; # opts
1796             # print STDERR "ENCOD: @_\n";
1797 16         71 my $enc=_ev_string($_[0]);
1798 16         57 my $ok=test_enc($enc);
1799 16         38 $ENCODING=$enc;
1800 16         41 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 17026 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   52 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   5563 my ($err,$survive_int,$remove_at)=@_;
1878 2389 100       4962 if ($err) {
1879             # cleanup the error message
1880 63         168 $err =~ s/^XPathContext: error coming back from perl-dispatcher in pm file\.\s*//;
1881 63 50 33     171 if ($remove_at and !ref($err)) {
1882 0         0 $err=~s/ at (?:.|\n)*$//;
1883             }
1884              
1885 63 50       220 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       218 if ($err=~/^SIGPIPE/) {
    100          
1901 0         0 _err('broken pipe (SIGPIPE)');
1902             } elsif (ref($err)) {
1903 40         202 die $err; # propagate
1904             } else {
1905 23         60 chomp $err;
1906 23 100       194 unless ($err=~/ at (?:.|\n)*$/) {
1907 5         37 $err.=" at "._rt_position();
1908             }
1909 23         345 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         3481 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 1131 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 70 my ($opts,$exp)=@_;
1942 27 100       75 $exp = "/" if ($exp eq "");
1943 27         90 _set_context([_ev_nodelist($exp)->shift()]);
1944 27         186 return 1;
1945             }
1946              
1947             sub cannon_name {
1948 61     61 0 93 my ($node)=@_;
1949 61         257 my $local_name =$node->localname();
1950 61         173 my $uri = $node->namespaceURI();
1951 61 50       140 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         168 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 194 my $node = shift || $_xpc->getContextNode();
1972 97         405 my $no_parent = shift;
1973 97         126 my $name;
1974 97 100 66     359 if ($_xml_module->is_element($node)) {
    100          
    50          
    50          
    50          
1975 61         135 $name=cannon_name($node);
1976             } elsif ($_xml_module->is_text($node) or
1977             $_xml_module->is_cdata_section($node)) {
1978 5         33 $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     761 if (!$no_parent and $node->parentNode) {
1988 66         449 my @children;
1989             # if ($_xml_module->is_element($node)) {
1990             # @children=$_xpc->findnodes("./$name",$node->parentNode);
1991             # } else {
1992 66         180 my $context = $_xpc->getContextNode;
1993 66         116 @children= eval { $_xpc->findnodes("./$name",$node->parentNode) };
  66         410  
1994             # }
1995 66 100 66     3137 if (@children == 1 and $_xml_module->xml_equal($node,$children[0])) {
1996 45         187 return "$name";
1997             }
1998 21         409 for (my $pos=0;$pos<@children;$pos++) {
1999 106 100       259 return "$name"."[".($pos+1)."]"
2000             if ($_xml_module->xml_equal($node,$children[$pos]));
2001             }
2002 0         0 return "??$name??";
2003             } else {
2004 31         69 return ();
2005             }
2006             }
2007              
2008             # parent element (even for attributes)
2009             sub tree_parent_node {
2010 170     170 0 508 my $node=$_[0];
2011 170 50       2205 if ($_xml_module->is_attribute($node)) {
2012 0         0 return $node->ownerElement();
2013             } else {
2014 170         837 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 273 my $node=shift || $_xpc->getContextNode();
2035 31         195 my $use_id = shift;
2036 31 50       119 return undef unless ref($node);
2037 31 50 33     100 return $node->nodePath() if !$STRICT_PWD and UNIVERSAL::can($node,'nodePath');
2038 31         71 my @pwd=();
2039 31         52 do {
2040 97 50       687 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         210 unshift @pwd,node_address($node);
2047 97         1783 $node=tree_parent_node($node);
2048             } while ($node);
2049 31         142 my $pwd="/".join "/",@pwd;
2050 31         117 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 91 my $opts = _ev_opts(shift);
2062            
2063 17         111 my $pwd=pwd(undef, $opts->{id});
2064 17 50       59 if ($pwd) {
2065 17         97 out("$pwd\n");
2066 17         175 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   3323 my ($l,$vars)=@_;
2082 1497         2290 my $k;
2083 15     13   218 no strict;
  15         340  
  15         3328  
2084 1497         3857 $l=~/^/o;
2085 1497         4511 while ($l !~ /\G$/gsco) {
2086 2170 100 33     16277 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         326 $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         8811 $k.=$1;
2099             }
2100             }
2101 1497         7495 return $k;
2102             }
2103              
2104             # expand one or all parameters (according to return context)
2105             sub expand {
2106 20 50   20 0 94 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         70  
  15         2353  
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   1883 my ($name,$value,$op)=@_;
2132 15     13   86 no strict 'refs';
  15         345  
  15         2847  
2133 740 50       5366 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       2186 $op = '=' unless $op;
2141 740         1871 my $lex = lex_var($1);
2142 740 100       2978 if ($lex) {
2143 387 50       23436 eval '$$lex'.$op.'$value'; die $@ if $@;
  387         1755  
2144 387 50       1549 print STDERR "lexical \$$1=",${"XML::XSH2::Map::$1"},"\n" if $DEBUG;
  0         0  
2145             } else {
2146 353 50       31537 eval '${"XML::XSH2::Map::$1"}'.$op.'$value'; die $@ if $@;
  353         1991  
2147 353 50       2116 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   78 no strict 'refs';
  15         59  
  15         3107  
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   81 no strict qw(refs);
  15         398  
  15         13001  
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 1810 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 864 my ($exp,$op,$type,$name)=@_;
2202 254 100       1079 if ($type eq 'my') {
    100          
2203 1         5 store_lex_variables(0,$name);
2204             } elsif ($type eq 'local') {
2205 11         33 store_variables(0,$name);
2206             }
2207 254         759 my $val = _ev($exp);
2208 254         1240 _assign($name,$val,$op);
2209 254         1074 return 1;
2210             }
2211              
2212             sub command_assign {
2213 48     48 0 205 my ($command,$op,$type,$name)=@_;
2214 48 50       276 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         412 $op =~ s/\s*:\s*//;
2220 48         247 _assign($name,run_commands([$command],0,1),$op);
2221 48         532 return 1;
2222             }
2223              
2224             sub make_local {
2225 1     1 0 5 foreach (@_) {
2226 1         5 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 2019 my ($new,@vars)=@_;
2236 1051         1481 my $pool;
2237 1051 100 33     2042 if ($new) {
    50          
2238 1040         1852 $pool=[];
2239             } elsif (@stored_variables and ref($stored_variables[$#stored_variables])) {
2240 11         28 $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         2055 foreach (@vars) {
2247 18         76 my $value=var_value($_);
2248 18         60 push @$pool, $_ => $value;
2249             }
2250 1051 100       2881 push @stored_variables, $pool if ($new);
2251              
2252 1051         1693 return 1;
2253             }
2254              
2255             sub store_lex_variables {
2256 1288     1288 0 1977 my $new = shift;
2257 1288         1769 my $pool;
2258 1288 100       2351 if ($new) {
    50          
2259 1287         2086 $pool={};
2260 1287         2544 push @$lexical_variables, $pool;
2261             } elsif (@$lexical_variables) {
2262 1         4 $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         2796 foreach (@_) {
2268 387 50       1942 if (/^\s*\$?([^\$]*)/) {
2269 387         1364 $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 4926 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 26 my ($var,$local) = @_;
2284 7 50       45 if ($local =~ /local/) {
    0          
2285 7         26 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 40 if ($_[0] =~ /local/) {
    0          
2293 7         23 restore_variables();
2294             } elsif ($_[0]=~/my/) {
2295 0         0 restore_lex_variables();
2296             }
2297             }
2298              
2299             sub restore_variables {
2300 1040     1040 0 1919 my $pool=pop @stored_variables;
2301 1040 50       2468 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         2617 while (@$pool) {
2306 18         59 my ($value,$name)=(pop(@$pool), pop(@$pool));
2307 18 50       119 if ($name =~ m/^\$/) {
2308 18 50       53 if (defined($value)) {
2309 18         55 _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         1761 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   276 if ($_want_returns) {
2346 16         56 return XML::LibXML::NodeList->new();
2347             } else {
2348 104         198 return undef;
2349             }
2350             }
2351              
2352              
2353             sub count_xpath {
2354 52     52 0 121 my ($exp)=@_;
2355 52         160 my $result = _ev($exp);
2356 52 100       234 if (ref($result)) {
2357 36 100 66     493 if (UNIVERSAL::isa($result,'XML::LibXML::NodeList')) {
    50          
    50          
2358 4         21 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         151 return $result->value();
2364             }
2365             } else {
2366 16         62 return $result;
2367             }
2368             }
2369              
2370             sub new_doc {
2371 31     31 0 106 my ($opts,$root_element)=@_;
2372 31         122 $opts = _ev_opts($opts);
2373 31         145 $root_element = _ev_string($root_element);
2374 31   33     236 my $format= $opts->{format} || $DEFAULT_FORMAT;
2375 31         144 create_doc(undef, $root_element, $format);
2376             }
2377              
2378             # create new document
2379             sub create_doc {
2380 53     53 0 1269 my ($id, $root_element, $format, $filename)=@_;
2381             # TODO: $format argument is not used by the grammar
2382 53         86 my $doc;
2383 53 100       430 $root_element="<$root_element/>" unless ($root_element=~/^\s*
2384 53         229 $root_element=~s/^\s+//;
2385 53         186 $doc=xsh_parse_string($root_element,$format);
2386 53 100       12815 set_doc($id,$doc,$filename) if defined($id);
2387 53         96 $_newdoc++;
2388              
2389 53 50       303 _set_context([$doc]) if $SWITCH_TO_NEW_DOCUMENTS;
2390 53         221 return $doc;
2391             }
2392              
2393             # bind a document with a given id and filename
2394             sub set_doc {
2395 22     22 0 65 my ($id,$doc,$file)=@_;
2396             # $_doc{$id}=$doc;
2397             # $_files{$id}=$file;
2398 22         80 _assign($id,$doc);
2399 22         92 set_doc_URI($doc,$file);
2400 22         40 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 53 my ($doc,$file)=@_;
2412 22 50 66     383 $doc->setBaseURI($file)
      66        
2413             if (defined($file) and ref($doc) and
2414             UNIVERSAL::can($doc,'setBaseURI'));
2415 22         108 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   94 no strict 'refs';
  15         197  
  15         29268  
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 4 my ($opts,$src)=@_;
2464 1         4 $opts = _ev_opts($opts);
2465              
2466 1 50       15 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     9 my $format= $opts->{format} || $DEFAULT_FORMAT;
2472 1 50       7 if ($format !~ /^xml$|^html$/) {
2473 0         0 die "Unknown --format for command open: '$format'! Use 'xml' or 'html'.\n";
2474             }
2475              
2476 1         5 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     22 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       3 local $SWITCH_TO_NEW_DOCUMENTS = 0 if $opts->{'no-switch-to'};
2483 1 50       4 local $VALIDATION = 1 if $opts->{validate};
2484 1 50       3 local $VALIDATION = 0 if $opts->{'no-validate'};
2485 1 50       4 local $RECOVERING = 1 if $opts->{recover};
2486 1 50       3 local $RECOVERING = 0 if $opts->{'no-recover'};
2487 1 50       4 local $PARSER_EXPANDS_ENTITIES = 1 if $opts->{'expand-entities'};
2488 1 50       3 local $PARSER_EXPANDS_ENTITIES = 0 if $opts->{'no-expand-entities'};
2489 1 50       3 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       3 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       12 local $LOAD_EXT_DTD = 0 if $opts->{'no-load-ext-dtd'};
2495 1 50       18 local $PARSER_COMPLETES_ATTRIBUTES = 1 if $opts->{'complete-attributes'};
2496 1 50       4 local $PARSER_COMPLETES_ATTRIBUTES = 0 if $opts->{'no-complete-attributes'};
2497 1 50       4 local $PARSER_EXPANDS_XINCLUDE = 1 if $opts->{'xinclude'};
2498 1 50       9 local $PARSER_EXPANDS_XINCLUDE = 0 if $opts->{'no-xinclude'};
2499              
2500 1         9 my ($source) = grep exists($opts->{$_}),qw(file pipe string);
2501 1         2 my $file;
2502 1 50       4 unless ($source eq 'string') {
2503 1         6 $file = _tilde_expand(_ev_string($src));
2504             # $file=~s{^(\~[^\/]*)}{(glob($1))[0]}eg;
2505 1 50 33     5 if ($source eq 'file' and !_is_absolute($file)) {
2506 0         0 $file = File::Spec->rel2abs($file);
2507             }
2508 1 50       5 print STDERR "open [$file]\n" if "$DEBUG";
2509 1 50       3 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       4 unless ("$QUIET") {
2524 1 50       3 if ($source eq 'string') {
2525 0         0 print STDERR "parsing string\n";
2526             } else {
2527 1         72 print STDERR "parsing $file\n";
2528             }
2529             }
2530 1         5 my $doc;
2531 1 50       17 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         3 eval {
2557 1 50       4 if ($format eq 'xml') {
    0          
    0          
2558 1         14 $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         483 _check_err($@,1,1);
2566 1 50       4 die "Failed to parse $file as $format\n" unless (ref($doc));
2567             }
2568 1 50       42 print STDERR "done.\n" unless "$QUIET";
2569 1 50       13 _set_context([$doc]) if $SWITCH_TO_NEW_DOCUMENTS;
2570 1         6 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   15 my ($doc,$version,$enc) = @_;
2680 4 50 33     12 $version=($doc->can('getVersion') ? $doc->getVersion() : '1.0')
    50          
2681             if ($doc and !defined $version);
2682 4 50 33     140 $enc=($doc->can('getEncoding') ? $doc->getEncoding() : undef)
    50          
2683             if ($doc and !defined $enc);
2684 4 50       130 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   130 no strict qw(refs);
  15         395  
  15         20862  
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 53 my ($element,$fold_attrs)=@_;
2892             return "<".$element->nodeName().
2893 0         0 ($fold_attrs ? ((grep { $_->nodeName() ne "xsh:fold" }
2894             $element->attributes()) ? " ..." : "") :
2895 18 0       199 join("",map { " ".$_->nodeName()."=\"".$_->nodeValue()."\"" }
  19 50       357  
    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 108 my ($element)=@_;
2910 13 50       156 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 217 my ($node,$depth,$folding,$fold_attrs)=@_;
2916 64         95 my $result;
2917 64 50       527 if ($node) {
2918 64 50 66     969 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     553 if ($depth<0 and $folding==0) {
    100 66        
    50 100        
    100 66        
      66        
      66        
2928 31 50       381 $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       72 if (!ref($node)) {
    100          
    50          
2936 0         0 $result=$node;
2937             } elsif ($_xml_module->is_element($node)) {
2938             $result= start_tag($node).
2939 11         48 join("",map { to_string($_,$depth-1,$folding,$fold_attrs) } $node->childNodes).
  13         212  
2940             end_tag($node);
2941             } elsif ($_xml_module->is_document($node)) {
2942 4 50 33     105 if ($node->can('getVersion') and $node->can('getEncoding')) {
2943 4         45 $result=_xml_decl($node,undef,undef)."\n";
2944             }
2945             $result.=
2946 5         32 join("\n",map { to_string($_,$depth-1,$folding,$fold_attrs) }
2947 4 50       38 grep { $SKIP_DTD ? !$_xml_module->is_dtd($_) : 1 } $node->childNodes);
  5         153  
2948             } else {
2949 0         0 $result=$_xml_module->toStringUTF8($node,$INDENT);
2950             }
2951             } else {
2952 11 50       79 $result = ref($node) ? $_xml_module->toStringUTF8($node,$INDENT) : $node;
2953             }
2954             }
2955 64         1312 return $result;
2956             }
2957              
2958             # list nodes matching given XPath argument to a given depth
2959             sub list {
2960 33     33 0 206 my ($opts,$exp)=@_;
2961 33         334 my $opts = _ev_opts($opts);
2962 33 100       393 $opts->{depth} = ($exp eq '' ? 1 : -1) unless exists($opts->{depth});
    100          
2963 33 100       176 $exp = '.' if $exp eq '';
2964 33 0 33     147 if ($opts->{noindent} and $opts->{indent}) {
2965 0         0 die "Can't use --indent and --no-indent together\n";
2966             }
2967 33 50       134 local $INDENT=1 if $opts->{indent};
2968 33 50       111 local $INDENT=0 if $opts->{'no-indent'};
2969 33         253 my $ql=_ev_nodelist($exp);
2970 33         305 foreach my $node (@$ql) {
2971 46 50       203 print STDERR "checking for folding\n" if "$DEBUG";
2972             my $fold=$opts->{fold} &&
2973 46   0     138 ($_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       116 print STDERR "folding: $fold\n" if "$DEBUG";
2976 46         328 out (to_string($node,$opts->{depth},$fold,$opts->{'fold-attrs'}),"\n");
2977             }
2978 33 50       195 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 22 my ($opts,$exp)=@_;
3060 1         32 $opts = _ev_opts($opts);
3061 1         29 my $ql= _ev_nodelist($exp);
3062 1         18 foreach (@$ql) {
3063 9         62 out(pwd($_,$opts->{id}),"\n");
3064             }
3065 1 50       25 print STDERR "\nFound ",scalar(@$ql)," node(s).\n" unless "$QUIET";
3066 1         16 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 29 my ($opts,$exp)=@_;
3084 5         22 my $i=0;
3085 5         41 my $ql=_ev_nodelist($exp);
3086 5         51 foreach my $node (@$ql) {
3087 8         43 remove_node($node,get_keep_blanks());
3088 8         21 $i++;
3089             }
3090 5 50       36 print STDERR "removed $i node(s)\n" unless "$QUIET";
3091 5         59 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 35 my ($opts,$exp)=@_;
3107 11         50 my $opts = _hash_opts($opts);
3108 11         42 my $list = _ev_nodelist($exp);
3109 11         67 my @list;
3110 11         47 my $old_context = _save_context();
3111 11         27 my $pos=1;
3112 11         34 my $rl = _prepare_result_nl();
3113 11 100       85 if ($opts->{compare}) {
3114 5         16 foreach (qw(numeric descending)) {
3115 10 50       32 die "sort cannot use --$_ with --compare at the same time\n" if (exists($opts->{$_}));
3116             }
3117             }
3118 11         24 eval {
3119 11 100       33 if (defined($opts->{key})) {
3120 9         28 foreach my $node (@$list) {
3121 36         130 _set_context([$node,0+@$list,$pos]);
3122 36         138 push @list,[$node, _ev_literal($opts->{key})];
3123 36         98 $pos++;
3124             }
3125             } else {
3126 2         4 @list = map { [$_,to_literal($_)] } @$list;
  8         20  
3127             }
3128 11 100       52 if ($opts->{numeric}) {
    100          
3129 1 50       5 if ($opts->{descending}) {
3130 0         0 @$rl = map { $_->[0] } sort { $b->[1] <=> $a->[1] } @list;
  0         0  
  0         0  
3131             } else {
3132 1         7 @$rl = map { $_->[0] } sort { $a->[1] <=> $b->[1] } @list;
  4         8  
  4         16  
3133             }
3134             } elsif ($opts->{compare}) {
3135 20         46 @$rl = map { $_->[0] }
3136             sort {
3137 5         38 local $XML::XSH2::Map::a = $a->[1];
  23         68  
3138 23         45 local $XML::XSH2::Map::b = $b->[1];
3139 23         74 my $result=eval lexicalize($opts->{compare});
3140 23 50       77 die $@ if ($@); # propagate
3141             $result;
3142             } @list;
3143             } else {
3144 5 100       15 if ($opts->{descending}) {
3145 1 50       4 if ($opts->{locale}) {
3146 15     13   5134 use locale;
  15         4866  
  15         111  
3147 0         0 @$rl = map { $_->[0] } sort { $b->[1] cmp $a->[1] } @list;
  0         0  
  0         0  
3148             } else {
3149 1         9 @$rl = map { $_->[0] } sort { $b->[1] cmp $a->[1] } @list;
  4         9  
  5         12  
3150             }
3151             } else {
3152 4 50       14 if ($opts->{locale}) {
3153 15     13   825 use locale;
  15         366  
  15         91  
3154 0         0 @$rl = map { $_->[0] } sort { $a->[1] cmp $b->[1] } @list;
  0         0  
  0         0  
3155             } else {
3156 4         26 @$rl = map { $_->[0] } sort { $a->[1] cmp $b->[1] } @list;
  16         32  
  19         45  
3157             }
3158             }
3159             }
3160             };
3161 11         28 my $err = $@;
3162 11         20 do {
3163 11         196 local $SIG{INT}=\&flagsigint;
3164 11         56 _set_context($old_context);
3165 11         38 propagate_flagsigint();
3166             };
3167 11 50       43 die $err if $err; # propagate
3168              
3169 11         87 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 8 my ($opts, $mapexp, $exp)=@_;
3178 2         11 $opts = _ev_opts($opts);
3179 2         9 my $ql=_ev_nodelist($exp);
3180 2         39 my $old_context = _save_context();
3181 2         7 my $pos=1;
3182 2         14 my $size = @$ql;
3183 2         14 my $in_place = $opts->{'in-place'};
3184 2 50       11 @$ql = reverse @$ql if $opts->{reverse};
3185 2         5 eval {
3186 2         10 foreach my $node (@$ql) {
3187 11         50 _set_context([$node,$size,$pos++]);
3188 11 100 33     52 if ($_xml_module->is_attribute($node)) {
    50          
    50          
3189 1         9 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       100 $node->setData($val) if defined $val;
3223             }
3224             }
3225             };
3226 2         8 my $err = $@;
3227             {
3228 2         2 local $SIG{INT}=\&flagsigint;
  2         39  
3229 2         10 _set_context($old_context);
3230 2         10 propagate_flagsigint();
3231             }
3232 2 50       12 die $err if $err; # propagate
3233              
3234 2         63 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   291 my ($val)=@_;
3271 87 50       417 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         208 return $val;
3277             }
3278             }
3279              
3280             sub perlrename {
3281 4     4 0 16 my ($opts, $nameexp, $exp)=@_;
3282 4         20 $opts = _ev_opts($opts);
3283 4         25 my $ns = _ev_namespace($opts->{namespace});
3284 4         20 my $ql=_ev_nodelist($exp);
3285 4         42 my $old_context = _save_context();
3286 4         12 my $pos=1;
3287 4         12 my $size = @$ql;
3288 4 50       17 @$ql = reverse @$ql if $opts->{reverse};
3289 4         10 my $in_place = $opts->{'in-place'};
3290 4         6 eval {
3291 4         12 foreach my $node (@$ql) {
3292 14         56 _set_context([$node,$size,$pos++]);
3293 14 50 100     61 if ($_xml_module->is_attribute($node) ||
      66        
3294             $_xml_module->is_element($node) ||
3295             $_xml_module->is_pi($node)) {
3296 14 50       87 if ($node->can('setName')) {
3297 14         77 my $name=$node->getName();
3298 14         34 my $old_name = $name;
3299 14         56 $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     55 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         71 $node->setName($name);
3311 14 50 33     55 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         28 my $err = $@;
3323             {
3324 4         7 local $SIG{INT}=\&flagsigint;
  4         81  
3325 4         22 _set_context($old_context);
3326 4         15 propagate_flagsigint();
3327             }
3328 4 50       16 die $err if $err; # propagate
3329              
3330 4         82 return 1;
3331             }
3332              
3333              
3334             ############### AUXILIARY FUNCTIONS ###############
3335              
3336             sub set_attr_ns {
3337 29     29 0 96 my ($node,$ns,$name,$value)=@_;
3338 29 50       66 if ($ns eq "") {
3339 29         79 $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 185 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 208 my ($node,$ns,$dest_doc,$dest)=@_;
3355              
3356 97         150 my $copy;
3357 97 100 100     222 if ($_xml_module->is_element($node) and !$node->hasChildNodes) {
    50          
3358             # -- prepare NS
3359 25 50       143 $ns=$node->namespaceURI() if ($ns eq "");
3360 25         444 my $prefix = name_prefix($node->getName);
3361 25 50 33     129 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       167 [map { [$_->nodeName(),$_->nodeValue(),
  20         325  
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         254 $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 9 my ($node)=@_;
3385 4         7 my @siblings;
3386 4         27 $node=$node->nextSibling();
3387 4         63 while ($node) {
3388 2         12 push @siblings,$node;
3389 2         11 $node=$node->nextSibling();
3390             }
3391 4         13 return @siblings;
3392             }
3393              
3394             # create new document element before the given nodelist
3395             sub new_document_element {
3396 7     7 0 20 my ($doc,$node,@nodelist)=@_;
3397 7         42 $doc->setDocumentElement($node);
3398 7         103 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         12 my $doc=$_xml_module->owner_document($old);
3408 3         28 my @after_nodes = $old->findnodes('following::node()');
3409 3         193 $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 215 my ($source,$dest,$where) = @_;
3421 86         278 my $parent=$dest->parentNode();
3422 86 50       240 return unless $parent;
3423 86 100       538 if ($_xml_module->is_document($parent)) {
3424              
3425             # placing a node on the document-level
3426             # SOURCE: Element
3427 7 100 66     23 if ($_xml_module->is_element($source)) {
    50 33        
      33        
3428 4 50       49 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       24 if ($parent->getDocumentElement()) {
3449 2 50       12 if ($_xml_module->is_element($dest)) {
3450 2         7 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         8 new_document_element($parent,$source,
3459             $dest,get_following_siblings($dest));
3460             }
3461 4         13 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       33 if ($where eq 'after') {
    100          
    50          
3470 1         12 $parent->insertAfter($source,$dest);
3471 1         9 return 'keep';
3472             } elsif ($where eq 'before') {
3473 1         14 $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         11 $parent->insertBefore($source,$dest);
3478 1         19 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       227 if ($where eq 'after') {
    50          
    50          
3485 51         280 $parent->insertAfter($source,$dest);
3486 51         161 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         144 $parent->insertBefore($source,$dest);
3492 28         62 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 21 my ($opts,$exp,$value)=@_;
3582 8         1484 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         23 $value = _ev($value);
3588 8         24 $exp = _expand($exp);
3589 8 50 66     42 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         25 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 365 my ($node,$dest,$dest_doc,$where,$ns,$rl)=@_;
3617 134 50       451 if ($_xml_module->is_document($node)) {
3618 0         0 die "Error: Can't insert/copy/move document nodes!\n";
3619             }
3620 134 50       327 if (!defined($dest_doc)) {
3621 134         350 $dest_doc = $_xml_module->owner_document($dest);
3622             }
3623             # destination: Attribute
3624 134 100 33     397 if ($_xml_module->is_attribute($dest)) {
    50 33        
    100 0        
    50 0        
3625             # source: Text, CDATA, Comment, Entity, Element
3626 26 100 66     60 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       55 my $val = $_xml_module->is_element($node) ?
3632             $node->textContent() : $node->getData();
3633 23 50 33     189 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         67 $val=~s/\s+$//g;
3646 23         211 set_attr_ns($dest->ownerElement(),$dest->namespaceURI(),$dest->getName(),
3647             $dest->getValue().$val);
3648 23 50       233 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         25 my $name=$node->getName();
3655 3         22 my $value = $node->getValue();
3656 3 50 33     16 if ($where eq 'replace' or $where eq 'after' or $where eq 'before') {
      33        
3657             # -- prepare NS
3658 3 50       22 $ns=$node->namespaceURI() if ($ns eq "");
3659 3 50 33     23 if ($ns eq "" and name_prefix($name) ne "") {
3660 0         0 $ns=$dest->lookupNamespaceURI(name_prefix($name))
3661             }
3662             # --
3663 3         13 my $elem=$dest->ownerElement();
3664 3         14 set_attr_ns($elem,"$ns",$name,$value);
3665 3 50       39 push @$rl,$elem->getAttributeNodeNS("$ns",$name) if defined($rl);
3666 3 50 33     11 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         10 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       383 if ($_xml_module->is_attribute($node)) {
3744             # -- prepare NS
3745 2 50       20 $ns=$node->namespaceURI() if ($ns eq "");
3746 2 50 33     31 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     14 if ($where eq 'into' or $where eq 'append' or $where eq 'prepend') {
    0 33        
3751 2         37 set_attr_ns($dest,"$ns",$node->getName(),$node->getValue());
3752 2 50       38 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         321 my $copy=node_copy($node,$ns,$dest_doc,$dest);
3786 90 100 100     574 if ($where eq 'after' or $where eq 'before' or $where eq 'replace') {
    50 100        
    0 33        
3787 47 50       123 push @$rl,_expand_fragment($copy) if defined($rl);
3788 47         123 return safe_insert($copy,$dest,$where);
3789             } elsif ($where eq 'into' or $where eq 'append') {
3790 43         308 $dest->appendChild($copy);
3791 43 50       187 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     126 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     73 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       23 my $value=$_xml_module->is_element($node) ?
3824             $node->textContent() : $node->getData();
3825 9         74 $dest->setData($dest->getData().$value);
3826 9 50       31 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         22 my $parent=$dest->parentNode();
3849 7         12 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       16 push @$rl,_expand_fragment($new) if defined $rl;
3871 7         14 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         1392 return 1;
3879             }
3880              
3881             # parse a string and create attribute nodes
3882             sub create_attributes {
3883 18     18 0 39 my ($str)=@_;
3884 18         47 my (@ret,$value,$name);
3885 18         76 while ($str!~/\G$/gsco) {
3886 27 50       148 if ($str=~/\G\s*([^ \n\r\t=]+)=/gsco) {
    0          
3887 27         63 my $name=$1;
3888 27 50       67 print STDERR "attribute_name=$1\n" if $DEBUG;
3889 27 50 100     257 if ($str=~/\G\"((?:[^\\\"]|\\.)*)\"/gsco or
    0 66        
3890             $str=~/\G\'((?:[^\\\']|\\.)*)\'/gsco or
3891             $str=~/\G(.*?)(?=\s+[^ \n\r\t=]+=|\s*$)/gsco) {
3892 27         54 $value=$1;
3893 27         47 $value=~s/\\(.)/$1/g;
3894 27 50       61 print STDERR "creating $name='$value' attribute\n" if $DEBUG;
3895 27         154 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         61 return @ret;
3911             }
3912              
3913             sub new_element {
3914 25     25 0 197 my ($doc,$name,$ns,$attrs,$dest)=@_;
3915 25         41 my $el;
3916 25         80 my ($prefix,$localname) = $name=~/^([^:>]+):(.*)$/;
3917 25 50 33     86 if ($prefix ne "" and $ns eq "") {
3918 0         0 die "Error: namespace error: undefined namespace prefix `$prefix'\n";
3919             }
3920 25 50 33     75 if ($dest && $_xml_module->is_element($dest)) {
    0          
3921 25 50       66 print STDERR "DEST is element\n" if $DEBUG;
3922 25         182 $el=$dest->addNewChild($ns,$name);
3923            
3924 25 50 33     269 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       64 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         133 $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       105 if (ref($attrs)) {
3940 25         67 foreach (@$attrs) {
3941 20 50 33     259 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     75 next if ($_->[0] eq "xmlns:$prefix" and $_->[1] eq $ns);
3966 20         55 $el->setAttribute($_->[0],$_->[1]); # what about other namespaces?
3967             }
3968             }
3969             }
3970 25         212 return $el;
3971             }
3972              
3973             # create nodes from their textual representation
3974             sub create_nodes {
3975 106     106 0 308 my ($type,$str,$doc,$ns)=@_;
3976 106         180 my @nodes=();
3977 106 50       283 die "No document for create $type $str for.\n" unless ref($doc);
3978 106 50 33     326 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       238 if ($type eq 'chunk') {
3982 9         62 @nodes=map {$_->childNodes()}
3983 9         54 grep {ref($_)} ($_parser->parse_xml_chunk($str));
  9         1651  
3984             } else {
3985 97 100       345 if ($type eq 'attribute') {
    100          
    100          
    50          
    50          
    100          
    50          
3986 5         19 foreach (create_attributes($str)) {
3987 5         12 my $at;
3988 5 50 33     27 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         74 $at=$doc->createAttribute($_->[0],$_->[1]);
3994             }
3995 5         17 push @nodes,$at;
3996             }
3997             } elsif ($type eq 'element') {
3998 57         109 my ($name,$attributes);
3999 57 50       379 if ($str=~/^\]+)(\s+.*)?(?:\/?\>)?\s*$/) {
4000 57 50       159 print STDERR "element_name=$1\n" if $DEBUG;
4001 57 50       126 print STDERR "attributes=$2\n" if $DEBUG;
4002 57         247 my ($elt,$att)=($1,$2);
4003 57         94 my $el;
4004 57 100 66     242 if ($elt=~/^([^:>]+):(.*)$/ or $ns ne "") {
4005 3 50       13 print STDERR "Name: $elt\n" if $DEBUG;
4006 3 50       9 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       10 print STDERR "NS: $ns\n" if $DEBUG;
4011             }
4012 3 50 33     18 die "Error: undefined namespace prefix `$1'\n" if ($1 ne "" and $ns eq "");
4013 3         31 $el=$doc->createElementNS($ns,$elt);
4014             } else {
4015 54         357 $el=$doc->createElement($elt);
4016             }
4017 57 100       168 if ($att ne "") {
4018 13         103 $att=~s/\/?\>?$//;
4019 13         51 foreach (create_attributes($att)) {
4020 22 50       229 print STDERR "atribute: ",$_->[0],"=",$_->[1],"\n" if $DEBUG;
4021 22 50 33     70 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         94 $el->setAttribute($_->[0],$_->[1]);
4027             }
4028             }
4029             }
4030 57         278 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         142 push @nodes,$doc->createTextNode($str);
4037 19 50       56 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         62 my ($name,$data)=($str=~/^\s*(?:\<\?)?(\S+)(?:\s+(.*?)(?:\?\>)?)?$/);
4046 8 50       39 $data = "" unless defined $data;
4047 8         58 my $pi = $doc->createPI($name,$data);
4048 8 50       24 print STDERR "pi=\n" if $DEBUG;
4049 8         18 push @nodes,$pi;
4050             # print STDERR "cannot add PI yet\n" if $DEBUG;
4051             } elsif ($type eq 'comment') {
4052 8         46 push @nodes,$doc->createComment($str);
4053 8 50       22 print STDERR "comment=$str\n" if $DEBUG;
4054             } else {
4055 0         0 die "unknown type: $type\n";
4056             }
4057             }
4058 106         388 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 94 my ($opts,$fexp,$where,$texp,$all_to_all)=@_;
4249 27         45 my $fl;
4250 27         77 $opts = _ev_opts($opts);
4251 27         64 $fl=_ev_nodelist($fexp);
4252 27 100       208 unless (@$fl) {
4253 1         7 _warn("No nodes matching $fexp");
4254 1         4 return 1;
4255             }
4256             # respective copying
4257 26         72 my $rl=_prepare_result_nl();
4258 26 100       85 if ($opts->{respective}) {
4259 2         4 my @rtl;
4260 2         5 my $old_context = _save_context();
4261 2         4 eval {
4262 2         5 my $pos=1;
4263 2         4 my $size = @$fl;
4264 2         5 foreach my $fp (@$fl) {
4265 10         29 _set_context([$fp,$size,$pos]);
4266 10         23 my $tl=_ev_nodelist($texp);
4267 10 50       63 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       19 unless ($all_to_all) { @$tl = ($tl->[0]) }
  0         0  
4272 10         18 push @rtl, $tl;
4273 10         18 $pos++;
4274             }
4275             };
4276 2         4 my $err = $@;
4277 2         4 do {
4278 2         34 local $SIG{INT}=\&flagsigint;
4279 2         9 _set_context($old_context);
4280 2         5 propagate_flagsigint();
4281             };
4282 2 50       8 die $err if $err; # propagate
4283 2   66     16 my $reverse = $opts->{'preserve-order'} && $where=~/^(after|prepend)$/;
4284 2 100       8 foreach my $fp ($reverse ? reverse @$fl : @$fl) {
4285 10 100       85 my $tl = $reverse ? pop(@rtl) : shift(@rtl);
4286 10         20 foreach my $tp (@$tl) {
4287 10         15 my $replace=0;
4288 10   33     23 $replace = ((insert_node($fp,$tp,undef,$where,undef,$rl)
4289             eq 'remove') || $replace);
4290 10 50       307 if ($replace) {
4291 0         0 remove_node($tp);
4292             }
4293             }
4294             }
4295             } else {
4296             # non-respective copying
4297 24         89 my $tl=_ev_nodelist($texp);
4298 24 50       172 unless (@$tl) {
4299 0         0 _warn("No nodes matching $texp");
4300 0         0 return $rl;
4301             }
4302 24 100       66 if ($all_to_all) {
4303 13         21 my $real_fl;
4304 13 100 66     55 if ($opts->{'preserve-order'} && $where=~/^(after|prepend)$/) {
4305 2         7 $real_fl = [ reverse @$fl ];
4306             } else {
4307 11         20 $real_fl = $fl;
4308             }
4309 13         30 foreach my $tp (@$tl) {
4310 13         20 my $replace=0;
4311 13         28 foreach my $fp (@$real_fl) {
4312 41   33     473 $replace = ((insert_node($fp,$tp,undef,$where,undef,$rl)
4313             eq 'remove') || $replace);
4314             }
4315 13 50       286 if ($replace) {
4316 0         0 remove_node($tp);
4317             }
4318             }
4319             } else {
4320 11 50       48 _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     106 while (ref(my $fp=shift @$fl) and ref(my $tp=shift @$tl)) {
4324 11         56 my $replace=insert_node($fp,$tp,undef,$where,undef,$rl);
4325 11 100       59 if ($replace eq 'remove') {
4326 1         12 remove_node($tp);
4327             }
4328             }
4329             }
4330             }
4331 26         203 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 217 my ($opts,$type,$str,$where,$exp,$to_all)=@_;
4339 60         257 $opts = _ev_opts($opts);
4340 60         192 $str = _ev_string($str);
4341 60         328 my $ns = _ev_namespace($opts->{namespace});
4342 60         197 my $tl=_ev_nodelist($exp);
4343 60 50       426 unless (@$tl) {
4344 0         0 _warn("Expression '$exp' returns empty node-list");
4345 0         0 return 1;
4346             }
4347 60         149 my $rl = _prepare_result_nl();
4348 60         100 my @nodes;
4349 60         360 @nodes=grep {ref($_)} create_nodes($type,$str,$_xml_module->owner_document($tl->[0]),$ns);
  71         256  
4350 60 50       267 unless (@nodes) {
4351 0         0 _warn("Expression generates no nodes to insert");
4352 0         0 return $rl;
4353             }
4354 60 100       356 if ($to_all) {
    50          
4355 1         24 foreach my $tp (@$tl) {
4356 2         12 my $replace=0;
4357 2         13 foreach my $node (@nodes) {
4358 2   33     26 $replace = (insert_node($node,$tp,undef,$where,undef,$rl) eq 'remove') || $replace;
4359             }
4360 2 50       94 if ($replace) {
4361 0         0 remove_node($tp);
4362             }
4363             }
4364             } elsif ($tl->[0]) {
4365 59         399 foreach my $node (@nodes) {
4366 70 50       295 if (ref($tl->[0])) {
4367 70 50       260 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         618 return $rl;
4374             }
4375              
4376             # wrap nodes into a given element
4377             sub wrap {
4378 16     16 0 48 my ($opts,$str,$exp)=@_;
4379 16         60 $opts = _ev_opts($opts);
4380 16         86 my $ns = _ev_namespace($opts->{namespace});
4381 16         59 $str = _ev_string($str);
4382              
4383 16         38 my $rl=_prepare_result_nl();
4384 16         45 my $ql=_ev_nodelist($exp);
4385 16         77 my %moved;
4386 16         43 foreach my $node (@$ql) {
4387 47 100       376 next if $moved{$$node};
4388 33         133 my ($el) = create_nodes('element',$str,
4389             $_xml_module->owner_document($node),$ns);
4390 33 50       136 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       106 if ($_xml_module->is_attribute($node)) {
4404 1 50 33     8 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         23 $parent->insertBefore($el,$parent->firstChild());
4409 1         5 set_attr_ns($el,$node->namespaceURI(),
4410             $node->getName(),$node->getValue());
4411 1         19 $node->unbindNode();
4412             } else {
4413 32         116 my $parent = $node->parentNode();
4414 32         47 my $last = undef;
4415 32 50       89 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     225 if (defined $opts->{while} or defined $opts->{until}) {
4420 17         26 my $while = $opts->{while};
4421 17         25 my $until = $opts->{until};
4422 17         20 my $skip_comments = $opts->{'skip-comments'};
4423 17         29 my $skip_ws = $opts->{'skip-whitespace'};
4424 17         23 my $skip_pi = $opts->{'skip-pi'};
4425 17         54 my $next = $node->nextSibling;
4426             # evaluate $opts->{while} in the context of the following sibling
4427 17 50       33 if ($next) {
4428 17         83 my $old_context = _save_context();
4429 17         27 eval {
4430             # what should the size be? guess number of all following siblings
4431 17         23 my $pos=1;
4432 17         55 my $size = $node->findvalue('count(following-sibling::node())');
4433 17         382 while ($next) {
4434 38 100 100     332 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         101 _set_context([$next,$size,$pos]);
4438 31 100       102 if (defined $while) {
4439 23 100       50 last if !_ev_count($while);
4440             }
4441 19 100       222 if (defined $until) {
4442 8         12 my $res = _ev_count($until);
4443 8 100       56 last if $res;
4444             }
4445 18         35 $last = $next;
4446 18         66 $pos++;
4447             }
4448 25         109 $next = $next->nextSibling;
4449             }
4450             };
4451 17         78 my $err = $@;
4452 17         23 do {
4453 17         297 local $SIG{INT}=\&flagsigint;
4454 17         65 _set_context($old_context);
4455 17         30 propagate_flagsigint();
4456             };
4457             }
4458             }
4459 32         186 safe_insert($el,$node,'replace');
4460 32         582 $el->appendChild($node);
4461 32 100       89 if ($last) {
4462 9         127 my $next = $el->nextSibling;
4463 9         46 while ($next) {
4464 23         170 $next->unbindNode();
4465 23         58 $el->appendChild($next);
4466 23         49 $moved{$$next}=1;
4467 23 100       177 last if $next->isSameNode($last);
4468 14         45 $next = $el->nextSibling;
4469             }
4470 9 50       16 unless ($next) {
4471 0         0 _warn("wrap: something went wrong");
4472             }
4473             }
4474             }
4475             }
4476 33 100       564 push @$rl, $el if defined $rl;
4477             }
4478 16         211 return $rl;
4479             }
4480              
4481             # wrap span of nodes into a given element
4482             sub wrap_span {
4483 7     7 0 24 my ($opts,$str,$xp_start,$xp_end)=@_;
4484 7         28 $opts = _ev_opts($opts);
4485 7         21 $str = _ev_string($str);
4486 7         43 my $ns = _ev_namespace($opts->{namespace});
4487 7         43 my $rl=_prepare_result_nl();
4488 7         29 my $ql_start=_ev_nodelist($xp_start);
4489 7         41 my $ql_end=_ev_nodelist($xp_end);
4490 7 50       55 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         98 my $node = $ql_start->[$i];
4496 13         20 my $end_node = $ql_end->[$i];
4497 13 50 33     82 if (not($node->parentNode()) or not($end_node->parentNode())) {
4498 0         0 die "Error: cannot wrap document node\n";
4499             }
4500 13         178 foreach my $n ($node,$end_node) {
4501 26 50       230 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       76 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         43 my ($el) = create_nodes('element',$str,
4511             $_xml_module->owner_document($node),$ns);
4512 13         46 my $parent = $node->parentNode();
4513 13         42 my @span;
4514 13         23 my $n=$node;
4515 13         40 while ($n) {
4516 23         120 push @span,$n;
4517 23 100       91 last if ($n->isSameNode($end_node));
4518 10         6517 $n=$n->nextSibling();
4519             }
4520 13 50       23 die "Error: Node ".pwd($end_node).
4521             " isn't following sibling of ".pwd($node)."!\n" unless $n;
4522 13 100       71 if ($_xml_module->is_document($parent)) {
4523             # check that document element is within the span
4524 3         21 my $docel=$parent->getDocumentElement();
4525 3         9 my $found=0;
4526 3         8 foreach my $n (@span) {
4527 5 100       19 if ($n->isSameNode($docel)) {
4528 3         3 $found=1;
4529 3         6 last;
4530             }
4531             }
4532 3 50       7 die "Cannot wrap span: ".pwd($node).
4533             " .. ".pwd($end_node)." (document already has a root element)\n"
4534             unless $found;
4535 3         19 replace_document_element($docel,$el);
4536 3         11 foreach my $n (@span) {
4537 7         58 $n->unbindNode();
4538 7         31 $el->appendChild($n);
4539             }
4540             } else {
4541 10         55 $parent->insertBefore($el,$node);
4542 10         22 foreach my $n (@span) {
4543 16         172 $n->unbindNode();
4544 16         57 $el->appendChild($n);
4545             }
4546             }
4547 13 100       146 push @$rl, $el if defined $rl;
4548             }
4549 7         109 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 10 my ($doc)=@_;
4644 1         11 my $dtd;
4645 1         26 $dtd=$_xml_module->get_dtd($doc,$QUIET);
4646              
4647 1         48 return $dtd;
4648             }
4649              
4650             # check document validity
4651             sub validate_doc {
4652 2     2 0 15 my ($opts,$exp)=@_;
4653 2         36 my $doc = _ev_doc($exp);
4654 2         26 $opts = _ev_opts($opts);
4655 2 50       24 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       24 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     37 $opts->{dtd} = 1 unless $opts->{schema} or $opts->{relaxng};
4662 2 50 33     15 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       12 $opts->{file} = _tilde_expand($opts->{file}) if exists($opts->{file});
4666 2         3 my $ret = 0;
4667 2 50       24 if ($doc->can('is_valid')) {
4668 2 50 33     45 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       19 if ($opts->{yesno}) {
4739 1         71 $ret = $doc->is_valid();
4740 1 50       20 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         59 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 21 my $opts = shift;
4765 1         32 my $doc = _ev_doc($_[0]);
4766 1 50       15 if ($doc) {
4767 1         36 my $dtd=get_dtd($doc);
4768 1 50       58 if ($dtd) {
4769 1         31 out($_xml_module->toStringUTF8($dtd),"\n");
4770             }
4771             }
4772 1         15 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 12 my ($opts,$exp)=@_;
4854 1         13 my $doc = _ev_doc($exp);
4855 1 50       7 if ($doc) {
4856 1         31 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 84 my ($nodea,$nodeb)=@_;
4865 39         119 while ($nodeb) {
4866 73 100       601 if ($_xml_module->xml_equal($nodea,$nodeb)) {
4867 1         10 return 1;
4868             }
4869 72         154 $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 87 my ($node,$trim_space)=@_;
4878 39 100       98 if (is_ancestor_or_self($node,xsh_context_node())) {
4879 1         40 _set_context([tree_parent_node($node)]);
4880             }
4881 39         80 my $doc;
4882 39         114 $doc=$_xml_module->owner_document($node);
4883 39 100       97 if ($trim_space) {
4884 8         57 my $sibling=$node->nextSibling();
4885 8 50 66     55 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         145 $_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 41 my $exp=$_[1]; #source xpath
4900 10         50 my $sourcenodes=_ev_nodelist($exp);
4901 10         133 my $res=copy(@_);
4902 10         212 foreach my $node (@$sourcenodes) {
4903 30         80 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 4454 system($_[0]);
4911 1         184 return 1;
4912             }
4913              
4914             sub sh {
4915 1     1 0 4 my $opts = shift;
4916 1         4 my $cmd=join " ",map { _ev_string($_) } @_;
  2         7  
4917 1         7 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 205 my $opts = _ev_opts(shift);
4923 52         189 my $count=count_xpath(@_);
4924 52 50       821 out("$count\n") unless $opts->{quiet};
4925 52         595 return $count;
4926             }
4927              
4928             sub perl_eval_command {
4929 229     229 0 387 shift; # opts
4930 229         516 &perl_eval;
4931             }
4932              
4933             sub perl_eval {
4934 319     319 0 695 my ($exp,$map,$in_place)=@_;
4935 319         944 select $OUT;
4936 15     13   106414 use utf8;
  15         58  
  15         138  
4937 319 100       1037 if (wantarray) {
    100          
4938 6         20 my @result=eval(lexicalize($exp));
4939 6 50       25 die $@ if $@;
4940 6         28 return @result;
4941             } elsif (defined $map) {
4942 25 50       56 if (ref($map)) {
4943 0         0 $map = to_literal($map);
4944             }
4945 25 100       40 if ($in_place) {
4946 3         7 local $_ = $map;
4947 6     9   39 eval(lexicalize($exp));
  6     9   233  
  6         191  
  6         35  
  6         35  
  5         50  
  3         12  
4948 3 50       12 die $@ if $@;
4949 3         14 return $_;
4950             } else {
4951             # abraka dabra: some magic to make $_ read only
4952 22         1634 local *_ = eval "\\'$map'";
4953 5     9   26 my $result=eval(lexicalize($exp));
  5     9   136  
  5     9   164  
  5     9   27  
  5     8   25  
  5     8   53  
  5     8   23  
  5     8   155  
  5     7   182  
  5     7   22  
  5     6   18  
  5     6   37  
  4     5   13  
  4     5   250  
  4     4   65  
  4     4   13  
  4     4   18  
  4     4   34  
  4     4   12  
  4     4   191  
  4     4   69  
  4     4   12  
  4     4   37  
  4     4   32  
  4     4   15  
  4     4   177  
  4     4   66  
  4     4   15  
  4     4   26  
  4     4   40  
  4     4   13  
  4     4   168  
  4     4   70  
  4     4   13  
  4     4   22  
  4     4   40  
  4     4   13  
  4     4   160  
  4     4   69  
  4     4   14  
  4         28  
  4         46  
  4         15  
  4         179  
  4         70  
  4         14  
  4         20  
  4         34  
  4         13  
  4         192  
  4         81  
  4         15  
  4         19  
  4         42  
  4         13  
  4         162  
  4         73  
  4         13  
  4         32  
  4         36  
  4         16  
  4         192  
  4         164  
  4         17  
  4         22  
  4         35  
  4         14  
  4         160  
  4         65  
  4         14  
  4         22  
  4         37  
  4         13  
  4         190  
  4         74  
  4         15  
  4         25  
  4         37  
  4         14  
  4         197  
  4         54  
  4         35  
  4         25  
  4         44  
  4         14  
  4         229  
  4         93  
  4         14  
  4         28  
  2         18  
  2         10  
  2         68  
  2         50  
  2         17  
  2         7  
  2         14  
  2         9  
  2         44  
  2         48  
  2         8  
  2         11  
  2         11  
  2         10  
  2         39  
  2         64  
  2         8  
  2         7  
  2         14  
  2         8  
  2         46  
  2         48  
  2         8  
  2         9  
  2         14  
  2         8  
  2         68  
  2         44  
  2         12  
  2         7  
  2         13  
  22         94  
4954 22 50       79 die $@ if $@;
4955 22         74 return $result;
4956             }
4957             } else {
4958 10     14   80 my $result=eval(lexicalize($exp));
  10     14   214  
  10     2   447  
  10     2   57  
  10         28  
  10         91  
  288         956  
  2         19  
  2         4  
  2         112  
  2         13  
  2         4  
  2         12  
4959 288 100       1939 die $@ if $@;
4960 283         1208 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 3342 return 0 unless ref($_[0]) eq "ARRAY";
4986 1033         1547 my @cmds=@{$_[0]};
  1033         2786  
4987 1033         1702 my $top_level=$_[1];
4988 1033         1563 my $want_returns=$_[2];
4989 1033         1473 my $trapsignals=$top_level;
4990 1033         1836 my $result=undef;
4991              
4992 1033         2086 my ($cmd,@params);
4993              
4994             # make sure errors throw exceptions
4995 1033 100       2382 local $_die_on_err=1 unless ($top_level);
4996 1033 100       1899 local $_want_returns=1 if ($want_returns);
4997              
4998 1033         3322 store_variables(1);
4999 1033         2665 store_lex_variables(1);
5000 15     13   3593 no strict qw(refs);
  15         363  
  15         4530  
5001 1033         1927 eval {
5002 1033 100       9044 local $SIG{INT}=\&sigint if $trapsignals;
5003 1033 100       6964 local $SIG{PIPE}=\&sigpipe if $trapsignals;
5004 1033         2494 foreach my $run (@cmds) {
5005 1494 100 100     7888 if (ref($run) eq 'ARRAY' or ref($run) eq 'XML::XSH2::Command') {
5006 1482         6242 ($RT_LINE,$RT_COLUMN,$RT_OFFSET,$RT_SCRIPT,$cmd,@params)=@$run;
5007 1482 100       3342 if ($cmd eq "test-mode") { $TEST_MODE=1; $result=1; next; }
  1         3  
  1         2  
  1         3  
5008 1481 100       2802 if ($cmd eq "run-mode") { $TEST_MODE=0; $result=1; next; }
  1         4  
  1         7  
  1         20  
5009 1480 100       2724 next if $TEST_MODE;
5010 1479 50       7879 $result=$cmd->(@params) if defined($cmd);
5011             } else {
5012 12         246 $result=0;
5013             }
5014             }
5015             };
5016 1033         4760 my $err = $@;
5017 1033         1453 do {
5018 1033         16732 local $SIG{INT}=\&flagsigint;
5019 1033         4059 restore_lex_variables();
5020 1033         3360 restore_variables();
5021 1033         2130 propagate_flagsigint();
5022             };
5023 1033 50 66     5865 if (!$trapsignals and $err =~ /^SIGINT|^SIGPIPE/) {
5024 0         0 die $err
5025             } else {
5026 1033         2591 _check_err($err,1);
5027             }
5028 970         8119 return $result;
5029             }
5030              
5031             sub run_string {
5032 208 50   208 0 631 xsh_rd_parser_init() unless $_xsh;
5033 208         2100 my $pt = $_xsh->startrule($_[0]);
5034 208         966 post_process_parse_tree($pt);
5035 208         824 return run_commands($pt,0);
5036             }
5037              
5038             sub run_exp {
5039 25     25 0 75 my ($opts,$exp)=@_;
5040 25         99 local $SCRIPT="";
5041 25         90 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 68 return 1 if $TEST_MODE;
5047              
5048 20     0   374 local $SIG{PIPE}=sub { };
5049 20         105 my ($cmd,$pipe)=@_;
5050              
5051 20 50       73 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         78 $pipe = expand($pipe);
5058 20 50       68 if ($pipe eq '') {
5059 0         0 die "Error: empty redirection\n";
5060             }
5061 20         39 my $out=$OUT;
5062 20 50       58 print STDERR "openning pipe $pipe\n" if $DEBUG;
5063 20         38 my $pid;
5064 20         40 eval {
5065 15     13   5248 use IPC::Open2;
  15         28470  
  15         28417  
5066             {
5067 20         24 local *O = *$out;
  20         365  
5068 20         100 my $P;
5069 20   50     191 $pid = open2('>&O',$P,$pipe) || die "cannot open pipe $pipe\n";
5070 20         109158 $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         441 run_commands($cmd);
5081             }
5082             };
5083             # print STDERR "FILENO:",$OUT->fileno,"\n";
5084             # print STDERR `ls -l /proc/$$/fd/`;
5085 20         135 my $err=$@;
5086 20         74 do {
5087 20         293 local $SIG{INT}=\&flagsigint;
5088 20 50       429 if (UNIVERSAL::can($OUT,'flush')) {
5089 20         233 flush $OUT;
5090 20         170 flush $OUT;
5091             }
5092 20         705 close $OUT;
5093 20         5733 waitpid($pid,0);
5094 20         355 $OUT=$out;
5095 20 50       401 flush $OUT if UNIVERSAL::can($OUT,'flush');
5096 20         119 propagate_flagsigint();
5097             };
5098 20 50       132 die $err if $err; # propagate
5099 20         1595 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 14 my ($cmd,$name)=@_;
5105 5 50       16 return 0 unless (ref($cmd) eq 'ARRAY');
5106 5 50       15 if ($name ne '') {
5107 5         19 my $out=$OUT;
5108 5 50       15 print STDERR "Pipe to $name\n" if $DEBUG;
5109 5         1010 require IO::Scalar;
5110 5         4759 $OUT=new IO::Scalar;
5111 5         286 eval {
5112 5         24 run_commands($cmd);
5113             };
5114 5         21 my $err;
5115 5         11 do {
5116 5         64 local $SIG{INT}=\&flagsigint;
5117 5 50       25 _assign($name,${$OUT->sref}) unless $@;
  5         28  
5118 5         25 $OUT=$out;
5119 5         85 propagate_flagsigint();
5120             };
5121 5 50       22 die $err if $err; # propagate
5122             }
5123 5         20 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         18 my $result=1;
5131 7         13 my $res;
5132 7         28 while ($res=_ev_count($exp)) {
5133 29         200 eval {
5134 29   33     97 $result = run_commands($command) && $result;
5135             };
5136 29 100 66     191 if (ref($@) and UNIVERSAL::isa($@,'XML::XSH2::Internal::LoopTerminatingException')) {
    50          
5137 14 50 33     52 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       42 if ($@->label eq 'next') {
    100          
    50          
5142 6         20 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         195 return $result;
5155             }
5156              
5157             sub throw_exception {
5158 5     5 0 16 my $opts = shift;
5159 5         26 die _ev_literal($_[0])."\n";
5160             }
5161              
5162             sub try_catch {
5163 6     6 0 21 my ($try,$catch,$var)=@_;
5164 6         13 my $result;
5165 6         10 eval {
5166 6         16 local $TRAP_SIGPIPE=1;
5167 6         92 local $SIG{INT}=\&sigint;
5168 6         103 local $SIG{PIPE}=\&sigpipe;
5169             # local $_die_on_err=1; # make sure errors cause an exception
5170 6         34 $result = run_commands($try);
5171             };
5172 6 50 33     69 if (ref($@) and UNIVERSAL::isa($@,'XML::XSH2::Internal::UncatchableException')) {
    50          
5173 0         0 die $@; # propagate
5174             } elsif ($@) {
5175 6         17 my $err=$@;
5176 6 50       45 if ($err =~ /^SIGINT/) {
5177 0         0 die $err; # propagate sigint
5178             } else {
5179 6 50       28 chomp($err) unless ref($err);
5180 6 50 33     28 if (ref($var) and @{$var}>1) {
  6         45  
5181 6         36 create_block_var(@$var);
5182 6         26 _assign($var->[0],$err);
5183 6         11 eval {
5184 6         25 $result = run_commands($catch);
5185             };
5186 6         22 $err = $@;
5187 6         11 do {
5188 6         83 local $SIG{INT}=\&flagsigint;
5189 6         37 destroy_block_var($var->[1]);
5190 6         18 propagate_flagsigint();
5191             };
5192 6 50       33 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         26 return $result;
5200             }
5201              
5202             sub loop_next {
5203 12     12 0 21 my $opts = shift;
5204 12         36 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 22 my $opts = shift;
5212 12         35 die XML::XSH2::Internal::LoopTerminatingException->new('redo',_ev_literal(@_));
5213             }
5214             sub loop_last {
5215 4     4 0 9 my $opts = shift;
5216 4         19 die XML::XSH2::Internal::LoopTerminatingException->new('last',_ev_literal(@_));
5217             }
5218              
5219             sub _save_context {
5220 56 50   56   188 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   746 my ($node,$size,$pos)=@{$_[0]};
  300         735  
5228 300 50       1761 if ($node) {
5229 300         3288 $_xpc->setContextNode($node);
5230 300 50 66     2141 if (defined($size) and defined($pos) and $_xpc->can('setContextSize')) {
      66        
5231 218 50       548 die "invalid size $size\n" if ($size < -1);
5232 218         544 $_xpc->setContextSize($size);
5233 218 50 33     793 die "invalid position $pos (size is $size)\n" if ($pos < -1 or $pos>$size);
5234 218         555 $_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 62 my ($exp,$command,$v)=@_;
5245 14 100       54 my ($var,$local) = ref($v) ? @$v : ();
5246 14         57 my $old_context = _save_context();
5247 14 100       69 create_block_var($var,$local) if $var ne "";
5248 14         27 eval {
5249 14 100       79 my @ql = ($var ne "") ? _ev_list($exp) : _ev_nodelist($exp);
5250 14         33 my $pos=1;
5251 14         28 my $size = @ql;
5252 14         51 foreach my $node (@ql) {
5253 57 100       171 if ($var ne "") {
5254 1         5 _assign($var,$node);
5255             } else {
5256 56         227 _set_context([$node,$size,$pos]);
5257             }
5258 57         143 eval {
5259 57         160 run_commands($command);
5260             };
5261 57 100 66     356 if (ref($@) and UNIVERSAL::isa($@,'XML::XSH2::Internal::LoopTerminatingException')) {
    50          
5262 14 50 33     53 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       39 if ($@->label eq 'next') {
    100          
    50          
5267 6         12 $pos++;
5268 6         27 next;
5269             } elsif ($@->label eq 'last') {
5270 2         11 last;
5271             } elsif ($@->label eq 'redo') {
5272 6         13 redo;
5273             } else {
5274 0         0 die $@; # propagate
5275             }
5276             } elsif ($@) {
5277 0         0 die $@; # propagate
5278             }
5279 43         141 $pos++;
5280             }
5281             };
5282 14         356 my $err = $@;
5283 14         39 do {
5284 14         214 local $SIG{INT}=\&flagsigint;
5285 14         76 _set_context($old_context);
5286 14 100       57 destroy_block_var($local) if ($var ne "");
5287 14         40 propagate_flagsigint();
5288             };
5289 14 50       77 die $err if $err; # propagate
5290 14         295 return 1;
5291             }
5292              
5293             # run commands if given XPath holds
5294             sub if_statement {
5295 4     4 0 14 my @cases=@_;
5296 4         15 foreach (@cases) {
5297 5         24 my ($exp,$command)=@$_;
5298 5 100 100     48 if (!defined($exp) or _ev_count($exp)) {
5299 2         14 return run_commands($command);
5300             }
5301             }
5302 2         47 return 1;
5303             }
5304              
5305             # run commands unless given XPath holds
5306             sub unless_statement {
5307 255     255 0 726 my ($exp,$command,$else)=@_;
5308 255 100       910 unless (_ev_count($exp)) {
5309 25         310 return run_commands($command);
5310             } else {
5311 230 100       2833 return ref($else) ? run_commands($else->[1]) : 1;
5312             }
5313             }
5314              
5315             sub _clone_xmldoc {
5316 1     1   4 my ($doc)=@_;
5317 1 50       54 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         223 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 259 my ($opts,$exp,@args)=@_;
5464 86         281 my $name = _ev_string($exp);
5465 86         348 call($opts,1,$name, @args);
5466             }
5467              
5468             # call a named set of commands
5469             sub call {
5470 254     254 0 755 my ($opts,$eval_args, $name, @args)=@_;
5471 254         670 my $def = $_defs{$name};
5472 254 50       636 if (defined $def) {
5473 254         962 my @vars = @$def[2..$#$def];
5474 254 50       1035 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         538 my $result;
5480             my %vars;
5481 254         561 foreach (@args) {
5482 386 50       2029 $vars{ shift(@vars) }=$eval_args ? _ev($_) : $_;
5483             }
5484 254         2305 my $prev_lex_context = $lexical_variables;
5485 254         544 $lexical_variables = $def->[1];
5486 254         1080 store_lex_variables(1,keys(%vars));
5487 254         471 eval {
5488 254         664 foreach (keys(%vars)) {
5489 386         996 _assign($_,$vars{$_});
5490             }
5491 254         840 $result = run_commands($def->[0]);
5492             };
5493 254         634 my $err = $@;
5494 254         426 do {
5495 254         3321 local $SIG{INT}=\&flagsigint;
5496 254         984 restore_lex_variables();
5497 254         1540 $lexical_variables=$prev_lex_context;
5498 254         2186 propagate_flagsigint();
5499             };
5500 254 50 33     1133 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       743 die $err if $err; # propagate
5506 247         5424 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   120 no strict qw(refs);
  15         291  
  15         32088  
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         51 $_defs{$name} = [ $command, [ @$lexical_variables ], @$args ];
5533 10         122 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         6 foreach (sort keys (%_defs)) {
5545 1         2 out(join(" ",$_,@{ $_defs{$_} }[2..$#{ $_defs{$_} }] ),"\n" );
  1         7  
  1         4  
5546             }
5547 1         5 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 7680 my ($node,$command,$input)=@_;
5644 4         26 my $old_context = _save_context();
5645 4         13 eval {
5646 4         13 foreach (1) {
5647 4         21 _set_context([$node,1,1]);
5648 4         11 eval {
5649 4         31 run_commands($command);
5650             };
5651 4 50 33     25 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         8 my $err = $@;
5669 4         6 do {
5670 4         52 local $SIG{INT}=\&flagsigint;
5671 4         21 _set_context($old_context);
5672 4         14 propagate_flagsigint();
5673             };
5674 4 50       29 die $err if $err; # propagate
5675             }
5676              
5677             sub stream_process {
5678 2     2 0 10 my ($opts, $process)=@_;
5679 2         9 $opts = _ev_opts($opts);
5680              
5681 2         796 require XML::Filter::DOMFilter::LibXML;
5682 2         36204 require XML::LibXML::SAX;
5683 2         2414 require XML::SAX::Writer;
5684              
5685 2 50       7088 if (grep {/^input-/} keys %$opts>1) {
  2         13  
5686 0         0 die "Only one --input-xxxx parameter can be specified\n";
5687             }
5688 2 50       8 if (grep {/^output-/} grep { !/^output-encoding/ } keys %$opts>1) {
  2         10  
  2         7  
5689 0         0 die "Only one --output-xxxx parameter can be specified\n";
5690             }
5691 2 50 33     33 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         6 my $out;
5696             my $termout;
5697 2 50       10 $opts->{'input-file'} = _tilde_expand($opts->{'input-file'}) if exists($opts->{'input-file'});
5698 2 50       8 $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     32 $opts->{'output-file'} || undef;
5701             my $input = $opts->{'input-string'} || $opts->{'input-pipe'} ||
5702 2   0     15 $opts->{'input-file'} || '-';
5703              
5704 2 50       14 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       11 $_->[0] => [\&stream_process_node,$_->[1],
  2         32  
5745             $input] }
5746             @$process
5747             ]
5748             )
5749             );
5750 2         201 my $old_context = _save_context();
5751 2         5 my $error;
5752 2 50       4 eval {
5753 2 100       10 if (exists $opts->{'input-pipe'}) {
    50          
5754 1         4444 open my $F,"$input|";
5755 1 50       88 $F || die "Cannot open pipe to $input: $!\n";
5756 1         138 $parser->parse_file($F);
5757 1         365 close $F;
5758             } elsif (exists $opts->{'input-string'}) {
5759 1         7 $parser->parse_string($input);
5760             } else { #file
5761 0         0 $parser->parse_uri($input);
5762             }
5763 2 50       497 if (exists $opts->{'output-pipe'}) {
5764 0         0 close($out);
5765             }
5766 2 50       9 if ($termout) { out("\n"); }
  0         0  
5767 2         10 1 } or $error = $@;
5768 2         11 _set_context($old_context);
5769 2 50       13 die $error if $error;
5770              
5771 2         124 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         5 my $prefix = _ev_string($_[0]);
5877 1         3 my $ns = _ev_string($_[1]);
5878              
5879 1 50       8 unless ($prefix=~m{^[-_.[:alpha:]][-_.[:alnum:]]*$}) {
5880 0         0 die "Invalid namespace prefix '$prefix'\n";
5881             }
5882 1         5 $_ns{$prefix}=$ns;
5883 1         15 $_xpc->registerNs($prefix,$ns);
5884 1         3 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       2 return undef unless $node;
5965 1 50       15 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         6 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   3069 import XML::XSH2::Functions ':param_vars';
5998            
5999              
6000 15         85 *fromUTF8 = *XML::XSH2::Functions::fromUTF8;
6001 15         7861 *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   76 my $exp=$_[0];
6010 19         82 my $ql;
6011 19 100       64 if (ref($exp)) {
6012 14 50       55 if (UNIVERSAL::isa($exp,'XML::LibXML::NodeList')) {
    0          
6013 14         22 $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         20 $ql=&XML::XSH2::Functions::_ev_nodelist($exp);
6021             }
6022 19         61 my $result='';
6023 19         48 foreach (@$ql) {
6024 21         489 $result.=$_->toString();
6025             }
6026 19         310 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   3095 my ($p, $s, $l)=caller;
6076 183         699 local $XML::XSH2::Functions::SCRIPT="";
6077 183         722 XML::XSH2::Functions::run_string(join "",XML::XSH2::Functions::cast_objects_to_values(@_));
6078             }
6079              
6080             sub current {
6081 8     8   22 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   116 my $class=(ref($_[0]) || $_[0]);
6111 28         47 shift;
6112 28         189 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   486 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   101 use vars qw(@ISA);
  15         310  
  15         709  
6136             @ISA=qw(XML::XSH2::Internal::Exception);
6137              
6138             package XML::XSH2::Internal::LoopTerminatingException;
6139 15     14   74 use vars qw(@ISA);
  15         69  
  15         647  
6140             @ISA=qw(XML::XSH2::Internal::UncatchableException);
6141              
6142             package XML::XSH2::Internal::SubTerminatingException;
6143 15     15   76 use vars qw(@ISA);
  15         324  
  15         629  
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   67 use vars qw(@ISA);
  15         51  
  14         1084  
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   94 use strict;
  14         296  
  14         359  
6168 14     15   63 use warnings;
  14         181  
  13         395  
6169 13     15   5132 use Tie::Scalar;
  13         5035  
  13         301  
6170 13     15   65 use vars qw(@ISA);
  13         64  
  12         721  
6171             @ISA=qw(Tie::StdScalar);
6172              
6173             sub TIESCALAR {
6174 364     364   7151 return bless $_[1], $_[0];
6175             }
6176              
6177             1;
6178