# $Id: CallEditor.pm,v 1.9 2004/06/04 17:30:37 jmates Exp $
#
# Copyright 2004 by Jeremy Mates
#
# This library is free software; you can redistribute it and/or modify
# it under the same terms as Perl itself.
#
# Solicits for data from an external Editor such as 'vi' or whatever the
# EDITOR environment variable is set to.
#
# Run perldoc(1) on this module for additional documentation.
package Term::CallEditor;
use 5.005;
use strict;
use warnings;
use vars qw(@EXPORT @ISA $VERSION $errstr);
@EXPORT = qw(solicit);
@ISA = qw(Exporter);
use Exporter;
use Fcntl qw(:DEFAULT :flock);
use File::Temp qw(tempfile);
use POSIX qw(getpgrp tcgetpgrp);
$VERSION = '0.11';
sub solicit {
my $message = shift;
return unless is_interactive();
File::Temp->safe_level(2);
my ( $tfh, $filename ) = tempfile( UNLINK => 1 );
unless ( $tfh and $filename ) {
$errstr = 'no temporary file';
return;
}
select( ( select($tfh), $|++ )[0] );
if ($message) {
my $ref = ref $message;
if ( not $ref ) {
print $tfh $message;
} elsif ( $ref eq 'SCALAR' ) {
print $tfh $$message;
} elsif ( $ref eq 'ARRAY' ) {
print $tfh "@$message";
} elsif ( UNIVERSAL::can( $message, 'getlines' ) ) {
print $tfh $message->getlines;
}
# TODO warn here if no idea how to deal with $message?
}
my $editor = $ENV{EDITOR} || 'vi';
# need to unlock for external editor
flock $tfh, LOCK_UN;
# TODO how suppress "Can't exec" error system returns?
my $status = system $editor, $filename;
if ( $status != 0 ) {
$errstr =
( $status != -1 )
? "external editor failed: editor=$editor, errno=$?"
: "could not launch program: editor=$editor, errno=$!";
return undef;
}
unless ( seek $tfh, 0, 0 ) {
$errstr = "could not seek on temp file: errno=$!";
return;
}
return wantarray ? ( $tfh, $filename ) : $tfh;
}
# Perl CookBook code to check whether terminal is interactive
sub is_interactive {
my $tty;
unless ( open $tty, '< /dev/tty' ) {
$errstr = "cannot open /dev/tty: errno=$!";
return;
}
my $tpgrp = tcgetpgrp fileno $tty;
my $pgrp = getpgrp();
close $tty;
unless ( $tpgrp == $pgrp ) {
$errstr = "no exclusive control of tty: pgrp=$pgrp, tpgrp=$tpgrp";
return;
}
return 1;
}
1;
__END__
=head1 NAME
Term::CallEditor - solicit for data from an external Editor
=head1 SYNOPSIS
use Term::CallEditor;
my $fh = solicit('FOO: please replace this text');
die "$Term::CallEditor::errstr\n" unless $fh;
print while <$fh>;
=head1 DESCRIPTION
This module calls an external editor with an optional text message and
returns what was input as a file handle. By default, the EDITOR
environment variable will be used, otherwise C<vi>.
The C<solicit()> function currently can parse a message from a number of
formats, including a scalar, scalar reference, array, or objects with
the C<getlines> method such as L<IO::Handle|IO::Handle> or
L<IO::All|IO::All>.
On error, C<solicit()> returns C<undef>. Consult
C<$Term::CallEditor::errstr> for details.
=head1 EXAMPLES
=over 4
=item Pass in a block of text to the editor.
my $fh = solicit(<< "BLARB");
FOO: This is an example designed to span multiple lines for the sake
FOO: of an example that span multiple lines.
BLARB
=item Support bbedit(1) on Mac OS X.
To use bbedit(1) as the EDITOR, create a shell script wrapper to
call bbedit(1) as follows, then set the wrapper as the EDITOR
environment variable.
#!/bin/sh
exec bbedit -w "$@"
=back
=head1 AUTHOR
Jeremy Mates, E<lt>jmates@sial.orgE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright 2004 by Jeremy Mates
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.
=head1 HISTORY
Version control systems like CVS and Subversion have similar
behaviour to prompt a user for a commit message, which this module is
inspired from.
=cut
syntax highlighted by Code2HTML, v. 0.9.1