[comp.lang.perl] integrating GNU Readline and interactive perl scripts

othar@joliet.berkeley.edu (Othar Hansson) (07/14/90)

The following trivial program demonstrates a quick and dirty way to
integrate the GNU Readline library into interactive perl scripts, i.e.,
by the following kind of perl fragment:

$TTY=`tty`; chop $TTY;
$USERPROMPT='yeah?> ';
open(USERIN,"gnureadline \"$USERPROMPT\" 2>$TTY |");
while (<USERIN>) {
  chop;
  printf STDOUT "you typed: (%s)\n", $_;
 # munch on $_
}

Readline allows the user to play with his input in the style of Ksh or Bash:

	- emacs-style editing commands work (even keyboard macros)
	- a command history is retained, and the current line is
	  viewed as a one-line window on a command-history (emacs)
	  buffer, i.e., C-s and C-r search through commands,
	  C-n and C-p scroll through them
	- gobs of other fortuitous features derived from
	  the emacs analogy (user-defined keymaps, completion, etc.)
	- if you don't want all those features, vi-mode is available

This program pleased one of my Ksh-spoiled users to no end.  Try it out
-- the fragment above should be pretty easy to put into any interactive
perl script that doesn't do too much tty ioctl magic of its own.  If you
like it, read the Readline docs for customization info.


Othar Hansson
CS Division
UC Berkeley
..!ucbvax!ernie!othar
othar@ernie.berkeley.edu


   ---------cut here for gnureadline.c----------

/* 
 * Readline Filter Process (gnureadline.c)
 *
 * Reads a line of text (echoed on stderr) and outputs it (on stdout).
 * Designed for piping between user and a Perl script.
 *
 * by Othar Hansson, othar@ernie.berkeley.edu
 *  by simply extending a fragment from the readline documentation
 *  (readline.texinfo) by Brian Fox
 *
 * the GNU Readline library is available on major ftp sites
 * (e.g., in pub/gnu on labrea.stanford.edu or prep.ai.mit.edu)
 * where it is bundled with the BASH distribution.  
 * This code works with bash-1.05.
 *
 * The GNU General Public License applies to the GNU readline library,
 * and thus to all but the most trivial uses of this program
 */

#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>

/* A static variable for holding the line. */
static char *my_gets_line = (char *)NULL;

/*
 * Read a string, and return a pointer to it.  Returns NULL on EOF. 
 */
     
static char *
my_gets (prompt)
     char* prompt;
{
                      /* free the string that readline() alloc'ed last time */
  if (my_gets_line != (char *)NULL)
    free (my_gets_line);
  
  my_gets_line = readline (prompt);            /* Get a line from the user. */
  
                                /* If the line has any text in it, save it. */
  if (my_gets_line && *my_gets_line)
    add_history (my_gets_line);

  return (my_gets_line);
}

int main (argc, argv)
     int argc;
     char* argv[];
{
  char *prompt = "";

  if (argc >= 2) 
    prompt = argv[1];

  rl_outstream = stderr;		/* so output doesn't see your typos */

  for (;;) {
    char *nextline = my_gets(prompt);
    if (nextline == (char*) NULL) 
      return 0;
    else
      fprintf(stdout,"%s\n",nextline);
      fflush(stdout);
  }
}

/* compile me with 
 *  cc -g -o gnureadline -I$BASHDIR gnureadline.c 
 *     -L$BASHDIR/readline -lreadline -ltermcap' 
 */

   ---------cut here----------

othar@joliet.berkeley.edu (Othar Hansson) (07/17/90)

A few days ago, I posted a simple C program that lets you bring the GNU
readline library into interactive perl scripts.  Someone suggested that
I try to do so for the debugger.  The diff for perldb.pl ends this
message.  

Briefly, this makes the perl debugger's command-line editing act
somewhat like GDB.  An initial test suggests that it clashes badly with
an interactive program that uses the same technique, but perhaps that
can be fixed.

You can try it out by patching a copy of perldb.pl in, e.g., /tmp, and
prefacing a script with: #!/usr/bin/perl -dI/tmp

For those who missed the original msg, the Readline library allows users
to edit the command line in the style of Ksh and Bash.  I can email you
gnureadline.c if you can't get the article out of your news feed.

 Othar Hansson
 CS Division, UC Berkeley
 ..!ucbvax!ernie!othar
 othar@ernie.berkeley.edu

Script started on Tue Jul 17 02:18:45 1990
/tmp>diff -c perldb.pl /usr/local/lib/perl/perldb.pl
*** perldb.pl	Tue Jul 17 02:17:04 1990
--- /usr/local/lib/perl/perldb.pl	Fri Dec 30 00:00:00 1988
***************
*** 25,39 ****
  # 
  #
  
! $TTY=`tty`;
! chop $TTY;
! # open(IN,"/dev/tty");		# so we don't dingle stdin
! open(IN,"gnureadline '  (perldb) ' 2>$TTY |");
! 
  open(OUT,">/dev/tty");	# so we don't dongle stdout
  select(OUT);
- printf "<readline debugger running...>\n";
- 
  $| = 1;				# for DB'OUT
  select(STDOUT);
  $| = 1;				# for real STDOUT
--- 25,33 ----
  # 
  #
  
! open(IN,"/dev/tty");		# so we don't dingle stdin
  open(OUT,">/dev/tty");	# so we don't dongle stdout
  select(OUT);
  $| = 1;				# for DB'OUT
  select(STDOUT);
  $| = 1;				# for real STDOUT
***************
*** 74,81 ****
  	print OUT $#stack . " levels deep in subroutine calls!\n"
  	    if $single & 4;
  	$start = $line;
! #	while ((print OUT "  DB<", $#hist+1, "> "), $cmd=<IN>) {
! 	while ($cmd=<IN>) {
  	    $single = 0;
  	    $signal = 0;
  	    $cmd eq '' && exit 0;
--- 68,74 ----
  	print OUT $#stack . " levels deep in subroutine calls!\n"
  	    if $single & 4;
  	$start = $line;
! 	while ((print OUT "  DB<", $#hist+1, "> "), $cmd=<IN>) {
  	    $single = 0;
  	    $signal = 0;
  	    $cmd eq '' && exit 0;
/tmp>exit
script done on Tue Jul 17 02:21:21 1990