!--- Entry subroutines -------------------------------------------------
!
subroutine classic_entry_init(file,xnum,maxsec,version,full,ed,error)
  use classic_interfaces, except_this=>classic_entry_init
  use classic_params
  use classic_types
  use classic_vars
  !---------------------------------------------------------------------
  ! @ public
  ! Initialize the Entry for a new observation
  !---------------------------------------------------------------------
  type(classic_file_t),       intent(inout) :: file     !
  integer(kind=entry_length), intent(in)    :: xnum     ! The corresponding Entry number
  integer(kind=4),            intent(in)    :: maxsec   ! Maximum number of sections the ed can describe
  integer(kind=4),            intent(in)    :: version  ! Observation version
  logical,                    intent(out)   :: full     ! File is full?
  type(classic_entrydesc_t),  intent(out)   :: ed       ! The new Entry Descriptor
  logical,                    intent(inout) :: error    ! Logical error flag
  !
  ! Allocate room for the entry index
  call entryindex_new(file,xnum,full,error)
  if (full.or.error)  return
  !
  ! Initialize the entry descriptor
  if (file%desc%version.eq.1) then
    call entrydesc_init_v1(ed,xnum,maxsec,version,error)
  else
    call entrydesc_init_v2(ed,xnum,maxsec,version,error)
  endif
  !
  ! Something else?
  !
end subroutine classic_entry_init
!
subroutine classic_entry_section_read(isec,lsec,sec,ed,buf,error)
  use gbl_message
  use classic_interfaces, except_this=>classic_entry_section_read
  use classic_params
  use classic_types
  !---------------------------------------------------------------------
  ! @ public
  !  Read a section from an entry
  !---------------------------------------------------------------------
  integer(kind=4),           intent(in)    :: isec    ! Section id
  integer(kind=data_length), intent(inout) :: lsec    ! Length of section (updated on return)
  integer(kind=4),           intent(inout) :: sec(*)  ! Section buffer
  type(classic_entrydesc_t), intent(in)    :: ed      ! The Entry Descriptor
  type(classic_recordbuf_t), intent(inout) :: buf     ! Working buffer
  logical,                   intent(inout) :: error   ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='RSEC'
  logical :: found
  integer(kind=4) :: jsec
  integer(kind=data_length) :: psec,i
  character(len=message_length) :: mess
  !
  call classic_entrydesc_secfind_one(ed,isec,found,jsec)
  !
  if (.not.found) then ! No section found
    write(mess,'(A,I0)') 'Absent section ',isec
    call classic_message(seve%w,rname,mess)
    error = .true.
    return
  endif
  !
  psec = lsec  ! Input length of buffer
  !
  ! lsec is updated to actual length read from disk.
  ! If the input buffer is too small, fill the buffer with only the
  ! beginning of the section. It is the responsibility of the caller
  ! to know that he may have missed some words (no warning here).
  lsec = min(lsec,ed%secleng(jsec))
  call recordbuf_read(ed%secaddr(jsec),lsec,sec,buf,error)
  !
  ! Fill end of buffer with zeroes
  do i=lsec+1,psec
    sec(i) = 0
  enddo
  !
end subroutine classic_entry_section_read
!
subroutine classic_entry_data_read(array,ndata,ed,buf,error)
  use gbl_message
  use classic_interfaces, except_this=>classic_entry_data_read
  use classic_params
  use classic_types
  !---------------------------------------------------------------------
  ! @ public-mandatory (because symbol is used elsewhere)
  !  Read the 'data' block from the entry
  !---------------------------------------------------------------------
  integer(kind=data_length), intent(inout) :: ndata         ! Length in 32_bit words (updated)
  real(kind=4),              intent(out)   :: array(ndata)  ! Data itself
  type(classic_entrydesc_t), intent(in)    :: ed            ! The Entry Descriptor
  type(classic_recordbuf_t), intent(inout) :: buf           ! Working buffer
  logical,                   intent(inout) :: error         ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='RDATA'
  character(len=message_length) :: mess
  !
  if (ndata.lt.ed%ldata) then
    ! Data array in memory is too short, it makes no sense to truncate implicitely
    write(mess,'(A,I0,A,I0,A)')  &
      'Data buffer too short in memory (',ndata,'<',ed%ldata,')'
    call classic_message(seve%e,rname,mess)
    error = .true.
    return
  endif
  !
  ndata = ed%ldata
  call recordbuf_read(ed%adata,ndata,array,buf,error)
  if (error)  return
  !
end subroutine classic_entry_data_read
!
subroutine classic_entry_data_readsub(array,ndata,first,last,ed,buf,error)
  use classic_interfaces, except_this=>classic_entry_data_readsub
  use classic_params
  use classic_types
  !---------------------------------------------------------------------
  ! @ public
  !  Read a subset of the 'data' block from the entry
  !---------------------------------------------------------------------
  integer(kind=data_length), intent(inout) :: ndata         ! Length in 32_bit words (updated)
  real(kind=4),              intent(out)   :: array(ndata)  ! Data itself
  integer(kind=data_length), intent(in)    :: first         ! First data word to read
  integer(kind=data_length), intent(in)    :: last          ! Last data word to read
  type(classic_entrydesc_t), intent(in)    :: ed            ! The Entry Descriptor
  type(classic_recordbuf_t), intent(inout) :: buf           ! Working buffer
  logical,                   intent(inout) :: error         ! Logical error flag
  ! Local
  integer(kind=data_length) :: first2,last2,istart
  !
  ! Normal data: implicit truncate to appropriate size
  first2 = max(first,1_8)     ! Corrected first word (can not read before first data
                              ! word): is this really needed?
  last2 = min(last,ed%ldata)  ! Corrected last word (can not read beyond last data
                              ! word): is this really needed?
  istart = ed%adata+first2-1  ! First word address in entry
  ndata = last2-first2+1      ! Number of data words to read
  !
  call recordbuf_read(istart,ndata,array,buf,error)
  if (error)  return
  !
