subroutine mrtindex_index_comm(line,ix,cx,error)
  use gbl_message
  use gkernel_interfaces
  use mrtindex_interfaces, except_this=>mrtindex_index_comm
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ public
  !  Support routine for command
  !   INDEX BUILD|UPDATE|OPEN|APPEND|WATCH|OUTPUT|CONVERT [DirName]
  ! 1       [/FILE IndexFile]
  ! 2       [/RECURSIVE]
  !---------------------------------------------------------------------
  character(len=*),          intent(in)    :: line   ! Input command line
  type(mrtindex_optimize_t), intent(inout) :: ix
  type(mrtindex_optimize_t), intent(inout) :: cx
  logical,                   intent(inout) :: error  ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='INDEX'
  integer(kind=4), parameter :: mvocab=7
  character(len=7) :: vocab(mvocab)
  character(len=16) :: argum,keyword
  integer(kind=4) :: nc,ikey
  logical :: append
  ! Data
  data vocab / 'BUILD','UPDATE','OPEN','APPEND','WATCH','OUTPUT','CONVERT' /
  !
  if (sic_present(1,0) .and. sic_present(2,0)) then
    call mrtindex_message(seve%e,rname,'Exclusive options /FILE and /RECURSIVE')
    error = .true.
    return
  endif
  !
  argum = 'OPEN'
  call sic_ke(line,0,1,argum,nc,.false.,error)
  if (error) return
  call sic_ambigs(rname,argum,keyword,ikey,vocab,mvocab,error)
  if (error)  return
  !
  ! All these commands modify the input index: reset the current index
  cx%next = 1
  !
  append = .false.
  select case (ikey)
  case (1)  ! BUILD
    call mrtindex_index_update(line,.true.,ix,error)
    if (error)  return
  case (2)  ! UPDATE
    call mrtindex_index_update(line,.false.,ix,error)
    if (error)  return
  case (3)  ! OPEN
    continue ! Work done below
  case (4)  ! APPEND
    append = .true.
    continue ! Work done below
  case (5)  ! WATCH
    call mrtindex_watch_comm(line,error)
    return
  case (6)  ! OUTPUT
    call mrtindex_index_output(line,error)
    if (error)  return
    append = .true.
  case (7)  ! CONVERT
    call mrtindex_index_convert(line,ix,error)
    return
  end select
  !
  ! Doing an implicit OPEN at the end is a desired feature of all
  ! modes (except WATCH and CONVERT)
  call mrtindex_index_open(line,append,ix,error)
  if (error)  return
  !
end subroutine mrtindex_index_comm
!
subroutine mrtindex_index_update(line,overwrite,ix,error)
  use gildas_def
  use gkernel_interfaces
  use mrtindex_interfaces, except_this=>mrtindex_index_update
  use mrtindex_types
  use mrtindex_parse_types
  !---------------------------------------------------------------------
  ! @ private
  !  Support routine for command
  !   INDEX BUILD|UPDATE [DirName] [/FILE IndexFile] [/RECURSIVE]
  ! Build or update the scans index.
  !---------------------------------------------------------------------
  character(len=*),          intent(in)    :: line       ! Input command line
  logical,                   intent(in)    :: overwrite  ! Overwrite or update only?
  type(mrtindex_optimize_t), intent(inout) :: ix
  logical,                   intent(inout) :: error      ! Logical error flag
  ! Local
  character(len=filename_length) :: directory,indexfile
  type(user_pattern_t) :: pattern
  !
  call mrtindex_parse(line,directory,indexfile,pattern,error)
  if (error)  return
  !
  if (overwrite) then
    ! If BUILD (not UPDATE), close all previous index files
    call mrtindex_ix_close(error)
    if (error)  return
    ix%next = 1  ! Forget all previous entries in IX
  endif
  !
  if (sic_present(2,0)) then  ! /RECURSIVE
    if (overwrite) then
      call mrtindex_index_build_recurs(directory,pattern,error)
    else
      call mrtindex_index_update_recurs(directory,pattern,error)
    endif
  else
    call mrtindex_file_update(indexfile,directory,pattern,overwrite,  &
      .true.,error)
  endif
  if (error)  return
  !
