module Macros

  implicit NONE
  private
  public :: Path_T, First_Path, Last_Path, Macro_Depth
  public :: Add_Path, Open_Macro

  type :: Path_T
    character(len=1023) :: Path ! A path to search for macros
    integer :: Len = 0
    type(path_t), pointer :: Next => NULL()
  end type Path_T

  type(path_t), pointer :: First_path => NULL(), Last_Path => NULL()

  ! if Macro_Depth /= 0, read from unit U_Input+Macro_Depth.
  integer, save :: Macro_Depth = 0

contains

  subroutine Add_Path ( Path )
  ! Add a path to the list of paths to search for macro files
    character(len=*), intent(in) :: Path
    if ( .not. associated(first_path) ) then
      allocate ( first_path )
      first_path%path = '.'
      first_path%len = 1
      last_path => first_path
    end if
    allocate ( last_path%next )
    last_path => last_path%next
    last_path%path = path
    last_path%len = len(path)
  end subroutine Add_Path

  integer function Open_Macro ( Macro )
  ! Open a file with the same name as Macro, trying lower case first
  ! and then upper case.  Return the macro depth if a file is successfully
  ! opened, else -1 if no such file is found.
    use IO_UNITS, only: U_INPUT
    character(len=*), intent(in) :: Macro
    character(len=len(macro)) :: Copy ! In either lower or upper case
    integer :: I, IOSTAT
    type(path_t), pointer :: Path ! For searching
    if ( .not. associated(first_path) ) then
      allocate ( first_path )
      first_path%path = '.'
      first_path%len = 1
      last_path => first_path
    end if
    path => first_path
    macro_depth = macro_depth + 1
    open_macro = macro_depth
    do while ( associated(path) )
      do i = 1, len(macro)
        ! Try lower case first
        if ( iachar(macro(i:i)) >= iachar("A") .and. &
          &  iachar(macro(i:i)) <= iachar("Z") ) then
          copy(i:i) = achar(iachar(macro(i:i)) + iachar("a") - iachar("A"))
        else
          copy(i:i) = macro(i:i)
        end if
      end do
      open ( unit=u_input+macro_depth, status='OLD', &
        &    file=path%path(:path%len) // "/" // copy, iostat=iostat )
      if ( iostat == 0 ) return
      ! now try upper case
      do i = 1, len(macro)
        if ( iachar(macro(i:i)) >= iachar("a") .and. &
          &  iachar(macro(i:i)) <= iachar("z") ) then
          copy(i:i) = achar(iachar(macro(i:i)) + iachar("A") - iachar("a"))
        else
          copy(i:i) = macro(i:i)
        end if
      end do
      open ( unit=u_input+macro_depth, status='OLD', &
        &    file=path%path(:path%len) // "/" // copy, iostat=iostat )
      if ( iostat == 0 ) return
      path => path%next
    end do
    macro_depth = macro_depth - 1
    open_macro = -1
  end function Open_Macro

end module Macros