end subroutine classic_entry_data_readsub
!
subroutine classic_entry_section_add(isec,lsec,sec,ed,buf,error)
  use gbl_message
  use classic_interfaces, except_this=>classic_entry_section_add
  use classic_params
  use classic_types
  !---------------------------------------------------------------------
  ! @ public
  !  Add a new section to an entry. Must not exist before.
  !---------------------------------------------------------------------
  integer(kind=4),           intent(in)    :: isec    ! Section id
  integer(kind=data_length), intent(in)    :: lsec    ! Length of section
  integer(kind=4),           intent(in)    :: sec(*)  ! Section buffer
  type(classic_entrydesc_t), intent(inout) :: ed      ! The Entry Descriptor
  type(classic_recordbuf_t), intent(inout) :: buf     ! Working buffer
  logical,                   intent(inout) :: error   ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='WSEC'
  logical :: found
  integer(kind=4) :: jsec
  character(len=message_length) :: mess
  !
  ! Search for section id 'isec'
  call classic_entrydesc_secfind_one(ed,isec,found,jsec)
  if (found) then
    write(mess,'(A,I0,A)') 'Section ',isec,' already written'
    call classic_message(seve%e,rname,mess)
    error = .true.
    return
  endif
  !
  call entrydesc_section_add(ed,isec,lsec,error)
  if (error) then
    write(mess,'(A,I6)') 'Could not add section ',isec
    call classic_message(seve%e,rname,mess)
    return
  endif
  !
  call recordbuf_write(ed%secaddr(ed%nsec),lsec,sec,buf,error)
  if (error) return
  !
  ! Update nextrec/nextword? Not here, this will be done once
  ! when finalizing the observation (classic_entrydesc_write)
  !
end subroutine classic_entry_section_add
!
subroutine classic_entry_section_update(isec,lsec,sec,ed,buf,error)
  use gbl_message
  use classic_interfaces, except_this=>classic_entry_section_update
  use classic_params
  use classic_types
  !---------------------------------------------------------------------
  ! @ public
  !  Update an old section in an entry. Must exist before.
  !---------------------------------------------------------------------
  integer(kind=4),           intent(in)    :: isec    ! Section id
  integer(kind=data_length), intent(in)    :: lsec    ! Length of section
  integer(kind=4),           intent(in)    :: sec(*)  ! Section buffer
  type(classic_entrydesc_t), intent(in)    :: ed      ! The Entry Descriptor
  type(classic_recordbuf_t), intent(inout) :: buf     ! Working buffer
  logical,                   intent(inout) :: error   ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='WSEC'
  logical :: found
  integer(kind=4) :: jsec
  character(len=message_length) :: mess
  !
  ! Search for section id 'isec'
  call classic_entrydesc_secfind_one(ed,isec,found,jsec)
  if (.not.found) then
    write(mess,'(A,I0)') 'Absent section ',isec
    call classic_message(seve%e,rname,mess)
    error = .true.
    return
  endif
  !
  ! Sanity checks
  if (lsec.gt.ed%secleng(jsec)) then
    write(mess,'(A,I0)') 'Insufficient room available for section ',isec
    call classic_message(seve%e,rname,mess)
    error = .true.
    return
  endif
  !
  call recordbuf_write(ed%secaddr(jsec),lsec,sec,buf,error)
  if (error) return
  !
  ! No need to update nextrec/nextword: we just updated data which already
  ! existed, and we did not exceeded its old size
  !
end subroutine classic_entry_section_update
!
subroutine classic_entry_data_add(array,ndata,ed,buf,error)
  use classic_interfaces, except_this=>classic_entry_data_add
  use classic_params
  use classic_types
  !---------------------------------------------------------------------
  ! @ public
  !  Write the 'data' block to the entry. Must not exist before.
  !---------------------------------------------------------------------
  integer(kind=data_length), intent(in)    :: ndata         ! Length in 32_bit words
  real(kind=4),              intent(in)    :: array(ndata)  ! Data itself
  type(classic_entrydesc_t), intent(inout) :: ed            ! The Entry Descriptor
  type(classic_recordbuf_t), intent(inout) :: buf           ! Working buffer
  logical,                   intent(inout) :: error         ! Logical error flag
  ! Local
  integer(kind=data_length) :: addr
  !
  addr = ed%nword+1
  !
  call recordbuf_write(addr,ndata,array,buf,error)
  if (error)  return
  !
  ed%ldata = ndata
  ed%adata = addr
  ed%nword = ed%nword+ndata
  !
  ! Update nextrec/nextword? Not here, this will be done once
  ! when finalizing the observation (classic_entrydesc_write)
  !
end subroutine classic_entry_data_add
!
subroutine classic_entry_data_update(array,ndata,ed,buf,error)
  use gbl_message
  use classic_interfaces, except_this=>classic_entry_data_update
  use classic_params
  use classic_types
  !---------------------------------------------------------------------
  ! @ public
  !  Update the 'data' block to the entry. Must exist before.
  !---------------------------------------------------------------------
  integer(kind=data_length), intent(in)    :: ndata         ! Length in 32_bit words
  real(kind=4),              intent(in)    :: array(ndata)  ! Data itself
  type(classic_entrydesc_t), intent(inout) :: ed            ! The Entry Descriptor
  type(classic_recordbuf_t), intent(inout) :: buf           ! Working buffer
  logical,                   intent(inout) :: error         ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='WDATA'
  integer(kind=data_length) :: addr
  !
  if (ndata.gt.ed%ldata) then
    call classic_message(seve%e,rname,  &
      'Insufficient space available for data array')
    error = .true.
    return
  endif
  !
  addr = ed%adata
  !
  call recordbuf_write(addr,ndata,array,buf,error)
  if (error)  return
  !
  ed%ldata = ndata
  ! ed%adata = data location unchanged
  ! ed%nword = entry size unchanged even if we use less words for data
  !
