regsub Subroutine

public subroutine regsub(matchline, matches, source, dest)

NAME

regsub(3f) - [M_regex] perform regex substitutions

SYNOPSIS

subroutine regsub(matchline, matches, source, dest)

character(len=*),intent(in)              :: matchline
integer,intent(in)                       :: matches(:,:)
character(len=*),intent(in)              :: source
character(len=:),allocatable,intent(out) :: dest

DESCRIPTION

The regsub() function copies source to dest, making substitutions according to the most recent regexec() performed using MATCHES(:,:).

Each instance of “&” in source is replaced by the substring indicated by the start and end array MATCHES(:,:).

Each instance of “\n”, where n is a digit, is replaced by the substring indicated by MATCHES(1,n) and MATCHES(2,n).

To get a literal “&” or “\n” into dest, prefix it with “"; to get a literal “" preceding “&” or “\n”, prefix it with another “".

OPTIONS

MATCHLINE  line REGEXEC(3f) was run against
MATCHES    output array from REGEXEC(3f) call
SOURCE     output template string containing "&" and/or "\n" indicating where to
           make substitutions
DEST       output string

EXAMPLES

Sample program that reads an array of lines representing a Unix /etc/passwd file and uses an RE (Regular Expression) to read fields from the line and then uses REGSUB(3f) to print strings using the matched strings