end subroutine mrtindex_index_update
!
recursive subroutine mrtindex_index_build_recurs(directory,pattern,error)
  use gildas_def
  use gbl_message
  use gkernel_interfaces
  use mrtindex_interfaces, except_this=>mrtindex_index_build_recurs
  use mrtindex_parse_types
  !---------------------------------------------------------------------
  ! @ private
  !  Build index files recursively from given directory
  !---------------------------------------------------------------------
  character(len=*),     intent(in)    :: directory  !
  type(user_pattern_t), intent(in)    :: pattern    ! Pattern for file to be indexed
  logical,              intent(inout) :: error      !
  ! Local
  character(len=*), parameter :: rname='BUILD>RECURSIVE'
  character(len=filename_length) :: indexfile
  integer(kind=4) :: ier
  !
  ! 1) Build local index file
  indexfile = trim(directory)//'/index.mrt'
  call mrtindex_file_update(indexfile,directory,pattern,.true.,.false.,error)
  if (error)  return
  !
  ! 2) Loop on sub-directories
  ier = gag_directory_exedir(directory,pattern,mrtindex_index_build_recurs)
  if (ier.ne.0) then
    call mrtindex_message(seve%e,rname,'Error looping in directory '//directory)
    error = .true.
    return
  endif
  !
end subroutine mrtindex_index_build_recurs
!
recursive subroutine mrtindex_index_update_recurs(directory,pattern,error)
  use gildas_def
  use gbl_message
  use gkernel_interfaces
  use mrtindex_interfaces, except_this=>mrtindex_index_update_recurs
  use mrtindex_parse_types
  !---------------------------------------------------------------------
  ! @ private
  !  Update index files recursively from given directory
  !---------------------------------------------------------------------
  character(len=*),     intent(in)    :: directory  !
  type(user_pattern_t), intent(in)    :: pattern    ! Pattern for file to be indexed
  logical,              intent(inout) :: error      !
  ! Local
  character(len=*), parameter :: rname='UPDATE>RECURSIVE'
  character(len=filename_length) :: indexfile
  integer(kind=4) :: ier
  !
  ! 1) Update/build local index file
  indexfile = trim(directory)//'/index.mrt'
  call mrtindex_file_update(indexfile,directory,pattern,.false.,.false.,error)
  if (error)  return
  !
  ! 2) Loop on sub-directories
  ier = gag_directory_exedir(directory,pattern,mrtindex_index_update_recurs)
  if (ier.ne.0) then
    call mrtindex_message(seve%e,rname,'Error looping in directory '//directory)
    error = .true.
    return
  endif
  !
end subroutine mrtindex_index_update_recurs
!
subroutine mrtindex_index_open(line,append,ix,error)
  use gildas_def
  use gbl_message
  use gkernel_interfaces
  use classic_api
  use mrtindex_interfaces, except_this=>mrtindex_index_open
  use mrtindex_types
  use mrtindex_parse_types
  !---------------------------------------------------------------------
  ! @ private
  !  Support routine for command
  !   INDEX OPEN|APPEND [DirName] [/FILE IndexFile] [/RECURSIVE]
  ! Load in memory an index file
  !---------------------------------------------------------------------
  character(len=*),          intent(in)    :: line    ! Input command line
  logical,                   intent(in)    :: append  ! Overwrite or append in memory?
  type(mrtindex_optimize_t), intent(inout) :: ix
  logical,                   intent(inout) :: error   ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='INDEX'
  character(len=message_length) :: mess
  character(len=filename_length) :: directory,indexfile
  type(user_pattern_t) :: pattern
  !
  call mrtindex_parse(line,directory,indexfile,pattern,error)
  if (error)  return
  !
  if (.not.append) then
    ! If OPEN (not APPEND), close all previous index files
    call mrtindex_ix_close(error)
    if (error)  return
    ix%next = 1  ! Forget all previous entries in IX
  endif
  !
  if (sic_present(2,0)) then  ! /RECURSIVE
    call mrtindex_open_recurs(directory,ix,error)
  else
    call mrtindex_open_one(indexfile,directory,ix,error)
  endif
  if (error)  return
  !
  call mrtindex_ix_sort(ix,error)
  if (error)  return
  !
  call mrtindex_ix_setnum(ix,error)
  if (error)  return
  !
  write(mess,'(I0,A)')  ix%next-1,' entries in Input indeX'
  call mrtindex_message(seve%i,rname,mess)
  !
end subroutine mrtindex_index_open
!
subroutine mrtindex_parse(line,dirname,filename,pattern,error)
  use gbl_message
  use gkernel_interfaces
  use mrtindex_interfaces, except_this=>mrtindex_parse
  use mrtindex_parse_types
  !---------------------------------------------------------------------
  ! @ private
  !  Parse command line of the form
  !   COMMAND YYY Dirname /OPT1 FileName /OPT3 Pattern
  !---------------------------------------------------------------------
  character(len=*),     intent(in)    :: line      ! Input command line
  character(len=*),     intent(out)   :: dirname   !
  character(len=*),     intent(out)   :: filename  !
  type(user_pattern_t), intent(out)   :: pattern   ! For files to be indexed
  logical,              intent(inout) :: error     ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='INDEX>PARSE'
  integer(kind=4) :: nd,nf
  character(len=filename_length) :: dirstring,filestring
  logical :: getpattern,getdate
  character(len=commandline_length) :: string
  integer(kind=4), parameter :: mlist=10
  !
  dirstring = '.' ! Default current working directory
  nd = 1
  call sic_ch(line,0,2,dirstring,nd,.false.,error)
  if (error)  return
  if (dirstring(nd:nd).eq.'/') then  ! ZZZ should use gag_separ for a more portable code
    dirstring(nd:nd) = ' '
    nd = nd-1
  endif
  call sic_resolve_env(dirstring,dirname)  ! Resolve ~ and other environment vars
  call sic_resolve_log(dirname)            ! Resolve sic logicals
  !
  ! /OPT1 FileName
  if (sic_present(1,0)) then
    call sic_ch(line,1,1,filestring,nf,.true.,error)
    if (error)  return
    call sic_parse_file(filestring,'','.mrt',filename)
  else
    ! Default 'index.mrt' in target directory
    if (dirname.eq.'.') then
      filename = 'index.mrt'
    else
      filename = trim(dirname)//'/index.mrt'
    endif
  endif
  !
  ! /PATTERN or /DATE
  getpattern = sic_present(3,0)
  getdate = sic_present(4,0)
  if (getpattern .and. getdate) then
    call mrtindex_message(seve%e,rname,'Incompatible options /PATTERN and /DATE')
    error = .true.
    return
  endif
  if (getpattern) then
    pattern%bylist = .false.
    call sic_ch(line,3,1,pattern%custom,nf,.true.,error)
    if (error)  return
  elseif (getdate) then
    pattern%bylist = .true.
    call datelist_decode(line,4,string,error)
    if (error)  return
    call sic_parse_listi4(rname,string,pattern%datelist,mlist,error)
    if (error)  return
    if (any(pattern%datelist%i3(1:pattern%datelist%nlist).ne.1)) then
      call mrtindex_message(seve%e,rname,  &
        'Step other than 1 in /DATE list(s) not implemented')
      error = .true.
      return
    endif
  else
    pattern%bylist = .false.
    pattern%custom = imbfits_name('*')
  endif
  !
end subroutine mrtindex_parse
!
recursive subroutine mrtindex_open_recurs(directory,ix,error)
  use gildas_def
  use gbl_message
  use gkernel_interfaces
  use mrtindex_interfaces, except_this=>mrtindex_open_recurs
  use mrtindex_types
  use mrtindex_parse_types
  !---------------------------------------------------------------------
  ! @ private
  !  Open and load in IX index files recursively found in given
  ! directory
  !---------------------------------------------------------------------
  character(len=*),          intent(in)    :: directory  !
  type(mrtindex_optimize_t), intent(inout) :: ix
  logical,                   intent(inout) :: error      !
  ! Local
  character(len=*), parameter :: rname='OPEN>RECURSIVE'
  character(len=filename_length) :: indexfile
  integer(kind=4) :: nc,ier
  !
  ! 1) Load local index file
  indexfile = trim(directory)//'/index.mrt'
  nc = len_trim(indexfile)
  if (gag_inquire(indexfile,nc).eq.0) then
    call mrtindex_open_one(indexfile,directory,ix,error)
    if (error)  return
  endif
  !
  ! 2) Loop on sub-directories
  ier = gag_directory_exedir(directory,ix,mrtindex_open_recurs)
  if (ier.ne.0) then
    call mrtindex_message(seve%e,rname,'Error looping in directory '//directory)
    error = .true.
    return
  endif
  !
end subroutine mrtindex_open_recurs
!
subroutine mrtindex_index_output(line,error)
  use gildas_def
  use gbl_message
  use mrtindex_dependencies_interfaces
  use mrtindex_interfaces, except_this=>mrtindex_index_output
  use mrtindex_vars
  use mrtindex_parse_types
  !---------------------------------------------------------------------
  ! @ private
  !  Create the new INDEX OUTPUT file, used for saving the calibrated
  ! entries (default is to save them in the index they come from).
  !  Note that the associated directory is left undeclared, which may
  ! mean more or less troubles...
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: line   ! Input command line
  logical,          intent(inout) :: error  ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='INDEX>OUTPUT'
  character(len=filename_length) :: directory,indexfile
  type(user_pattern_t) :: pattern
  !
  call mrtindex_parse(line,directory,indexfile,pattern,error)
  if (error)  return
  !
  call mrtindex_file_new(indexfile,.false.,directory,ox_fileid,error)
  if (error)  return
  !
  ! Do not keep opened this file, we may be building|updating a large
  ! number
  call mrtindex_file_close(ix_files(ox_fileid),error)
  if (error)  continue
  !
end subroutine mrtindex_index_output
!
subroutine mrtindex_index_convert(line,ix,error)
  use gildas_def
  use gbl_message
  use gkernel_interfaces
  use classic_api
  use mrtindex_interfaces, except_this=>mrtindex_index_convert
  use mrtindex_parse_types
  use mrtindex_vars
  !---------------------------------------------------------------------
  ! @ private
  ! Support routine for command
  !   INDEX CONVERT [/FILE IndexFile]
  ! Convert an index file to the latest index version (if relevant),
  ! most likely before trying to write into it afterwards.
  !---------------------------------------------------------------------
  character(len=*),          intent(in)    :: line    ! Input command line
  type(mrtindex_optimize_t), intent(inout) :: ix      !
  logical,                   intent(inout) :: error   ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='INDEX'
  character(len=message_length) :: mess
  character(len=filename_length) :: imbdir,indexdir,indexfile,oldfile,indexbase,oldbase
  character(len=12) :: ext
  type(user_pattern_t) :: pattern
  integer(kind=4) :: nc,ier,fileid
  type(mrtindex_optimize_t) :: idx
  !
  call mrtindex_parse(line,imbdir,indexfile,pattern,error)
  if (error)  return
  !
  nc = len_trim(indexfile)
  if (gag_inquire(indexfile,nc).ne.0) then
    call mrtindex_message(seve%w,rname,  &
      'No such file '//indexfile(1:nc)//', nothing done')
    return
  endif
  !
  ! Use general API. But this needs to reset the input index.
  call mrtindex_ix_close(error)
  if (error)  return
  ix%next = 1  ! Forget all previous entries in IX
  !
  ! Check for index version (in the CLASSIC sense). Play fast (i.e.
  ! avoid loading the whole index if not necessary):
  call mrtindex_file_old(indexfile,imbdir,.false.,fileid,error)
  if (error)  return
  if (ix_files(fileid)%desc%vind.eq.mrtcal_indx_version) then
    write(mess,'(3a,i0,a)')  'File ',indexfile(1:nc),  &
      ' is already at latest version (v',ix_files(fileid)%desc%vind,')'
    call mrtindex_message(seve%i,rname,mess)
    return
  endif
  !
  ! Rename old file
  call mrtindex_ix_close(error)
  if (error)  return
  call sic_parse_name(indexfile,indexbase,oext=ext,odir=indexdir)
  write(oldbase,'(a,a,i0)')  trim(indexbase),'-v',ix_files(fileid)%desc%vind
  call sic_parse_file(oldbase,indexdir,'.'//ext,oldfile)
  ier = gag_filrename(indexfile,oldfile)
  call mrtindex_message(seve%i,rname,'Old index file backup''ed to '//trim(oldfile))
  !
  ! Load in custom index
  call mrtindex_open_one(oldfile,imbdir,idx,error)
  if (error)  return
  write(mess,'(a,i0,a)')  'Converting an index of ',idx%next-1,' entries'
  call mrtindex_message(seve%i,rname,mess)
  !
  ! Convert
  call mrtindex_copy_index(indexfile,idx,error)
  if (error)  return
  !
  ! All done
  call deallocate_mrtoptimize(idx,error)
  if (error)  return
end subroutine mrtindex_index_convert
!
subroutine mrtindex_ix_sort(ix,error)
  use gkernel_interfaces
  use mrtindex_interfaces, except_this=>mrtindex_ix_sort
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  !  Sort the whole IX. Can be useful if e.g. 1) the index.mrt was not
  ! sorted, or 2) if we merged (APPEND or /RECURSIVE) several index
  ! files
  !---------------------------------------------------------------------
  type(mrtindex_optimize_t), intent(inout) :: ix
  logical,                   intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='IX>SORT'
  integer(kind=entry_length) :: ient,ielem
  type(mrtindex_optimize_t) :: tmpx
  !
  ! Need a copy of IX, can not sort inplace
  call mrtindex_optimize_to_optimize(ix,tmpx,error)
  if (error)  return
  !
  call mrtindex_optimize_setsort(tmpx,error)
  if (error)  goto 100
  !
  ix%next = 1
  do ient=1,tmpx%next-1
    ielem = tmpx%sort(ient)
    call mrtindex_optimize_to_optimize(tmpx,ielem,ix,error)
    if (error)  goto 100
  enddo
  !
  ! Need to renumber ix%mnum which is contiguous bu definition
  do ient=1,ix%next-1
    ix%mnum(ient) = ient
  enddo
  !
100 continue
  call deallocate_mrtoptimize(tmpx,error)
  if (error)  return
  !
end subroutine mrtindex_ix_sort
!
subroutine mrtindex_ix_setnum(ix,error)
  use classic_api
  use mrtindex_interfaces, except_this=>mrtindex_ix_setnum
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(mrtindex_optimize_t), intent(inout) :: ix
  logical,                   intent(inout) :: error
  ! Local
  integer(kind=entry_length) :: ient,iprev,num
  !
  if (ix%next.le.1)  return
  !
  num = 1
  iprev = 1
  ix%num(1) = num
  ix%islast(1) = .false.
  !
  ! This works because IX has been sorted with those criterions
  do ient=2,ix%next-1
    if (ix%dobs(ient).ne.ix%dobs(iprev) .or.  &
        ix%scan(ient).ne.ix%scan(iprev) .or.  &
        ix%backend(ient).ne.ix%backend(iprev)) then
      num = num+1
      iprev = ient
      ix%islast(ient-1) = .true.
    endif
    ix%num(ient) = num
    ix%islast(ient) = .false.
  enddo
  !
  ix%islast(ix%next-1) = .true.
  !
end subroutine mrtindex_ix_setnum
!
subroutine mrtindex_ix_close(error)
  use mrtindex_interfaces, except_this=>mrtindex_ix_close
  use mrtindex_vars
  !---------------------------------------------------------------------
  ! @ private
  ! Close all index files used in IX
  !---------------------------------------------------------------------
  logical, intent(inout) :: error
  ! Local
  integer(kind=4) :: ifile
  !
  do ifile=1,ix_ndir
    if (ix_files(ifile)%lun.ne.0) then
      call mrtindex_file_close(ix_files(ifile),error)
      ! if (error)  continue
    endif
  enddo
  !
  ix_ndir = 0
  ox_fileid = 0  ! Forget the previous INDEX OUTPUT, because it is IN-OUT
  !
end subroutine mrtindex_ix_close
!
subroutine reallocate_index_ibuf(file,error)
  use classic_api
  use mrtindex_interfaces, except_this=>reallocate_index_ibuf
  use mrtindex_vars
  !---------------------------------------------------------------------
  ! @ private
  !  Reallocate the Input working buffers according to given file
  !---------------------------------------------------------------------
  type(classic_file_t), intent(in)    :: file   !
  logical,              intent(inout) :: error  !
  !
  call reallocate_recordbuf(ibufobs,file%desc%reclen,error)
  if (error)  return
  call reallocate_recordbuf(ibufbi,file%desc%reclen,error)
  if (error)  return
  !
end subroutine reallocate_index_ibuf
!
subroutine reallocate_index_obuf(file,error)
  use classic_api
  use mrtindex_interfaces, except_this=>reallocate_index_obuf
  use mrtindex_vars
  !---------------------------------------------------------------------
  ! @ private
  !  Reallocate the Ouput working buffers according to given file
  !---------------------------------------------------------------------
  type(classic_file_t), intent(in)    :: file   !
  logical,              intent(inout) :: error  !
  !
  call reallocate_recordbuf(obufobs,file%desc%reclen,error)
  if (error)  return
  call reallocate_recordbuf(obufbi,file%desc%reclen,error)
  if (error)  return
  !
end subroutine reallocate_index_obuf