end subroutine classic_entry_data_update
!
subroutine classic_entry_close(file,buf,error)
  use classic_interfaces, except_this=>classic_entry_close
  use classic_types
  !---------------------------------------------------------------------
  ! @ public
  !  Perform the needed operations when all the entry elements have
  ! been written.
  !---------------------------------------------------------------------
  type(classic_file_t),      intent(inout) :: file   !
  type(classic_recordbuf_t), intent(in)    :: buf    !
  logical,                   intent(inout) :: error  !
  !
  if (file%desc%version.eq.1 .and. file%desc%nextword.ne.1) then
    ! If V1, realign free area at beginning of the next record
    file%desc%nextrec = file%desc%nextrec+1
    file%desc%nextword = 1
  endif
  !
  call recordbuf_close(buf,error)  ! Flush the working buffer
  if (error) return
  !
end subroutine classic_entry_close
!
!--- Entry Descriptor subroutines --------------------------------------
!
subroutine entrydesc_init_v1(ed,xnum,maxsec,version,error)
  use gbl_message
  use classic_interfaces, except_this=>entrydesc_init_v1
  use classic_params
  use classic_types
  use classic_vars
  !---------------------------------------------------------------------
  ! @ private
  !   Initialize the Entry Descriptor of a new observation, suited for
  ! a version 1 file
  !---------------------------------------------------------------------
  type(classic_entrydesc_t),  intent(out)   :: ed       ! The new Entry Descriptor
  integer(kind=entry_length), intent(in)    :: xnum     ! The corresponding Entry number
  integer(kind=4),            intent(in)    :: maxsec   ! Maximum number of sections the ed can describe
  integer(kind=4),            intent(in)    :: version  ! Observation version
  logical,                    intent(inout) :: error    ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='ENTRYDESC_INIT'
  !
  if (version.ne.classic_vobs_v1) then
    call classic_message(seve%e,rname,'Wrong observation version number for V1 entry')
    error = .true.
    return
  endif
  !
  ed%msec = min(maxsec,classic_maxsec)  ! Decrease to what the memory can hold
  !
  ed%code  = code_entry
  ed%nword = entrydescv1_nw1+entrydescv1_nw2*ed%msec
  ed%adata = 0
  ed%ldata = 0
  ed%nsec = 0
  ed%xnum = xnum
  ed%version = classic_vobs_v1
  !
  ! Do not init these arrays: useless
  ! ed%seciden(:) = 0
  ! ed%secleng(:) = 0
  ! ed%secaddr(:) = 0
  !
end subroutine entrydesc_init_v1
!
subroutine entrydesc_init_v2(ed,xnum,maxsec,version,error)
  use classic_interfaces, except_this=>entrydesc_init_v2
  use classic_params
  use classic_types
  use classic_vars
  !---------------------------------------------------------------------
  ! @ private
  !   Initialize the Entry Descriptor of a new observation, suited for
  ! a version 2 file
  !---------------------------------------------------------------------
  type(classic_entrydesc_t),  intent(out)   :: ed       ! The new Entry Descriptor
  integer(kind=entry_length), intent(in)    :: xnum     ! The corresponding Entry number
  integer(kind=4),            intent(in)    :: maxsec   ! Maximum number of sections the ed can describe
  integer(kind=4),            intent(in)    :: version  ! Observation version
  logical,                    intent(inout) :: error    ! Logical error flag
  !
  ed%msec = min(maxsec,classic_maxsec)  ! Decrease to what the memory can hold
  !
  ed%code    = code_entry
  ed%version = version
  ed%nword   = entrydescv2_nw1+entrydescv2_nw2*ed%msec  ! Number of INTEGER*4 words
  ed%adata   = 0
  ed%ldata   = 0
  ed%nsec    = 0
  ed%xnum    = xnum
  !
  ! Do not init these arrays: useless
  ! ed%seciden(:) = 0
  ! ed%secleng(:) = 0
  ! ed%secaddr(:) = 0
  !
end subroutine entrydesc_init_v2
!
subroutine classic_entrydesc_read(file,buf,ed,error)
  use classic_interfaces, except_this=>classic_entrydesc_read
  use classic_types
  use classic_vars
  !---------------------------------------------------------------------
  ! @ public
  !   Read the Entry Descriptor
  !---------------------------------------------------------------------
  type(classic_file_t),      intent(in)    :: file   !
  type(classic_recordbuf_t), intent(inout) :: buf    ! Working buffer
  type(classic_entrydesc_t), intent(inout) :: ed     ! The Entry Descriptor read
  logical,                   intent(inout) :: error  ! Logical error flag
  !
  if (file%desc%version.eq.1) then
    call entrydesc_read_v1(file,buf,ed,error)
  else
    call entrydesc_read_v2(file,buf,ed,error)
  endif
  !
end subroutine classic_entrydesc_read
!
subroutine entrydesc_read_v1(file,buf,edv2,error)
  use gbl_message
  use gkernel_interfaces
  use classic_interfaces, except_this=>entrydesc_read_v1
  use classic_params
  use classic_types
  use classic_vars
  !---------------------------------------------------------------------
  ! @ private
  !   Read the Entry Descriptor, Version 1
  !---------------------------------------------------------------------
  type(classic_file_t),      intent(in)    :: file   !
  type(classic_recordbuf_t), intent(inout) :: buf    ! Working buffer
  type(classic_entrydesc_t), intent(inout) :: edv2   ! The Entry Descriptor read
  logical,                   intent(inout) :: error  ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='ENTRYDESC_READ'
  integer(kind=4) :: len4
  integer(kind=4) :: ih(entrydescv1_nw1),isec(classic_maxsec)  ! Intermediate buffers
  integer(kind=data_length) :: addr,len8
  character(len=message_length) :: mess
  type(entrydesc_v1_t) :: ed
