Calling readline(3c) from Fortran

I have seen the question of how to use the GNU readline(3c) procedure from Fortran come up several times. I tried it using the ISO_C_BINDINGS module (available in g95(1) and gfortran(1) compilers, among others). Hopefully, others will find this (albeit unpolished) version useful.

The test program is basically just a read loop that prompts for lines of input read with readline(3c). You can edit the line being read with readline(3c) per it's documentation. At a minimum, you can probably move around the line with the left and right arrow keys, and insert characters by typing them whereever you moved the cursor to, and use the DEL/ RUBOUT key to delete characters and such. If you use a GNU/Linux shell with command line editing, you are probably familiar with readline(3c)'s function.

It quits if you enter 'q' on an input line, and it dumps the history if you enter 'h'.

It is presented here as a Bourne shell script that creates the neccessary files and does a "compile, load, and go"

#!/bin/sh
# @(#) Call readline(3c) from Fortran using ISO_C_BINDING
# assumes you have the GNU readline library libreadline.a available
################################################################################
#
# The C routine
#
cat > FCreadline.c <<\EOF
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <readline/readline.h>
#include <readline/history.h>
#
/* -------------------------------------------------------------------------- */
void show_history_list() {
  HISTORY_STATE *state =  history_get_history_state();
  int i;
  printf("History list now:\n");
  for (i=0; i < state->length; i++) {
    printf("%d: '%s'%s\n", i, state->entries[i]->line, (i == state->offset? "*":""));
  }     
} 
/* -------------------------------------------------------------------------- */
FCreadline(int *len, char *myline, char prompt[]){
/*
@(#)FCreadline.sh  return line from readline(3c) to Fortran. John S. Urban, 20100323

   Simple procedure that uses readline in "normal" (i.e. non-callback) mode. 

   len    -- number of characters in argument "myline"
   myline -- Fortran CHARACTER variable to recieve the line read by readline(3c) 
   prompt -- prompt string to preceed read

*/
   char *line;                            /* readline(3c) will return the read line to this pointer */
   int i;                                 /* counter for padding returned line with spaces */

   using_history();  
   line=readline(prompt);                 /* use readline(3c) to read a line of input in edit mode */
   add_history(line);

   if(strcmp(line,"h")==0){   /* if the "h" command is on a line by itself show history */
      show_history_list();
   }

   strncpy(myline,line,(int)len);         /* copy line returned by readline(3c) to MYLINE up to length of MYLINE */

   for(i=strlen(line);i<(int)len;i++){    /* starting with null, pad with spaces to end */
     myline[i]=' ';
   }

   /* free memory used to return line from readline(3c) */
   free(line);       
}
EOF
    
################################################################################
#
# Fortran module
#
cat > jsu_readline.f90 <<\EOF
!-------------------------------------------------------------------------------
MODULE jsu_readline
   USE ISO_C_BINDING
   IMPLICIT NONE
   PRIVATE
   PUBLIC iso_readline
!-------------------------------------------------------------------------------
! define the call to the C routine
! extern char     *Freadline(int ilen, char *buf, char prompt[]);
  PUBLIC ::  Freadline
   INTERFACE
      SUBROUTINE Freadline(ilen,buf,prompt) BIND(C,NAME='FCreadline')
         USE ISO_C_BINDING
         IMPLICIT NONE
         INTEGER(KIND=C_INT),INTENT(IN),VALUE      ::  ilen
         CHARACTER(KIND=C_CHAR),intent(out)  ::  buf(*)
         CHARACTER(KIND=C_CHAR),intent(in)   ::  prompt(*)
      END SUBROUTINE Freadline
   END INTERFACE
!-------------------------------------------------------------------------------
contains
! the routine that calls the C routine
SUBROUTINE iso_readline(line,prompt)
   USE ISO_C_BINDING
   IMPLICIT NONE
   CHARACTER(KIND=C_CHAR,LEN=*),INTENT(OUT) :: line
   CHARACTER(KIND=C_CHAR,LEN=*),INTENT(IN)  :: prompt

   ! trim to last non-blank character and append null for C
   CALL Freadline(LEN(line),line,prompt(:LEN_TRIM(prompt))//ACHAR(0))

 END SUBROUTINE iso_readline
!-------------------------------------------------------------------------------
END MODULE jsu_readline
!-------------------------------------------------------------------------------
EOF

################################################################################
cat > rl.f90 <<\EOF
! the test program
PROGRAM testit
   USE jsu_readline
   IMPLICIT NONE
   CHARACTER(LEN=256):: line

   WRITE(*,*)' ____________________________________________________________'
   WRITE(*,*)'  Your input lines are now edittable using the GNU'
   WRITE(*,*)'  readline(3C) procedure.  By default, up-arrow and'
   WRITE(*,*)'  down-arrow go thru the history lines; left and right arrow'
   WRITE(*,*)'  keys and delete and just typing characters let you do'
   WRITE(*,*)'  simple editting. Far more input control is available.'
   WRITE(*,*)'  See the browser pages and man(1) pages for readline(3c).'
   WRITE(*,*)' ____________________________________________________________'
   WRITE(*,*)' Enter text and then edit it. "q" quits; "h" display history:'

   DO
      CALL iso_readline(line,'readline>') ! read editable input line
      IF(line.EQ.'q') STOP
      !CALL system(line(:LEN_TRIM(line))) ! common extension
      !CALL execute_command_line(line(:LEN_TRIM(line))) ! f08 equivalent
   ENDDO

END PROGRAM testit
!-------------------------------------------------------------------------------
EOF
################################################################################
#
# compile, load, and go
#
set -x
rm -f rl FCreadline.o

cc -c FCreadline.c 
gfortran -c jsu_readline.f90  
ar rv libfrl.a FCreadline.o jsu_readline.o 
rm FCreadline.o jsu_readline.o 

gfortran rl.f90 -L . -I . -l frl -l readline -o rl 
./rl

################################################################################
exit
################################################################################

Generated by John S. Urban, 20100323