program demo_regsub
use M_regex, only: regex_type, regcomp, regexec, regerror, regmatch, regfree, regsub
implicit none
type(regex_type)             :: regex
integer,parameter            :: maxmatch=10
integer                      :: matches(2,maxmatch)
character(len=:),allocatable :: input_line(:)
character(len=:),allocatable :: output_line
character(len=:),allocatable :: expression
logical                      :: match
integer                      :: stat
integer                      :: i
logical                      :: BRE
   ! The /etc/passwd file is a colon-separated file that contains the following information:
   !
   !     User name.
   !     Encrypted password.
   !     User ID number (UID)
   !     User's group ID number (GID)
   !     Full name of the user (GECOS)
   !     User home directory.
   !     Login shell.
   !
   BRE=.true.
   if(BRE)then  ! BRE (Basic Regular Expression)
      expression= '\([^:]*\):*\([^:]*\):*\([^:]*\):*\([^:]*\):*\([^:]*\):*\([^:]*\):*\([^:]*\):*.*'
      call regcomp(regex,expression,status=stat)
   else         ! ERE (Extended Regular Expression)
      expression= '([^:]*):*([^:]*):*([^:]*):*([^:]*):*([^:]*):*([^:]*):*([^:]*):*.*'
      call regcomp(regex,expression,'x',status=stat)
   endif

   if(stat.ne.0)then                                        ! if RE did not compile report error
      write(*,*)'*regcomp* ERROR:',regerror(regex,stat)
      stop 1
   endif

   ! simulate /etc/password file in array, with common aberrant input lines included
   input_line= [character(len=128) ::                                     &
   "doeqj:xxxxx:1001:200:John Q. Doe:/home/doeqj:/bin/tcsh: and more",    &
   "doeqj:xxxxx:1001:200:John Q. Doe:/home/doeqj:/bin/tcsh: and more:",   &
   "doeqj: :1001: :John Q. Doe:/home/doeqj:/bin/tcsh:",                   &
   "doeqj:xxxxx:1001:200:John Q. Doe:/home/doeqj:/bin/tcsh:",             &
   "doeqj:xxxxx:1001:200:John Q. Doe:/home/doeqj:/bin/tcsh",              &
   "doeqj:xxxxx:1001:200:John Q. Doe:/home/doeqj",                        &
   "doeqj:xxxxx:",                                                        &
   "doeqj:xxxxx",                                                         &
   "doeqj:",                                                              &
   "doeqj",                                                               &
   ! the RE shown needs the field to have at least one character
   ! which should be replaced first, but not shown in this example
   ": : : : : : : : : : : : : : :",                                       &
   ": ::",                                                                &
   "" ]

   do i=1,size(input_line)
      match=regexec(regex,input_line(i),matches)            ! generate the matches array using the compiled RE

      write(*,'(a)')repeat('-',80)                          ! put out a number line
      write(*,'(a)') input_line(i)
                                                            ! replace \n lines
      call regsub(input_line(i),matches,'username="\1" &
      &password="\2" UID="\3" GID="\4" name="\5" &
      &home="\6" shell="\7" ',output_line)
      write(*,'(a)') output_line

      if(i.eq.1)then                                        ! show other examples of formatting for first entry
         call regsub(input_line(i),matches,&
         & 'The username for \5 is "\1"',output_line)
         write(*,'(a)') output_line

         call regsub(input_line(i),matches,&
         & 'username "\1" has UID=\3, GID=\4 &
         & and default shell "\7"',output_line)
         write(*,'(a)') output_line
      endif
   enddo

   call regfree(regex)

end program demo_regsub
--------------------------------------------------------------------------------
doeqj:xxxxx:1001:200:John Q. Doe:/home/doeqj:/bin/tcsh: and more
username="doeqj" password="xxxxx" UID="1001" GID="200" name="John Q. Doe" home="/home/doeqj" shell="/bin/tcsh"
The username for John Q. Doe is "doeqj"
username "doeqj" has UID=1001, GID=200 and default shell "/bin/tcsh"
--------------------------------------------------------------------------------
doeqj:xxxxx:1001:200:John Q. Doe:/home/doeqj:/bin/tcsh: and more:
username="doeqj" password="xxxxx" UID="1001" GID="200" name="John Q. Doe" home="/home/doeqj" shell="/bin/tcsh"
--------------------------------------------------------------------------------
doeqj: :1001: :John Q. Doe:/home/doeqj:/bin/tcsh:
username="doeqj" password=" " UID="1001" GID=" " name="John Q. Doe" home="/home/doeqj" shell="/bin/tcsh"
--------------------------------------------------------------------------------
doeqj:xxxxx:1001:200:John Q. Doe:/home/doeqj:/bin/tcsh:
username="doeqj" password="xxxxx" UID="1001" GID="200" name="John Q. Doe" home="/home/doeqj" shell="/bin/tcsh"
--------------------------------------------------------------------------------
doeqj:xxxxx:1001:200:John Q. Doe:/home/doeqj:/bin/tcsh
username="doeqj" password="xxxxx" UID="1001" GID="200" name="John Q. Doe" home="/home/doeqj" shell="/bin/tcsh"
--------------------------------------------------------------------------------
doeqj:xxxxx:1001:200:John Q. Doe:/home/doeqj
username="doeqj" password="xxxxx" UID="1001" GID="200" name="John Q. Doe" home="/home/doeqj" shell=""
--------------------------------------------------------------------------------
doeqj:xxxxx:
username="doeqj" password="xxxxx" UID="" GID="" name="" home="" shell=""
--------------------------------------------------------------------------------
doeqj:xxxxx
username="doeqj" password="xxxxx" UID="" GID="" name="" home="" shell=""
--------------------------------------------------------------------------------
doeqj:
username="doeqj" password="" UID="" GID="" name="" home="" shell=""
--------------------------------------------------------------------------------
doeqj
username="doeqj" password="" UID="" GID="" name="" home="" shell=""
--------------------------------------------------------------------------------
: :: :: :: :: :: :: ::
username="" password=" " UID=" " GID=" " name=" " home=" " shell=" "
--------------------------------------------------------------------------------
: ::
username="" password=" " UID="" GID="" name="" home="" shell=""
--------------------------------------------------------------------------------

username="" password="" UID="" GID="" name="" home="" shell=""

Arguments

Type IntentOptional Attributes Name
character(len=*), intent(in) :: matchline
integer, intent(in) :: matches(:,:)
character(len=*), intent(in) :: source
character(len=:), intent(out), allocatable :: dest

Source Code

subroutine regsub(matchline,matches, source, dest)

! ident_6="@(#) M_regex regsub(3f) perform regex substitutions"

character(len=*),intent(in)              :: matchline
integer,intent(in)                       :: matches(:,:)
character(len=*),intent(in)              :: source
character(len=:),allocatable,intent(out) :: dest
   character(len=:),allocatable :: src
   character(len=1)             :: c
   character(len=1)             :: cc
   integer                      :: no
   integer                      :: len
   integer                      :: i
   logical                      :: skip
   integer,parameter            :: inine=ichar('9'), izero=ichar('0')
   !!if(debug)then
   !!   write(*,'(a,*(i0.3:,","))')'MATCHES(1,:)=',matches(1,:)
   !!   write(*,'(a,*(i0.3:,","))')'MATCHES(2,:)=',matches(2,:)
   !!   write(*,*)'SOURCE=['//SOURCE//'] LEN=',len(SOURCE)
   !!endif
   if(source.eq.'')then
      dest=''
      write(ERROR_UNIT,*)"*regsub* NULL parameter to regsub"
      return
   endif
   src = source//' '

   dest = ''
   skip=.false.
   do i=1,len(source)
      if(skip)then
         skip=.false.
         cycle
      endif
      c=src(i:i)
      cc=src(i+1:i+1)
      if (c == '&')then                                                           ! & is replaced by match 1
         if(any(matches(:,1).le.0))cycle
         dest=dest//matchline( matches(1,1):matches(2,1) )
         cycle
      elseif (c == '\' .and. (ichar(cc) >= izero .and. ichar(cc) <= inine) )then  ! \[0-9] is replaced by match 1 to 10
         no = ichar(cc) - ichar('0')
         if(.not.any(matches(:,no+1).le.0))then
            dest=dest//matchline( matches(1,no+1):matches(2,no+1) )
         endif
         skip=.true.
         cycle
      elseif (c == '\' .and. (cc == '\' .or. cc == '&'))then                      ! escaping literal ("\\" or "\&")
         skip=.true.
         cycle
      else
         dest=dest//c                                                             ! non-escaped ordinary character
      endif
   enddo
end subroutine regsub