#if !defined(FORTRAN2003_PP) /* Fortran 2003 Procedure Pointers */
  integer(kind=address_length) :: adi4
  integer(kind=1) :: membyt(8)
  !
  adi4 = bytpnt(file%conv%read%i4,membyt)
#endif
  !
  error = .false.
  !
  ! Read the beginning of the Entry Descriptor
  addr = 1
  len8 = entrydescv1_nw1  ! The first words of the Entry Descriptor
  len4 = len8
  if (file%conv%code.le.2) then
    ! Read directly the 9 I*4 values
    call recordbuf_read(addr,len8,ed%code,buf,error)
  else
    ! Can not convert inplace (not Fortran standard!), use an intermediate buffer (ih)
    call recordbuf_read(addr,len8,ih,buf,error)
    ed%code = ih(1)
#if defined(FORTRAN2003_PP) /* Fortran 2003 Procedure Pointers */
    call file%conv%read%i4(ih(2),ed%nbloc,len4-1)
#else
    call convert_buf(membyt(adi4),ih(2),ed%nbloc,len4-1)
#endif
  endif
  if (error) return
  ed%msec = 0  ! Not in data. Play safe.
  !
  if (ed%code.ne.code_entry) then  ! Check this is the start of an entry (beginning of Entry Descriptor)
     error = .true.
     write(mess,'(A,I0,A,I0,A,I0)')  &
       'Attempt to read non-standard entry at record ',buf%rstart,': code ',  &
       ed%code,' instead of code ',code_entry
     call classic_message(seve%e,rname,mess)
     return
  endif
  !
  ! Read the Header Section Description
  buf%nrec = ed%nbloc
  addr = addr+len8
  len8 = ed%nsec  ! Number of sections
  len4 = len8
  !
  if (file%conv%code.le.2) then
    call recordbuf_read(addr,len8,ed%seciden,buf,error)  ! SecIden: section numbers
  else
    call recordbuf_read(addr,len8,isec,buf,error)
#if defined(FORTRAN2003_PP) /* Fortran 2003 Procedure Pointers */
    call file%conv%read%i4(isec,ed%seciden,len4)
#else
    call convert_buf(membyt(adi4),isec,ed%seciden,len4)
#endif
  endif
  if (error) return
  addr = addr+len8
  !
  if (file%conv%code.le.2) then
    call recordbuf_read(addr,len8,ed%secleng,buf,error)  ! SecLeng: section lengths
  else
    call recordbuf_read(addr,len8,isec,buf,error)
#if defined(FORTRAN2003_PP) /* Fortran 2003 Procedure Pointers */
    call file%conv%read%i4(isec,ed%secleng,len4)
#else
    call convert_buf(membyt(adi4),isec,ed%secleng,len4)
#endif
  endif
  if (error) return
  addr = addr+len8
  !
  if (file%conv%code.le.2) then
    call recordbuf_read(addr,len8,ed%secaddr,buf,error)  ! SecAddr: section address in observation
  else
    call recordbuf_read(addr,len8,isec,buf,error)
#if defined(FORTRAN2003_PP) /* Fortran 2003 Procedure Pointers */
    call file%conv%read%i4(isec,ed%secaddr,len4)
#else
    call convert_buf(membyt(adi4),isec,ed%secaddr,len4)
#endif
  endif
  if (error) return
  ! addr = addr+len8
  !
  ! Fill the V2 memory structure
  call entrydesc_v1tov2(ed,edv2,error)
  if (error)  return
  !
end subroutine entrydesc_read_v1
!
subroutine entrydesc_v1tov2(ed1,ed2,error)
  use classic_interfaces, except_this=>entrydesc_v1tov2
  use classic_types
  !---------------------------------------------------------------------
  ! @ private
  ! Convert a Version 1 Entry Descriptor to a Version 2
  !---------------------------------------------------------------------
  type(entrydesc_v1_t),      intent(in)    :: ed1    ! Version 1
  type(classic_entrydesc_t), intent(out)   :: ed2    ! Version 2
  logical,                   intent(inout) :: error  ! Logical error flag
  !
  ed2%code    = ed1%code
  ed2%version = classic_vobs_v1
  ed2%nword   = ed1%nword
  ed2%adata   = ed1%adata
  ed2%ldata   = ed1%ldata
  ed2%nsec    = ed1%nsec
  ed2%xnum    = ed1%xnum
  ed2%msec    = ed1%msec
  ed2%seciden(1:ed1%nsec) = ed1%seciden(1:ed1%nsec)
  ed2%secleng(1:ed1%nsec) = ed1%secleng(1:ed1%nsec)
  ed2%secaddr(1:ed1%nsec) = ed1%secaddr(1:ed1%nsec)
  !
