From: John on
I have seen the topic of how to use the GNU readline(3c) procedure
from Fortran come up several times.
I tried it using the ISO_C_BINDINGS module from the (recent) g95(1)
and gfortran(1) compilers. I had
several surprises; I also succeeded in making a module for the entire
VOGLE graphics library using the
ISO_C_BINDINGS module; which lets me eliminate a good deal of C/
Fortran interface code that was always
running into porting issues. The bindings work on at least the Intel,
GNU, and G95 compilers. But it seemed
more complex to pass strings around than I expected.
1) Hopefully, others will find this (albeit unpolished) version
useful.
2) If anyone can try this with other compilers I would be
interested in the results
3) Is there something I missed that would make this simpler? When I
started, I thought it was going to
take about 10 lines of coding to do this.

If it builds for you, it prompts for a regular line of input read with
a Fortran READ(), then it prompts for a line
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
characters it sees in the read line with a little old
routine called PDEC after a read so you can get a better idea of what
you actually sent to the program; because if you
start using the arrow keys and delete key and such on a regular
READ(3F), what you see is very likely not
what you get.

For me, something like
cc -c Freadline.c
g95 rl.f90 Freadline.o -l readline -o rl
../rl

works. After any feedback is incorporated and I clean it up a bit,
I'll put this on the Fortran Wiki for anyone
interested.


# @(#) Call readline(3c) from Fortran using ISO_C_BINDING
################################################################################
#
# The C routine Fcreadline.c
#

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <readline/readline.h>
#include <readline/history.h>
FCreadline(int *len, char *myline, char *prompt){
/*
@(#)FCreadline.sh return line from readline(3c) to Fortran. John S.
Urban, 20100323

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

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

/* use readline(3c) to read a line of input in edit mode */
line=readline(prompt);
/* copy line returned by readline(3c) to MYLINE up to length of
MYLINE */
strncpy(myline,line,(int)len);

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

/* free memory used to return line from readline(3c) */
free(line);
}


################################################################################
#
# Fortran module
#

!-------------------------------------------------------------------------------
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),intent(in) :: LINE(*)
character*(*) LINE
character*(*) 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
!-------------------------------------------------------------------------------

############################################################
# Fortran test program

program testit

use JSU_READLINE
character(len=90):: line

ilen=len(line)

do
! regular fortran read
write(*,'(a)',advance='no')'READ>'
read(*,'(a)')line
write(*,*)
call pdec(line) ! show what Fortran got
if(line.eq.'q') stop

! readline(3c) read
call iso_readline(line,'READLINE>') ! read editable input line
call pdec(line) ! show what Fortran got
if(line.eq.'q') stop

