#!/usr/bin/perl
# -*- cperl -*-
# $Id: xsh,v 1.32 2003/09/10 13:35:31 pajas Exp $
use FindBin;
use lib ("$FindBin::RealBin", "$FindBin::RealBin/../lib",
"$FindBin::Bin","$FindBin::Bin/../lib",
"$FindBin::Bin/lib", "$FindBin::RealBin/lib"
);
package main;
use strict;
#use Getopt::Std;
use Getopt::Long;
use Pod::Usage;
use vars qw($opt_q $opt_w $opt_i $help $opt_V $opt_E $opt_e $opt_d
$opt_c $opt_s $opt_f $opt_g $opt_v $opt_t $opt_T $opt_a
$opt_l $opt_n $opt_p $opt_P $usage $manpage $opt_HTML
$opt_XML $input $output $process $format);
use vars qw($VERSION $REVISION);
use IO::Handle;
use XML::XSH qw(&xsh_set_output &xsh_get_output &xsh &xsh_init
&xsh_pwd &xsh_local_id &set_quiet &set_debug
&set_compile_only_mode);
BEGIN {
my $optparser=new Getopt::Long::Parser(config => ["bundling"]);
$optparser->getoptions(
"arguments|a" => \$opt_a,
"stdin|t" => \$opt_t,
"compile|c" => \$opt_c,
"quiet|q" => \$opt_q,
"validation|v" => \$opt_v,
"format" => \$format,
"no-validation|w" => \$opt_w,
"debug|d" => \$opt_d,
"no-init|f" => \$opt_f,
"help|h" => \$help,
"version|V" => \$opt_V,
"interactive|i" => \$opt_i,
"input|I=s" => \$input,
"output|O=s" => \$output,
"process|P=s" => \$process,
"non-interactive|n" => \$opt_n,
"pipe|p" => \$opt_p,
"html|H" => \$opt_HTML,
"xml|X" => \$opt_XML,
"trace-grammar|T" => \$opt_T,
"query-encoding|E=s" => \$opt_E,
"encoding|e=s" => \$opt_e,
"load|l=s" => \$opt_l,
"usage|u" => \$usage,
"man" => \$manpage,
) or $usage=1;
# getopts('atscqvwdfhVinTE:e:l:pP');
$VERSION='0.12';
$REVISION='$Revision: 1.32 $';
$ENV{PERL_READLINE_NOWARN}=1;
}
if ($opt_P and ($input or $output)) {
print STDERR "Options -P is incompatible with -I and -O!\n";
$usage=1;
}
# Help and usage
if ($usage) {
pod2usage(-msg => 'xsh - XML Editing Shell');
# exit 0;
}
if ($help) {
pod2usage(-exitstatus => 0, -verbose => 1);
}
if ($manpage) {
pod2usage(-exitstatus => 0, -verbose => 2);
}
my $string;
if ($opt_a) {
@XML::XSH::Map::ARGV=@ARGV;
} else {
$string=join " ",@ARGV;
}
die "Incompatible options: --html, --xml\n" if $opt_XML+$opt_HTML > 1;
if ($opt_HTML) {
$XML::XSH::Functions::DEFAULT_FORMAT='html';
} elsif ($opt_XML) {
$XML::XSH::Functions::DEFAULT_FORMAT='xml';
}
if ($opt_p) {
$opt_n=1;
$opt_i=0;
$opt_t=0;
}
unless ($opt_n || $opt_i) {
$opt_i=((-t STDIN) && !$opt_l && !$string) ? 1 : 0;
}
require Term::ReadLine if $opt_i;
if ($opt_V) {
my $rev=$REVISION;
$rev=~s/\s*\$//g;
my $funcrev=$XML::XSH::Functions::REVISION;
$funcrev=~s/\s*\$//g;
print "xsh $VERSION ($rev)\n";
print "XML::XSH::Functions $XML::XSH::Functions::VERSION ($funcrev)\n";
exit 1;
}
$::RD_ERRORS = 1; # Make sure the parser dies when it encounters an error
$::RD_WARN = 1; # Enable warnings. This will warn on unused rules &c.
$::RD_HINT = 1; # Give out hints to help fix problems.
$::RD_TRACE = $opt_T; # Give out hints to help fix problems.
my $module;
#if ($opt_g) {
# $module="XML::XSH::GDOMECompat";
#} else {
$module="XML::XSH::LibXMLCompat";
#}
xsh_init($module);
set_quiet($opt_q);
set_debug($opt_d);
set_compile_only_mode($opt_c);
my $doc=XML::XSH::Functions::create_doc("scratch","scratch",'xml');
#XML::XSH::Functions::set_last_id("scratch");
XML::XSH::Functions::set_local_xpath(['scratch','/']);
if ($opt_w) {
XML::XSH::Functions::set_validation(0);
XML::XSH::Functions::set_load_ext_dtd(0);
XML::XSH::Functions::set_expand_entities(0);
XML::XSH::Functions::set_complete_attributes(0);
} elsif ($opt_v) {
XML::XSH::Functions::set_validation(1);
XML::XSH::Functions::set_load_ext_dtd(1);
XML::XSH::Functions::set_expand_entities(1);
XML::XSH::Functions::set_complete_attributes(1);
}
if ($format) {
XML::XSH::Functions::set_keep_blanks(0);
XML::XSH::Functions::set_indent(1);
print "FORMAT: $XML::XSH::Map::INDENT $XML::XSH::Map::KEEP_BLANKS\n";
}
my $l;
eval {
my $ini = "$ENV{HOME}/.xshrc";
if (-r $ini) {
open INI, $ini || die "cannot open $ini";
$XML::XSH::Functions::_includes{$ini}=1;
xsh(join("",<INI>));
close INI;
}
};
if ($@) {
print STDERR "Error occured while reading ~/.xshrc\n";
print STDERR "$@\n";
}
if ($opt_i) {
$XML::XSH::Functions::TRAP_SIGINT=1;
$XML::XSH::Functions::TRAP_SIGPIPE=1;
$XML::XSH::Functions::_die_on_err=0;
unless ($opt_q) {
my $rev=$REVISION;
$rev=~s/\s*\$//g;
$rev=" xsh - XML Editing Shell version $XML::XSH::Functions::VERSION/$VERSION ($rev)\n";
print STDERR "-"x length($rev),"\n";
print STDERR $rev;
print STDERR "-"x length($rev),"\n\n";
print STDERR "Copyright (c) 2002 Petr Pajas.\n";
print STDERR "This is free software, you may use it and distribute it under\n";
print STDERR "either the GNU GPL Version 2, or under the Perl Artistic License.\n";
}
} else {
$XML::XSH::Functions::_die_on_err=1;
}
if ($process ne "") {
$input=$process;
$output=$process;
}
if ($opt_p) {
$input ||= '-';
$output ||= '-';
}
foreach ($input, $output) {
s(\\)(\\\\)g;
s(')(\\')g;
}
print STDERR "open _='$input'\n" if $opt_d;
xsh("open _='$input'") if ($input);
if ($string) {
print "xsh> $string\n" if ($opt_i and not $opt_q);
xsh($string);
print "\n" if ($opt_i and not $opt_q);
}
if ($opt_l) {
my $load;
open($load,"$opt_l") || do { print STDERR "Error loading $opt_l: $!\n"; exit 1 };
xsh(join "",<$load>);
close $load;
}
my $term;
sub _term { $term };
if ($opt_i) {
$term = new Term::ReadLine('xsh');
$XML::XSH::Functions::_on_exit=
[sub {
my ($exit_code,$term)=@_;
if ($term->can('GetHistory')) {
eval {
print STDERR "saving $ENV{HOME}/.xsh_history\n";
open HIST,"> $ENV{HOME}/.xsh_history" || die "cannot open $ENV{HOME}/.xsh_history";
print HIST join("\n",$term->GetHistory());
close HIST;
print STDERR "done\n";
};
if ($@) {
print STDERR "Error occured while writing to ~/.xsh_history\n";
print STDERR "$@\n";
}
}
},$term
];
eval {
if (-r "$ENV{HOME}/.xsh_history") {
open HIST,"$ENV{HOME}/.xsh_history";
$term->addhistory(map { chomp; $_ } <HIST>);
close HIST;
}
};
if ($@) {
print STDERR "Error occured while reading from ~/.xsh_history\n";
print STDERR "$@\n";
}
# XML::XSH::Completion::cpl();
if ($term->ReadLine eq "Term::ReadLine::Gnu") {
$term->Attribs->{attempted_completion_function} = \&XML::XSH::Completion::gnu_complete;
$term->Attribs->{completion_entry_function} = sub { return () };
$term->Attribs->{completer_word_break_characters} = " =\t\n\r\"'`;|&})[{(]";
} else {
$readline::rl_completion_function = 'XML::XSH::Completion::perl_complete';
$readline::rl_completer_word_break_characters = " =\t\n\r\"'`;|&})[{(]";
}
xsh_set_output($term->OUT) if ($term->OUT);
unless ("$opt_q") {
print STDERR "Using terminal type: ",$term->ReadLine,"\n";
print STDERR "Hint: Type `help' or `help | less' to get more help.\n";
}
while (defined ($l=get_line($term,prompt(),''))) {
while ($l=~/\\+\s*$/) {
$l=~s/\\+\s*$//;
my $a=get_line($term,'> ',undef);
if (defined($a)) {
$l.=$a
} else {
$l='';
}
}
if ($l=~/\S/) {
xsh($l);
# $term->addhistory($l);
}
}
print STDERR "Good bye!\n" if $opt_i and not "$opt_q";
} elsif ((!$opt_p and $string eq "" and $opt_l eq "") or $opt_t) {
xsh(join "",<STDIN>);
}
xsh("save _ '$output'") if ($output);
# get a line of input from ReadLine
# if ^C interruption by user occures return $retonint
#
sub get_line {
my ($term,$prompt,$retonint)=@_;
my $line;
$SIG{INT}=sub { die 'TRAP-SIGINT'; };
eval {
$line = $term->readline($prompt);
};
if ($@) {
if ($@ =~ /TRAP-SIGINT/) {
print "\n" unless $term->ReadLine eq 'Term::ReadLine::Perl'; # clear screen
return $retonint;
} else {
die $@;
}
}
return $line;
}
sub prompt {
return 'xsh '.xsh_local_id().":".xsh_pwd().'> ';
}
__END__
=head1 xsh
xsh - XML Editing Shell
=head1 SYNOPSIS
xsh [options] commands
xsh [options] -al script [arguments ...]
xsh [options] -p commands < input.xml > output.xml
xsh [options] -I input.xml -O output.xml commands
xsh [options] -P file.xml commands
xsh -u for usage
xsh -h for help
xsh --man for the manual page
=head1 DESCRIPTION
XSH is an shell-like language for XPath-oriented editing, querying and
manipulation of XML and HTML files (with read-only support for DocBook
SGML). C<xsh> can work as an interactive shell (with full command-line
support such as history, TAB-completion, etc.) or as an off-line
interpreter for batch processing of XML files.
=head1 XSH COMMANDS
Please see L<http://xsh.sourceforge.net/doc/frames/index.html> or
L<XSH> for a complete XSH language reference.
For a quick help, type C<xsh help> (just C<help> on xsh prompt).
Type C<xsh help commands> to get list of available XSH commands and
C<xsh help B<command>> with B<command> replaced by a XSH command name
to get help on a particular command.
=head1 OPTIONS
=over 8
=item B<--load|-l> script-file
Load and execute given XSH script (the script is executed before all
other commands provided on the command-line, but after executed
~/.xshrc).
=item B<--arguments|-a>
Command-line contains arguments accessible to the script via
C<@XML::XSH::Map::ARGV> rather than XSH commands.
=item B<--stdin|-t>
Don't display command-prompt even if run from a terminal, expecting
XSH commands in the standard input.
=item B<--compile|-c>
Compile the XSH source and report errors, only. No commands are
actually executed.
=item B<--quiet|-q>
Quiet mode: suppress all unnecessary informatory ouptut.
=item B<--format>
Start with indent 1 (on) and keep_blanks 0 (off)
to allow nice indenting of the XML output.
=item B<--validation|-v>
Start with validation, load_ext_dtd, parser_expands_entities
and parser_completes_attributes 1 (on).
=item B<--no-validation|-w>
Start with validation, load_ext_dtd, parser_expands_entities
and parser_completes_attributes 0 (off).
=item B<--debug|-d>
Print some debug messages.
=item B<--no-init|-f>
Ignore ~/.xshrc
=item B<--version|-V>
Print XSH version info and exit.
=item B<--interactive|-i>
Start interactive mode with xsh command prompt. By default, the
interactive mode is only started if C<xsh> is running from a terminal
and neither XSH commands nor a script are given on the command-line.
=item B<--non-interactive|-n>
Force non-interactive mode.
=item B<--pipe|-p>
This is a special mode in which xsh acts as a pipe-line processing
tool. In this mode, first the standard input is read and opened as a
document _ (underscore), then all XSH commands given in ~/.xshrc,
command-line and given XSH scripts are applied and finally the
(possibly modified) document _ is dumped back on the standard output.
It is equivallent to C<-I - -O -> and C<-P ->.
=item B<--input|-I> filename
Preload given file as a document with ID _ upon startup.
=item B<--output|-O> filename
Try to saves document with ID _ into given file before XSH ends.
=item B<--process|-P> filename
A convenient shortcut for C<-I filename -O filename>.
=item B<--html|-H>
Make XSH expect HTML documents by default in all open/save operations.
=item B<--xml|-X>
This option is included only for completeness sake.
Make XSH expect XSH documents by default in all open/save operations
(this is the default).
=item B<--trace-grammar|-T>
This option allows tracing the way XSH language parser processes your
script.
=item B<--query-encoding|-E> encoding
Set the encoding that used in the XSH scripts (or keyboard input).
=item B<--encoding|-e> encoding
Set the encoding that should be used for XSH output.
=item B<--usage|-u>
Print a brief help message on usage and exits.
=item B<--help|-h>
Prints the help page and exits.
=item B<--man>
Displays the help as manual page.
=back
=head1 AUTHOR
Petr Pajas <pajas@matfyz.cz>
Copyright 2000-2003 Petr Pajas, All rights reserved.