end subroutine entrydesc_v1tov2
!
subroutine entrydesc_read_v2(file,buf,ed,error)
  use gbl_message
  use gkernel_interfaces
  use classic_interfaces, except_this=>entrydesc_read_v2
  use classic_params
  use classic_types
  use classic_vars
  !---------------------------------------------------------------------
  ! @ private
  !   Read the Entry Descriptor, Version 2
  !---------------------------------------------------------------------
  type(classic_file_t),      intent(in)    :: file   !
  type(classic_recordbuf_t), intent(inout) :: buf    ! Working buffer
  type(classic_entrydesc_t), intent(inout) :: ed     ! The Entry Descriptor read
  logical,                   intent(inout) :: error  ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='ENTRYDESC_READ'
  integer(kind=4) :: len4
  integer(kind=4) :: ih(entrydescv2_nw1)    ! Intermediate buffer for the entry descriptor
  integer(kind=4) :: isec4(classic_maxsec)  ! Intermediate buffer for seciden
  integer(kind=8) :: isec8(classic_maxsec)  ! Intermediate buffer for secaddr/secleng
  integer(kind=data_length) :: addr,len8
  character(len=message_length) :: mess
#if !defined(FORTRAN2003_PP) /* Fortran 2003 Procedure Pointers */
  integer(kind=address_length) :: adi4,adi8
  integer(kind=1) :: membyt(8)
  !
  adi4 = bytpnt(file%conv%read%i4,membyt)
  adi8 = bytpnt(file%conv%read%i8,membyt)
#endif
  !
  error = .false.
  !
  ! Read the beginning of the Entry Descriptor
  addr = 1
  len8 = entrydescv2_nw1  ! The first words of the Entry Descriptor
  len4 = len8
  !
  ! Can not convert inplace (not Fortran standard!), use an intermediate buffer (ih)
  ! Tricky part (part 1): we have to read the first 'entrydescv2_nw1' elements in
  ! the entry descriptor, but they can overlap 2 records. 'buf%nrec' should reflect
  ! this, else 'recordbuf_read' will stop with an error.
  buf%nrec = (buf%wstart+entrydescv2_nw1-2)/file%desc%reclen+1
  call recordbuf_read(addr,len8,ih,buf,error)
  if (error) return
  !
  ed%code = ih(1)
  if (ed%code.ne.code_entry) then  ! Check this is the start of an entry (beginning of Entry Descriptor)
     error = .true.
     write(mess,'(A,I0,A,I0,A,I0)')  &
       'Attempt to read non-standard entry at record ',buf%rstart,': code ',  &
       ed%code,' instead of code ',code_entry
     call classic_message(seve%e,rname,mess)
     return
  endif
  !
#if defined(FORTRAN2003_PP) /* Fortran 2003 Procedure Pointers */
  call file%conv%read%i4(ih(2),ed%version,2)
  call file%conv%read%i8(ih(4),ed%nword,  4)
#else
  call convert_buf(membyt(adi4),ih(2),ed%version,2)
  call convert_buf(membyt(adi8),ih(4),ed%nword,  4)
#endif
  ed%msec = 0  ! Not in data. Play safe.
  ! Tricky part (part 2): Now we know the exact size of the entry, use correct
  ! nrec.
  buf%nrec = (buf%wstart+ed%nword-2)/file%desc%reclen+1
  !
  ! Read the Header Section Description
  addr = addr+len8
  len8 = ed%nsec  ! Number of sections
  len4 = len8
  !
  ! Read the 'seciden' INTEGER*4 array
  if (file%conv%code.le.2) then
    call recordbuf_read(addr,len8,ed%seciden,buf,error)  ! SecIden: section numbers
  else
    call recordbuf_read(addr,len8,isec4,buf,error)
#if defined(FORTRAN2003_PP) /* Fortran 2003 Procedure Pointers */
    call file%conv%read%i4(isec4,ed%seciden,len4)
#else
    call convert_buf(membyt(adi4),isec4,ed%seciden,len4)
#endif
  endif
  if (error) return
  addr = addr+len8
  !
  ! Read the 'secleng' INTEGER*8 array
  if (file%conv%code.le.2) then
    call recordbuf_read(addr,2*len8,ed%secleng,buf,error)  ! SecLeng: section lengths
  else
    call recordbuf_read(addr,2*len8,isec8,buf,error)
#if defined(FORTRAN2003_PP) /* Fortran 2003 Procedure Pointers */
    call file%conv%read%i8(isec8,ed%secleng,len4)
#else
    call convert_buf(membyt(adi8),isec8,ed%secleng,len4)
#endif
  endif
  if (error) return
  addr = addr+2*len8
  !
  ! Read the 'secaddr' INTEGER*8 array
  if (file%conv%code.le.2) then
    call recordbuf_read(addr,2*len8,ed%secaddr,buf,error)  ! SecAddr: section address in observation
  else
    call recordbuf_read(addr,2*len8,isec8,buf,error)
#if defined(FORTRAN2003_PP) /* Fortran 2003 Procedure Pointers */
    call file%conv%read%i8(isec8,ed%secaddr,len4)
#else
    call convert_buf(membyt(adi8),isec8,ed%secaddr,len4)
#endif
  endif
  if (error) return
  ! addr = addr+2*len8
  !
end subroutine entrydesc_read_v2
!
subroutine classic_entrydesc_write(file,buf,ed,error)
  use classic_interfaces, except_this=>classic_entrydesc_write
  use classic_types
  use classic_vars
  !---------------------------------------------------------------------
  ! @ public
  !   Write the input Entry Descriptor
  !---------------------------------------------------------------------
  type(classic_file_t),      intent(inout) :: file   !
  type(classic_recordbuf_t), intent(inout) :: buf    ! Working buffer
  type(classic_entrydesc_t), intent(in)    :: ed     ! The Entry Descriptor to be written
  logical,                   intent(inout) :: error  ! Logical error flag
  !
! write (*,'(A,I0,1X,I0)') '>>> Write entry descriptor @ ',buf%rstart,buf%wstart
  if (file%desc%version.eq.1) then
    call entrydesc_write_v1(file,buf,ed,error)
  else
    call entrydesc_write_v2(file,buf,ed,error)
  endif
