Term::CallEditor - solicit data from an external editor


Term-CallEditor documentation Contained in the Term-CallEditor distribution.

Index


Code Index:

NAME

Top

Term::CallEditor - solicit data from an external editor

SYNOPSIS

Top

  use Term::CallEditor qw/solicit/;

  my $fh = solicit('FOO: please replace this text');
  die "$Term::CallEditor::errstr\n" unless $fh;

  print while <$fh>;

DESCRIPTION

Top

This module calls an external editor with an optional text message via the solicit() function, then returns any data from this editor as a file handle. By default, the EDITOR environment variable will be used, otherwise vi.

solicit() returns a temporary file handle pointing to what was written in the editor (or also the filename in list context).

SOLICIT

Top

solicit() as a second argument accepts a number of optional parameters as a hash reference.

  solicit(
    "\x{8ACB}",
    { skip_interactive => 1,
      binmode_layer => ':utf8'
    }
  );

BINMODE => BOOLEAN

If true, enables binmode on the filehandle prior to writing the message to it.

binmode_layer => binmode layer

If set, enables binmode on the filehandle prior to writing the message to it. Useful if one needs to write UTF-8 or some other encoded data as a message to the EDITOR.

safe_level => NUMBER

Set a custom safe_level value for the File::Temp method of that name. The default safe_level is number 2. Be seeing you.

skip_interactive => BOOLEAN

If true, solicit skips making a test to see whether the terminal is interactive.

On error, solicit() returns undef. Consult $Term::CallEditor::errstr for details. Note that File::Temp may throw a fatal error if the safe_level checks fail, so paranoid coders should wrap the solicit call in an eval block.

EXAMPLES

Top

See also the eg/solicit script under the module distribution.

Pass in a block of text to the editor

Use a here doc:

  my $fh = solicit(<< "END_BLARB");

  FOO: This is an example designed to span multiple lines for
  FOO: the sake of an example that span multiple lines.
  END_BLARB

Support bbedit(1) on Mac OS X

To use BBEdit as the external editor, create a shell script wrapper to call bbedit(1), then set this wrapper as the EDITOR environment variable. The -t option to bbedit(1) can be used to set a custom title, if desired.

  #!/bin/sh
  exec bbedit -w "$@"

Any editor that requires arguments will require a wrapper like this.

BUGS

Top

No known bugs.

Reporting Bugs

Newer versions of this module may be available from CPAN.

If the bug is in the latest version, send a report to the author. Patches that fix problems or add new features are welcome.

http://github.com/thrig/Term-CallEditor

Known Issues

This module relies heavily on the Unix terminal, permissions on the temporary directory (for the File::Temp module safe_level call), whether system() can actually run the EDITOR environment variable, and so forth.

AUTHOR

Top

Jeremy Mates, <jmates@sial.org>

COPYRIGHT

Top

HISTORY

Top

Inspired from the CVS prompt-user-for-commit-message functionality.


Term-CallEditor documentation Contained in the Term-CallEditor distribution.

# -*- Perl -*-
#
# Solicits data from an external editor as determined by the EDITOR
# environment variable. Run perldoc(1) on this module for additional
# documentation.
#
# Copyright 2004-2005,2009-2010 Jeremy Mates
#
# This module is free software; you can redistribute it and/or modify it
# under the Artistic license.

package Term::CallEditor;

use strict;
use warnings;

require 5.006;

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 IO::Handle;

use POSIX qw(getpgrp tcgetpgrp);

$VERSION = '0.60';

sub solicit {
  my $message = shift;
  my $params = shift || {};

  unless ( exists $params->{skip_interative} and $params->{skip_interative} )
  {
    return unless _is_interactive();
  }

  $params->{safe_level} = 2 unless exists $params->{safe_level};
  File::Temp->safe_level( $params->{safe_level} );
  my ( $tfh, $filename ) = tempfile( UNLINK => 1 );

  unless ( $tfh and $filename ) {
    $errstr = 'no temporary file';
    return;
  }

  if ( exists $params->{binmode_layer}
    and defined $params->{binmode_layer} ) {
    binmode( $tfh, $params->{binmode_layer} );
  } elsif ( exists $params->{BINMODE} and $params->{BINMODE} ) {
    binmode($tfh);
  }

  select( ( select($tfh), $|++ )[0] );

  if ( defined $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 ( $ref eq 'GLOB' ) {
      while ( my $line = <$message> ) {
        print $tfh $line;
      }
    } elsif ( UNIVERSAL::can( $message, 'getlines' ) ) {
      print $tfh $message->getlines;
    }
    # Help the bits reach the disk
    $tfh->flush();
    # TODO may need eval or exclude on other platforms
    if ( $^O !~ m/Win32/ ) {
      $tfh->sync();
    }
  }

  my $editor = $ENV{EDITOR} || 'vi';

  # need to unlock for external editor
  flock $tfh, LOCK_UN;

  my $status = system $editor, $filename;
  if ( $status != 0 ) {
    $errstr =
      ( $status != -1 )
      ? "external editor failed: editor=$editor, errstr=$?"
      : "could not launch program: editor=$editor, errstr=$!";
    return;
  }

  # Must reopen filename, as editor could have done a rename() on us, in
  # which case the $tfh is then invalid.
  my $outfh;
  unless ( open( $outfh, '<', $filename ) ) {
    $errstr = "could not reopen tmp file: errstr=$!";
    return;
  }

  return wantarray ? ( $outfh, $filename ) : $outfh;
}

# 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__