!call system(line)
enddo
!
=======================================================================--------
contains
!
=======================================================================--------
! @(#) write out a string with ASCII Decimal Equivalent (ADE) numbers
under it
subroutine pdec(string)
character*(*) string
ilen=len(string)
ilen=len_trim(string(:ilen))

write(*,'(a)')string ! print line as-is
! replace lower unprintable characters with spaces
write(*,101)(char(max(32,ichar(string(i:i)))),i=1,ilen)

! print ADE value of character underneath it
write(*,202)(ichar(string(i:i))/100,i=1,ilen)
write(*,202)(mod(ichar(string(i:i)),100)/10,i=1,ilen)
write(*,202)(mod((ichar(string(i:i))),10),i=1,ilen)
101 format(9999a1:)
202 format(9999i1:)
return
end subroutine pdec
!
=======================================================================--------
end program testit
From: Arjen Markus on
On 24 mrt, 04:09, John <urbanj...(a)comcast.net> wrote:
> I have seen the topic of how to use the GNU readline(3c) procedure
> from Fortran come up several times.
> I tried it using the ISO_C_BINDINGS module from the (recent) g95(1)
> and gfortran(1) compilers. I had
> several surprises; I also succeeded in making a module for the entire
> VOGLE graphics library using the
> ISO_C_BINDINGS module; which lets me eliminate a good deal of C/
> Fortran interface code that was always
> running into porting issues. The bindings work on at least the Intel,
> GNU, and G95 compilers. But it seemed
> more complex to pass strings around than I expected.
>    1) Hopefully, others will find this (albeit unpolished) version
> useful.
>    2) If anyone can try this with other compilers I would be
> interested in the results
>    3) Is there something I missed that would make this simpler? When I
> started, I thought it was going to
>        take about 10 lines of coding to do this.
>
> If it builds for you, it prompts for a regular line of input read with
> a Fortran READ(), then it prompts for a line
> 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
> characters it sees in the read line with a little old
> routine called PDEC after a read  so you can get a better idea of what
> you actually sent to the program; because if you
> start using the arrow keys and delete key and such on a regular
> READ(3F), what you see is very likely not
> what  you get.
>
> For me, something like
> cc -c Freadline.c
> g95 rl.f90 Freadline.o -l readline -o rl
> ./rl
>
> works. After any feedback is incorporated and I clean it up a bit,
> I'll put this on the Fortran Wiki for anyone
> interested.
>
> # @(#) Call readline(3c) from Fortran using ISO_C_BINDING
> ###########################################################################­#####
> #
> # The C routine Fcreadline.c
> #
>
> #include <stdlib.h>
> #include <unistd.h>
> #include <stdio.h>
> #include <string.h>
> #include <readline/readline.h>
> #include <readline/history.h>
> FCreadline(int *len, char *myline, char *prompt){
> /*
> @(#)FCreadline.sh  return line from readline(3c) to Fortran. John S.
> Urban, 20100323
>
> len    -- number of characters in argument "myline"
> myline -- Fortran CHARACTER variable to recieve the line read by
> readline(3c)
>
> */
>    char *line;  /* readline(3c) will return the read line to this
> pointer */
>    int i;       /* counter for padding returned line with spaces */
>
>    /* use readline(3c) to read a line of input in edit mode */
>    line=readline(prompt);
>    /* copy line returned by readline(3c) to MYLINE up to length of
> MYLINE */
>    strncpy(myline,line,(int)len);
>
>    /* starting with null, pad with spaces to end */
>    for(i=strlen(line);i<(int)len;i++){
>      myline[i]=' ';
>    }
>
>    /* free memory used to return line from readline(3c) */
>    free(line);
>
> }
>
> ###########################################################################­#####
> #
> # Fortran module
> #
>
> !--------------------------------------------------------------------------­-----
> 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),intent(in) :: LINE(*)
>    character*(*) LINE
>    character*(*) 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
> !--------------------------------------------------------------------------­-----
>
> ############################################################
> #  Fortran test program
>
> program testit
>
>    use JSU_READLINE
>    character(len=90):: line
>
>    ilen=len(line)
>
>    do
>       ! regular fortran read
>       write(*,'(a)',advance='no')'READ>'
>       read(*,'(a)')line
>       write(*,*)
>       call pdec(line) ! show what Fortran got
>       if(line.eq.'q') stop
>
>       ! readline(3c) read
>       call iso_readline(line,'READLINE>') ! read editable input line
>       call pdec(line) ! show what Fortran got
>       if(line.eq.'q') stop
>
>       !call system(line)
>    enddo
> !
> =======================================================================----­----
> contains
> !
> =======================================================================----­----
> ! @(#) write out a string with ASCII Decimal Equivalent (ADE) numbers
> under it
> subroutine pdec(string)
>       character*(*) string
>       ilen=len(string)
>       ilen=len_trim(string(:ilen))
>
>       write(*,'(a)')string ! print line as-is
>       ! replace lower unprintable characters with spaces
>       write(*,101)(char(max(32,ichar(string(i:i)))),i=1,ilen)
>
>       ! print ADE value of character underneath it
>       write(*,202)(ichar(string(i:i))/100,i=1,ilen)
>       write(*,202)(mod(ichar(string(i:i)),100)/10,i=1,ilen)
>       write(*,202)(mod((ichar(string(i:i))),10),i=1,ilen)
> 101   format(9999a1:)
> 202   format(9999i1:)
>       return
> end subroutine pdec
> !
> =======================================================================----­----
> end program testit

It is a nice example of calling a less than trivial C function, but
when you
put it on the Fortran Wiki be sure to mention the licence for the
readline
library - it is GPL, if I am not mistaken and not everybody likes
that.

With a bit more work it should be possible to implement the
functionality in pure
Fortran, by the way (with a bit of help from the Linux/UNIX/... stty
program) -
see http://wiki.tcl.tk/20215 for an implementation in Tcl.

Regards,

Arjen
From: Julien Rioux on
Hi John,

It works really nicely over here. I tried the TAB completion feature
and found that it lists the files in the current directory. So it
seems to emulate a shell prompt somehow. How hard would it be to use
the input history and autocompletion features of readline from
Fortran?

Cheers,
Julien
From: John on
On Mar 25, 10:39 am, Julien Rioux <julien.ri...(a)gmail.com> wrote:
> Hi John,
>
> It works really nicely over here. I tried the TAB completion feature
> and found that it lists the files in the current directory. So it
> seems to emulate a shell prompt somehow. How hard would it be to use
> the input history and autocompletion features of readline from
> Fortran?
>
> Cheers,
> Julien

The readline(3c) does auto-completion and history vi and emacs mode
and most of
the other command-line "editing" options the GNU shells provide by
default. I made a simple
stab at activating the history mode and made the example a little
cleaner and
added the note/warning about readline(3c) being bound by the GPL
license, and a
"SEE ALSO" section pointing to the Tcl implementation, and some other
links like
rlwrap(1) and such and put it on the Fortran wiki at:

http://fortranwiki.org/fortran/show/iso_readline(3f)

The history mechanism setup I added is rudimentary, but should work.
readline(3c) is
a Tour de Force on CLI completion, history, and editing. I admit to
using but a subset of what it does, but it's nice to use where I can
live with the licensing.
I have just begun using the ISO_C_BINDING module, so anyone that can
improve the Wiki entry
should feel quite free to do so. I use my own routines (JUCMD) quite
frequently instead,
but it pales in comparison, I suppose. In some cases, I actually use
both, as they can co-exist.
I "learned a few things" (hopefully correct things) about passing
strings with the ISO_C_BINDING
interface, so I hope the example is useful.