! write (*,'(A,I0,1x,I0)') '>>> Wrote entry descriptor @ ',buf%rstart,buf%wstart
  !
  ! Update the FILE descriptor about the fact that we wrote an entry
  ! (descriptor+sections+data) with nwords elements. Remark: we are not able
  ! to know if we wrote a new entry or updated an old one, but subroutine
  ! below is protected about this:
  call filedesc_update(buf,ed%nword,file%desc,error)
  if (error)  return
  !
end subroutine classic_entrydesc_write
!
subroutine entrydesc_write_v1(file,buf,edv2,error)
  use gbl_message
  use gkernel_interfaces
  use classic_interfaces, except_this=>entrydesc_write_v1
  use classic_params
  use classic_types
  use classic_vars
  !---------------------------------------------------------------------
  ! @ private
  !   Write the input Entry Descriptor, Version 1
  !---------------------------------------------------------------------
  type(classic_file_t),      intent(in)    :: file   !
  type(classic_recordbuf_t), intent(inout) :: buf    ! Working buffer
  type(classic_entrydesc_t), intent(in)    :: edv2   ! The Entry Descriptor to be written
  logical,                   intent(inout) :: error  ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='ENTRYDESC_WRITE'
  integer(kind=4) :: len4
  integer(kind=4) :: ih(entrydescv1_nw1),isec(classic_maxsec)  ! Intermediate buffers
  integer(kind=data_length) :: addr,len8
  character(len=message_length) :: mess
  type(entrydesc_v1_t) :: ed
#if !defined(FORTRAN2003_PP) /* Fortran 2003 Procedure Pointers */
  integer(kind=address_length) :: adi4
  integer(kind=1) :: membyt(8)
  !
  adi4 = bytpnt(file%conv%writ%i4,membyt)
#endif
  !
  if (buf%wstart.ne.1) then
    write(mess,'(A,I0,A)')  &
      'Entry descriptor should be written at beginning of record for V1 file (got ',buf%wstart,')'
    call classic_message(seve%e,rname,mess)
    error = .true.
    return
  endif
  !
  error = .false.
  !
  call entrydesc_v2tov1(edv2,ed,error)
  if (error)  return
  !
  if (ed%code.ne.code_entry) then
    error = .true.
    write(mess,'(A,I0,A,I0,A,I0)')  &
      'Attempt to write non-standard entry at record ',buf%rstart,': code ',  &
      ed%code,' instead of code ',code_entry
    call classic_message(seve%e,rname,mess)
    return
  endif
  !
  ! Write the beginning of the Entry Descriptor
  addr = 1
  len8 = entrydescv1_nw1  ! The first words of the Entry Descriptor
  len4 = len8
  if (file%conv%code.le.2) then
    ! Write directly the 9 I*4 values
    call recordbuf_write(addr,len8,ed%code,buf,error)
  else
    ! Can not convert inplace (not Fortran standard!), use an intermediate buffer.
    ih(1) = ed%code
#if defined(FORTRAN2003_PP) /* Fortran 2003 Procedure Pointers */
    call file%conv%writ%i4(ed%nbloc,ih(2),len4-1)
#else
    call convert_buf(membyt(adi4),ed%nbloc,ih(2),len4-1)
#endif
    call recordbuf_write(addr,len8,ih,buf,error)
  endif
  if (error) return
  !
  ! Write the Header Section Description
  addr = addr+len8
  len8 = ed%nsec  ! Number of sections
  len4 = len8
  !
  if (file%conv%code.le.2) then
    call recordbuf_write(addr,len8,ed%seciden,buf,error)  ! SecIden: section numbers
  else
#if defined(FORTRAN2003_PP) /* Fortran 2003 Procedure Pointers */
    call file%conv%writ%i4(ed%seciden,isec,len4)
#else
    call convert_buf(membyt(adi4),ed%seciden,isec,len4)
#endif
    call recordbuf_write(addr,len8,isec,buf,error)
  endif
  if (error) return
  addr = addr+len8
  !
  if (file%conv%code.le.2) then
    call recordbuf_write(addr,len8,ed%secleng,buf,error)  ! SecLeng: section lengths
  else
#if defined(FORTRAN2003_PP) /* Fortran 2003 Procedure Pointers */
    call file%conv%writ%i4(ed%secleng,isec,len4)
#else
    call convert_buf(membyt(adi4),ed%secleng,isec,len4)
#endif
    call recordbuf_write(addr,len8,isec,buf,error)
  endif
  if (error) return
  addr = addr+len8
  !
  if (file%conv%code.le.2) then
    call recordbuf_write(addr,len8,ed%secaddr,buf,error)  ! SecAddr: section address in observation
  else
#if defined(FORTRAN2003_PP) /* Fortran 2003 Procedure Pointers */
    call file%conv%writ%i4(ed%secaddr,isec,len4)
#else
    call convert_buf(membyt(adi4),ed%secaddr,isec,len4)
#endif
    call recordbuf_write(addr,len8,isec,buf,error)
  endif
  if (error) return
  ! addr = addr+len8
  !
end subroutine entrydesc_write_v1
!
subroutine entrydesc_v2tov1(ed2,ed1,error)
  use classic_interfaces, except_this=>entrydesc_v2tov1
  use classic_types
  !---------------------------------------------------------------------
  ! @ private
  ! Convert a Version 2 Entry Descriptor to a Version 1
  !---------------------------------------------------------------------
  type(classic_entrydesc_t), intent(in)    :: ed2    ! Version 2
  type(entrydesc_v1_t),      intent(out)   :: ed1    ! Version 1
  logical,                   intent(inout) :: error  ! Logical error flag
  !
  ed1%code  = ed2%code
  call i8toi4_fini(ed2%nword,ed1%nword,1,error)
  if (error)  return
  ed1%nbloc = (ed2%nword-1)/classic_reclen_v1+1
  ed1%dumm4 = 0
  ed1%adata = ed2%adata
  ed1%ldata = ed2%ldata
  ed1%dumm7 = 0
  ed1%nsec  = ed2%nsec
  call i8toi4_fini(ed2%xnum,ed1%xnum,1,error)
  if (error)  return
  ed1%msec  = ed2%msec
  ed1%seciden(1:ed2%nsec) = ed2%seciden(1:ed2%nsec)
  ed1%secleng(1:ed2%nsec) = ed2%secleng(1:ed2%nsec)
  ed1%secaddr(1:ed2%nsec) = ed2%secaddr(1:ed2%nsec)
  !
