function replace(targetline,old,new,cmd,occurrence,repeat,ignorecase,ierr) result (newline)
! ident_11="@(#) M_strings replace(3f) replace one substring for another in string"
!-----------------------------------------------------------------------------------------------------------------------------------
! parameters
character(len=*),intent(in) :: targetline ! input line to be changed
character(len=*),intent(in),optional :: old ! old substring to replace
character(len=*),intent(in),optional :: new ! new substring
character(len=*),intent(in),optional :: cmd ! contains the instructions changing the string
integer,intent(in),optional :: occurrence ! Nth occurrence of OLD string to start replacement at
integer,intent(in),optional :: repeat ! how many replacements
logical,intent(in),optional :: ignorecase
integer,intent(out),optional :: ierr ! error code. if ierr = -1 bad directive, >=0 then ierr changes made
!-----------------------------------------------------------------------------------------------------------------------------------
! returns
character(len=:),allocatable :: newline ! output string buffer
!-----------------------------------------------------------------------------------------------------------------------------------
! local
character(len=:),allocatable :: new_local, old_local, old_local_for_comparison
integer :: icount,ichange,ier2
integer :: original_input_length
integer :: len_old, len_new
integer :: ladd
integer :: left_margin, right_margin
integer :: ind
integer :: ic
integer :: ichr
integer :: range_local(2)
character(len=:),allocatable :: targetline_for_comparison ! input line to be changed
logical :: ignorecase_local
logical :: flip
character(len=:),allocatable :: targetline_local ! input line to be changed
!-----------------------------------------------------------------------------------------------------------------------------------
flip=.false.
ignorecase_local=.false.
original_input_length=len_trim(targetline) ! get non-blank length of input line
! get old_local and new_local from cmd or old and new
if(present(cmd))then
call crack_cmd(cmd,old_local,new_local,ier2)
if(ier2 /= 0)then
newline=targetline ! if no changes are made return original string on error
if(present(ierr))ierr=ier2
return
endif
elseif(present(old).and.present(new))then
old_local=old
new_local=new
else
newline=targetline ! if no changes are made return original string on error
call journal('sc','*replace* must specify OLD and NEW or CMD')
return
endif
if(present(ignorecase))then
ignorecase_local=ignorecase
else
ignorecase_local=.false.
endif
if(present(occurrence))then
range_local(1)=abs(occurrence)
else
range_local(1)=1
endif
if(present(repeat))then
range_local(2)=range_local(1)+repeat-1
else
range_local(2)=original_input_length
endif
if(ignorecase_local)then
targetline_for_comparison=lower(targetline)
old_local_for_comparison=lower(old_local)
else
targetline_for_comparison=targetline
old_local_for_comparison=old_local
endif
if(present(occurrence))then
if(occurrence < 0)then
flip=.true.
targetline_for_comparison=reverse(targetline_for_comparison)
targetline_local=reverse(targetline)
old_local_for_comparison=reverse(old_local_for_comparison)
old_local=reverse(old_local)
new_local=reverse(new_local)
else
targetline_local=targetline
endif
else
targetline_local=targetline
endif
!-----------------------------------------------------------------------------------------------------------------------------------
icount=0 ! initialize error flag/change count
ichange=0 ! initialize error flag/change count
len_old=len(old_local) ! length of old substring to be replaced
len_new=len(new_local) ! length of new substring to replace old substring
left_margin=1 ! left_margin is left margin of window to change
right_margin=len(targetline) ! right_margin is right margin of window to change
newline='' ! begin with a blank line as output string
!-----------------------------------------------------------------------------------------------------------------------------------
if(len_old == 0)then ! c//new/ means insert new at beginning of line (or left margin)
ichr=len_new + original_input_length
if(len_new > 0)then
newline=new_local(:len_new)//targetline_local(left_margin:original_input_length)
else
newline=targetline_local(left_margin:original_input_length)
endif
ichange=1 ! made one change. actually, c/// should maybe return 0
if(present(ierr))ierr=ichange
if(flip) newline=reverse(newline)
return
endif
!-----------------------------------------------------------------------------------------------------------------------------------
ichr=left_margin ! place to put characters into output string
ic=left_margin ! place looking at in input string
loop: do
! try finding start of OLD in remaining part of input in change window
ind=index(targetline_for_comparison(ic:),old_local_for_comparison(:len_old))+ic-1
if(ind == ic-1.or.ind > right_margin)then ! did not find old string or found old string past edit window
exit loop ! no more changes left to make
endif
icount=icount+1 ! found an old string to change, so increment count of change candidates
if(ind > ic)then ! if found old string past at current position in input string copy unchanged
ladd=ind-ic ! find length of character range to copy as-is from input to output
newline=newline(:ichr-1)//targetline_local(ic:ind-1)
ichr=ichr+ladd
endif
if(icount >= range_local(1).and.icount <= range_local(2))then ! check if this is an instance to change or keep
ichange=ichange+1
if(len_new /= 0)then ! put in new string
newline=newline(:ichr-1)//new_local(:len_new)
ichr=ichr+len_new
endif
else
if(len_old /= 0)then ! put in copy of old string
newline=newline(:ichr-1)//old_local(:len_old)
ichr=ichr+len_old
endif
endif
ic=ind+len_old
enddo loop
!-----------------------------------------------------------------------------------------------------------------------------------
select case (ichange)
case (0) ! there were no changes made to the window
newline=targetline_local ! if no changes made output should be input
case default
if(ic <= len(targetline))then ! if there is more after last change on original line add it
newline=newline(:ichr-1)//targetline_local(ic:max(ic,original_input_length))
endif
end select
if(present(ierr))ierr=ichange
if(flip) newline=reverse(newline)
!-----------------------------------------------------------------------------------------------------------------------------------
end function replace