end subroutine entrydesc_v2tov1
!
subroutine entrydesc_write_v2(file,buf,ed,error)
  use gbl_message
  use gkernel_interfaces
  use classic_interfaces, except_this=>entrydesc_write_v2
  use classic_params
  use classic_types
  use classic_vars
  !---------------------------------------------------------------------
  ! @ private
  !   Write the input Entry Descriptor, Version 2
  !---------------------------------------------------------------------
  type(classic_file_t),      intent(in)    :: file   !
  type(classic_recordbuf_t), intent(inout) :: buf    ! Working buffer
  type(classic_entrydesc_t), intent(in)    :: ed     ! The Entry Descriptor to be written
  logical,                   intent(inout) :: error  ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='ENTRYDESC_WRITE'
  integer(kind=4) :: len4
  integer(kind=data_length) :: addr,len8
  integer(kind=4) :: ih(entrydescv2_nw1)    ! Intermediate buffer for the entry descriptor
  integer(kind=4) :: isec4(classic_maxsec)  ! Intermediate buffer for seciden
  integer(kind=8) :: isec8(classic_maxsec)  ! Intermediate buffer for secaddr/secleng
  character(len=message_length) :: mess
#if !defined(FORTRAN2003_PP) /* Fortran 2003 Procedure Pointers */
  integer(kind=address_length) :: adi4,adi8
  integer(kind=1) :: membyt(8)
  !
  adi4 = bytpnt(file%conv%writ%i4,membyt)
  adi8 = bytpnt(file%conv%writ%i8,membyt)
#endif
  !
  error = .false.
  !
  if (ed%code.ne.code_entry) then
    error = .true.
    write(mess,'(A,I0,A,I0,A,I0)')  &
      'Attempt to write non-standard entry at record ',buf%rstart,': code ',  &
      ed%code,' instead of code ',code_entry
    call classic_message(seve%e,rname,mess)
    return
  endif
  !
  ! Write the beginning of the Entry Descriptor
  addr = 1
  len8 = entrydescv2_nw1  ! The first words of the Entry Descriptor
  !
  ! Can not convert inplace (not Fortran standard!), use an intermediate buffer.
  ih(1) = ed%code
#if defined(FORTRAN2003_PP) /* Fortran 2003 Procedure Pointers */
  call file%conv%writ%i4(ed%version,ih(2),2)
  call file%conv%writ%i8(ed%nword,  ih(4),4)
#else
  call convert_buf(membyt(adi4),ed%version,ih(2),2)
  call convert_buf(membyt(adi8),ed%nword,  ih(4),4)
#endif
  call recordbuf_write(addr,len8,ih,buf,error)
  if (error) return
  !
  ! Write the Header Section Description
  addr = addr+len8
  len8 = ed%nsec  ! Number of sections
  len4 = len8
  !
  ! Write the 'seciden' INTEGER*4 array
  if (file%conv%code.le.2) then
    call recordbuf_write(addr,len8,ed%seciden,buf,error)  ! SecIden: section numbers
  else
#if defined(FORTRAN2003_PP) /* Fortran 2003 Procedure Pointers */
    call file%conv%writ%i4(ed%seciden,isec4,len4)
#else
    call convert_buf(membyt(adi4),ed%seciden,isec4,len4)
#endif
    call recordbuf_write(addr,len8,isec4,buf,error)
  endif
  if (error) return
  addr = addr+len8
  !
  ! Write the 'secleng' INTEGER*8 array
  if (file%conv%code.le.2) then
    call recordbuf_write(addr,2*len8,ed%secleng,buf,error)  ! SecLeng: section lengths
  else
#if defined(FORTRAN2003_PP) /* Fortran 2003 Procedure Pointers */
    call file%conv%writ%i8(ed%secleng,isec8,len4)
#else
    call convert_buf(membyt(adi8),ed%secleng,isec8,len4)
#endif
    call recordbuf_write(addr,2*len8,isec8,buf,error)
  endif
  if (error) return
  addr = addr+2*len8
  !
  ! Write the 'secaddr' INTEGER*8 array
  if (file%conv%code.le.2) then
    call recordbuf_write(addr,2*len8,ed%secaddr,buf,error)  ! SecAddr: section address in observation
  else
#if defined(FORTRAN2003_PP) /* Fortran 2003 Procedure Pointers */
    call file%conv%writ%i8(ed%secaddr,isec8,len4)
#else
    call convert_buf(membyt(adi8),ed%secaddr,isec8,len4)
#endif
    call recordbuf_write(addr,2*len8,isec8,buf,error)
  endif
  if (error) return
  ! addr = addr+2*len8
  !
end subroutine entrydesc_write_v2
!
function classic_entrydesc_seclen(ed,iden)
  use classic_interfaces, except_this=>classic_entrydesc_seclen
  use classic_types
  !---------------------------------------------------------------------
  ! @ public
  ! Return the length of the identified section (or 0 if missing).
  ! Unefficient if you call each section one by one.
  !---------------------------------------------------------------------
  integer(kind=4) :: classic_entrydesc_seclen
  type(classic_entrydesc_t), intent(in)  :: ed    ! The Entry Descriptor
  integer(kind=4),           intent(in)  :: iden  ! The section identifier
  ! Local
  logical :: found
  integer(kind=4) :: isec
  !
  call classic_entrydesc_secfind_one(ed,iden,found,isec)
  if (found) then
    classic_entrydesc_seclen = ed%secleng(isec)
  else
    classic_entrydesc_seclen = 0
  endif
  !
end function classic_entrydesc_seclen
!
subroutine classic_entrydesc_secfind_one(ed,iden,found,num)
  use classic_interfaces, except_this=>classic_entrydesc_secfind_one
  use classic_types
  !---------------------------------------------------------------------
  ! @ public
  !  Find if 1 section is present in the input Entry Descriptor.
  ! Inefficient if you test each section one by one.
  !---------------------------------------------------------------------
  type(classic_entrydesc_t), intent(in)  :: ed     ! The Entry Descriptor
  integer(kind=4),           intent(in)  :: iden   ! The section identifier
  logical,                   intent(out) :: found  ! Section was found or not?
  integer(kind=4),           intent(out) :: num    ! The section number (if found)
  ! Local
  integer(kind=4) :: isec
  !
  found = .false.
  num = 0
  !
  do isec=1,ed%nsec
    if (ed%seciden(isec).eq.iden) then
      found = .true.
      num = isec
      exit
    endif
  enddo
  !
end subroutine classic_entrydesc_secfind_one
!
subroutine classic_entrydesc_secfind_all(ed,found,first,error)
  use gbl_message
  use classic_interfaces, except_this=>classic_entrydesc_secfind_all
  use classic_types
  !---------------------------------------------------------------------
  ! @ public
  !   Fill a 'found' array for all sections present or not. This assumes
  ! that the section identifiers (seciden(:) values) can be used as
  ! position index (maybe with an offset) in the 'found' array.
  !---------------------------------------------------------------------
  type(classic_entrydesc_t), intent(in)    :: ed        ! The Entry Descriptor
  logical,                   intent(out)   :: found(:)  ! Sections were found or not?
  integer(kind=4),           intent(in)    :: first     ! First index in the array (offset)
  logical,                   intent(inout) :: error     ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='ENTRYDESC_FIND'
  character(len=message_length) :: mess
  integer(kind=4) :: isec,ind
  !
  found(:) = .false.
  !
  do isec=1,ed%nsec
    ind = ed%seciden(isec)-first+1
    !
    if (ind.lt.1 .or. ind.gt.ubound(found,1)) then
      write(mess,'(A,I0,A)')  &
        'Out of bounds section identifier (got ',ed%seciden(isec),')'
      call classic_message(seve%e,rname,mess)
      error = .true.
      return
    endif
    !
    found(ind) = .true.
  enddo
  !
end subroutine classic_entrydesc_secfind_all
!
subroutine entrydesc_section_add(ed,iden,leng,error)
  use gbl_message
  use classic_interfaces, except_this=>entrydesc_section_add
  use classic_params
  use classic_types
  !---------------------------------------------------------------------
  ! @ private
  ! Add a new section in the Entry Descriptor. Assume it does not
  ! exist yet
  !---------------------------------------------------------------------
  type(classic_entrydesc_t), intent(inout) :: ed     ! The Entry Descriptor
  integer(kind=4),           intent(in)    :: iden   ! The section identifier
  integer(kind=data_length), intent(in)    :: leng   ! The section length
  logical,                   intent(inout) :: error  ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='WSEC'
  integer(kind=data_length) :: addr
  !
  if (ed%nsec.ge.ed%msec) then
    call classic_message(seve%e,rname,'Too many sections')
    error = .true.
    return
  endif
  !
  addr = ed%nword+1
  ed%nsec = ed%nsec+1
  ed%nword = ed%nword+leng
  ed%seciden(ed%nsec) = iden
  ed%secleng(ed%nsec) = leng
  ed%secaddr(ed%nsec) = addr
  !
end subroutine entrydesc_section_add
!
subroutine classic_entrydesc_dump(ed)
  use gbl_message
  use classic_interfaces, except_this=>classic_entrydesc_dump
  use classic_types
  !---------------------------------------------------------------------
  ! @ public
  !  Dump the input Entry Descriptor
  !---------------------------------------------------------------------
  type(classic_entrydesc_t), intent(in) :: ed  ! The Entry Descriptor
  ! Local
  character(len=*), parameter :: rname='ENTRYDESC_DUMP'
  character(len=message_length) :: mess
  integer(kind=4) :: i
  !
  write(mess,100) 'Length in words ',     ed%nword
  call classic_message(seve%r,rname,mess)
  write(mess,100) 'Data address ',        ed%adata
  call classic_message(seve%r,rname,mess)
  write(mess,100) 'Data length (words) ', ed%ldata
  call classic_message(seve%r,rname,mess)
  write(mess,100) 'Obs. version ',        ed%version
  call classic_message(seve%r,rname,mess)
  write(mess,100) 'Number of sections ',  ed%nsec
  call classic_message(seve%r,rname,mess)
  if (ed%nsec.gt.0)  then
    write(mess,'(A,15(I0,1X))') 'Allocated sections ',(ed%seciden(i),i=1,ed%nsec)
    call classic_message(seve%r,rname,mess)
  endif
  do i=1,ed%nsec
    write(mess,'(4(A,I0))') '#',i,' code #',ed%seciden(i),  &
                                  ' addr ',ed%secaddr(i),    &
                                  ' leng ',ed%secleng(i)
    call classic_message(seve%r,rname,mess)
  enddo
  !
100 format(a,t21,i0)
end subroutine classic_entrydesc_dump
