!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!  
module cubeadm_cubeid_types
  !----------------------------------------------------------------------
  ! Implement the parsing and resolution of
  !    COMMAND id1 id2 ... idn
  !----------------------------------------------------------------------
  use cubetools_parameters
  use cubetools_primitive_arg
  use cubetools_structure
  use cubedag_parameters
  use cubedag_allflags
  use cubedag_find
  use cubeadm_messaging
  !
  public :: cube_k
  public :: cubeadm_cubeid_parse,cubeadm_cubeid_user2prog,cubeadm_cubeid_user2prog_one
  public :: cubeadm_cubeid_user2find,cubeadm_cubeid_string2familyflags
  public :: cubeadm_cubeid_familyflags2string
  public :: cubeadm_cubeid_arg_ptr
  public :: cubeid_arg_t,cubeid_user_cube_t,cubeid_user_t,cubeid_prog_t
  private
  !
  character(len=1), parameter :: separator_strg=':'  ! Family/flags separator
  !
  integer(kind=4), parameter :: flag_k = 4
  integer(kind=4), parameter :: cube_k = 4
  integer(kind=4), parameter :: iden_k = 4
  !
  type, extends(primitive_arg_t) :: cubeid_arg_t
     integer(kind=flag_k)      :: nflag = -1
     type(flag_t), allocatable :: flag(:)
     integer(kind=code_k)      :: action = code_unk
     integer(kind=code_k)      :: access = code_unk
   contains
     generic,   public  :: register => register_old,register_new
     procedure, private :: register_old => cubeadm_cubeid_register_arg_old
     procedure, private :: register_new => cubeadm_cubeid_register_arg
     procedure :: print_abstract => cubeadm_cubeid_arg_abstract
  end type cubeid_arg_t
  !
  type cubeid_user_cube_t
     character(len=argu_l) :: family = strg_unk
     character(len=argu_l) :: flags = strg_unk
     character(len=argu_l) :: pos = strg_unk
     character(len=base_l) :: id = strg_unk
  end type cubeid_user_cube_t
  type cubeid_user_t
     integer(kind=cube_k) :: ncube = 0
     type(cubeid_user_cube_t), allocatable :: cube(:)
   contains
     procedure, public :: fill => cubeadm_cubeid_user_fill
  end type cubeid_user_t
  !
  type cubeid_prog_t
     integer(kind=iden_k) :: nid = 0
     integer(kind=iden_l), allocatable :: id(:)
     ! ZZZ SB: storing pointers to cubes would save id-to-cube resolutions later on
   contains
     procedure, public :: defstruct => cubeadm_cubeid_defstruct
  end type cubeid_prog_t
  !
contains
  !
  subroutine cubeadm_cubeid_user_free(user,error)
    !----------------------------------------------------------------------
    ! 
    !----------------------------------------------------------------------
    type(cubeid_user_t), intent(out)   :: user
    logical,             intent(inout) :: error   
    !
    character(len=*), parameter :: rname='CUBEID>USER>FREE'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
  end subroutine cubeadm_cubeid_user_free
  !
  function cubeadm_cubeid_arg_ptr(tot,error)
    use cubetools_list
    !-------------------------------------------------------------------
    ! Check if the input class is of type(cubeid_arg_t), and return
    ! a pointer to it if relevant.
    !-------------------------------------------------------------------
    type(cubeid_arg_t), pointer                  :: cubeadm_cubeid_arg_ptr  ! Function value on return
    class(tools_object_t), pointer               :: tot ! *** JP which intent?
    logical,                       intent(inout) :: error
    !
    character(len=*), parameter :: rname='ARGUMENT>PTR'
    !
    select type(tot)
    type is (cubeid_arg_t)
      cubeadm_cubeid_arg_ptr => tot
    class default
      cubeadm_cubeid_arg_ptr => null()
      call cubeadm_message(seve%e,rname,  &
        'Internal error: object is not a cubeid_arg_t type')
      error = .true.
      return
    end select
  end function cubeadm_cubeid_arg_ptr
  !
  subroutine cubeadm_cubeid_user_allocate_and_init(n,user,error)
    use gkernel_interfaces
    use cubetools_parse
    !----------------------------------------------------------------------
    ! 
    !----------------------------------------------------------------------
    integer(kind=iden_k), intent(in)    :: n
    type(cubeid_user_t),  intent(inout) :: user
    logical,              intent(inout) :: error
    !
    integer(kind=4) :: ier,icub
    character(len=*), parameter :: rname='CUBEID>USER>ALLOCATE>AND>INIT'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    if (allocated(user%cube)) then
       call cubeadm_message(admseve%alloc,rname,'Cube array already allocated deallocating')
       deallocate(user%cube)
    endif
    !
    allocate(user%cube(n),stat=ier)
    if (failed_allocate(rname,'cube array',ier,error)) return
    user%ncube = n
    !
    do icub=1,n
       user%cube(icub)%id     = strg_star
       user%cube(icub)%family = strg_star
       user%cube(icub)%flags  = blankstr  ! Empty => catched in user2find
       user%cube(icub)%pos    = blankstr  ! Empty => catched in user2prog_one
    enddo
    ! 
  end subroutine cubeadm_cubeid_user_allocate_and_init
  !
  !------------------------------------------------------------------------
  !
  subroutine cubeadm_cubeid_prog_free(prog,error)
    !----------------------------------------------------------------------
    ! 
    !----------------------------------------------------------------------
    type(cubeid_prog_t), intent(out)   :: prog
    logical,             intent(inout) :: error
    !
    character(len=*), parameter :: rname='CUBEID>PROG>FREE'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
  end subroutine cubeadm_cubeid_prog_free
  !
  subroutine cubeadm_cubeid_prog_allocate_and_init(n,prog,error)
    use gkernel_interfaces
    !----------------------------------------------------------------------
    ! 
    !----------------------------------------------------------------------
    integer(kind=iden_k), intent(in)    :: n
    type(cubeid_prog_t),  intent(inout) :: prog
    logical,              intent(inout) :: error
    !
    integer(kind=4) :: icub,ier
    character(len=*), parameter :: rname='CUBEID>PROG>ALLOCATE'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    if (allocated(prog%id)) then
       call cubeadm_message(admseve%alloc,rname,'Id array already allocated deallocating')
       deallocate(prog%id)
    endif
    !
    allocate(prog%id(n),stat=ier)
    if (failed_allocate(rname,'id array',ier,error)) return
    prog%nid = n
    !
    do icub=1,n
       prog%id(icub) = code_abs
    enddo
  end subroutine cubeadm_cubeid_prog_allocate_and_init
  !
  !------------------------------------------------------------------------
  !
  subroutine cubeadm_cubeid_arg_allocate_and_init(nflag,arg,error)
    use gkernel_interfaces
    use cubedag_parameters
    !----------------------------------------------------------------------
    ! 
    !----------------------------------------------------------------------
    integer(kind=flag_k), intent(in)    :: nflag
    type(cubeid_arg_t),   intent(inout) :: arg
    logical,              intent(inout) :: error
    !
    integer(kind=4) :: ier
    character(len=*), parameter :: rname='CUBEID>ARG>ALLOCATE'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    if (allocated(arg%flag)) then
       call cubeadm_message(admseve%alloc,rname,'Flag array already allocated deallocating')
       deallocate(arg%flag)
    endif
    !
    allocate(arg%flag(nflag),stat=ier)
    if (failed_allocate(rname,'flag array',ier,error)) return
    arg%nflag = nflag
    !
    arg%flag(:) = flag_unknown
  end subroutine cubeadm_cubeid_arg_allocate_and_init
  !
  subroutine cubeadm_cubeid_arg_put(flags,action,access,arg,error)
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    type(flag_t),         intent(in)    :: flags(:)  !
    integer(kind=code_k), intent(in)    :: action    ! e.g. read, update,...
    integer(kind=code_k), intent(in)    :: access    ! e.g. imaset, speset,...
    type(cubeid_arg_t),   intent(inout) :: arg
    logical,              intent(inout) :: error
    !
    character(len=*), parameter :: rname='CUBEID>ARG>PUT'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    call cubeadm_cubeid_arg_allocate_and_init(size(flags),arg,error)
    if (error) return
    arg%flag(:) = flags(:)
    arg%action  = action
    arg%access  = access
  end subroutine cubeadm_cubeid_arg_put
  !
  !------------------------------------------------------------------------
  !
  subroutine cubeadm_cubeid_register_arg_old(templatearg,name,abstract,  &
    help,mandat,flags,error)
    !----------------------------------------------------------------------
    ! OBSOLESCENT version which does not register the cube action and
    ! access
    !----------------------------------------------------------------------
    class(cubeid_arg_t),  intent(in)    :: templatearg  ! Template
    character(len=*),     intent(in)    :: name
    character(len=*),     intent(in)    :: abstract
    character(len=*),     intent(in)    :: help
    integer(kind=code_k), intent(in)    :: mandat
    type(flag_t),         intent(in)    :: flags(:)
    logical,              intent(inout) :: error
    !
    type(cubeid_arg_t), pointer :: pcube
    !
    call cubeadm_cubeid_register_arg(templatearg,name,abstract,help,  &
      mandat,flags,code_unk,code_unk,pcube,error)
    if (error)  return
  end subroutine cubeadm_cubeid_register_arg_old
  !
  subroutine cubeadm_cubeid_register_arg(templatearg,name,abstract,help,  &
    mandat,flags,action,access,pcube,error)
    !----------------------------------------------------------------------
    ! 
    !----------------------------------------------------------------------
    class(cubeid_arg_t),  intent(in)    :: templatearg  ! Template
    character(len=*),     intent(in)    :: name
    character(len=*),     intent(in)    :: abstract
    character(len=*),     intent(in)    :: help
    integer(kind=code_k), intent(in)    :: mandat
    type(flag_t),         intent(in)    :: flags(:)
    integer(kind=code_k), intent(in)    :: action  ! e.g. read, update,...
    integer(kind=code_k), intent(in)    :: access  ! e.g. imaset, speset,...
    type(cubeid_arg_t),   pointer       :: pcube   ! Pointer to registered cube
    logical,              intent(inout) :: error
    !
    class(primitive_arg_t), pointer :: actualarg
    character(len=*), parameter :: rname='CUBEID>REGISTER>ARG'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    ! Register the primitive parts, and get pointer to the actual object
    ! saved in the parsing structure
    call cubetools_register_primitive_arg(templatearg,name,abstract,help,mandat,actualarg,error)
    if (error) return
    !
    ! Register the extended parts
    select type (actualarg)
    type is (cubeid_arg_t)
      pcube => actualarg
      call cubeadm_cubeid_arg_put(flags,action,access,pcube,error)
      if (error) return
    class default
      call cubeadm_message(seve%e,rname,'Internal error: argument has wrong type')
      error = .true.
      return
    end select
  end subroutine cubeadm_cubeid_register_arg
  !
  subroutine cubeadm_cubeid_arg_abstract(arg,iarg,error)
    use cubetools_format
    use cubetools_help
    use cubetools_messaging
    use cubetools_string
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(cubeid_arg_t),    intent(in)    :: arg
    integer(kind=narg_k),   intent(in)    :: iarg
    logical,                intent(inout) :: error
    !
    character(len=mess_l) :: status,mess,flags
    character(len=32) :: statuslist(3)
    character(len=*), parameter :: rname='CUBEID>ABSTRACT'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    if ((iarg.lt.1).or.(9.lt.iarg)) then
       call cubeadm_message(seve%e,rname,'Argument number out of authorized range: [1-9]')
       error = .true.
       return
    endif
    !
    call cubedag_flaglist_tostr(arg%flag,arg%nflag,strflag=flags,error=error)
    if (error) return
    if (flags.ne.strg_star) then
      ! Illustrate the fact that the engine searches for *myflag*
      flags = strg_star//trim(flags)//strg_star
    endif
    !
    if (arg%action.eq.code_unk) then  ! OBSOLESCENT
      call cubetools_string_concat(1,[arg_status(arg%mandatory)],',',status,error)
    else
      statuslist(1) = arg_status(arg%mandatory)
      statuslist(2) = action_status(arg%action)
      statuslist(3) = access_status(arg%access)
      call cubetools_string_concat(3,statuslist,',',status,error)
    endif
    if (error)  return
    !
    write(mess,'(a1,i1,1x,3a,1x,a)')  '#',iarg,'(',trim(status),')',trim(flags)
    mess = '  '//cubetools_format_stdkey_boldval(mess,arg%abstract,help_width())
    call cubetools_message(toolseve%help,rname,mess)
  end subroutine cubeadm_cubeid_arg_abstract
  !
  !------------------------------------------------------------------------
  !
  subroutine cubeadm_cubeid_user_fill(user,strg,error)
    !----------------------------------------------------------------------
    ! Fill a cubeid user structure in a similar manner to what would
    ! be done by parsing but without going through SIC. Limited to a
    ! single cubeid for the moment
    ! ----------------------------------------------------------------------
    class(cubeid_user_t), intent(out)   :: user
    character(len=*),     intent(in)    :: strg
    logical,              intent(inout) :: error
    !
    character(len=*), parameter :: rname='CUBEID>USER>FILL'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    call cubeadm_cubeid_user_allocate_and_init(int(1,kind=iden_k),user,error)
    if (error) return
    user%cube(1)%id = strg
    call cubeadm_parse_family_flags(user%cube(1)%id,user%cube(1)&
         &%family,user%cube(1)%flags,user%cube(1)%pos,error)
    if (error) return
  end subroutine cubeadm_cubeid_user_fill
  !
  !----------------------------------------------------------------------
  !
  subroutine cubeadm_cubeid_parse(line,opt,id,error)
    !----------------------------------------------------------------------
    ! 
    !----------------------------------------------------------------------
    character(len=*),    intent(in)    :: line
    type(option_t),      intent(in)    :: opt
    type(cubeid_user_t), intent(inout) :: id
    logical,             intent(inout) :: error
    !
    integer(kind=4) :: icub
    type(cubeid_arg_t), pointer :: arg
    character(len=*), parameter :: rname='CUBEID>PARSE'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    call cubeadm_cubeid_user_allocate_and_init(int(opt%arg%n,kind=iden_k),id,error)
    if (error) return
    do icub=1,opt%getnarg()
       arg => cubeadm_cubeid_arg_ptr(opt%arg%list(icub)%p,error)
       if (error) return
       call cubeadm_cubeid_user_flag_parse(line,opt,icub,  &
         arg%mandatory,separator_strg,id%cube(icub),error)
       if (error) return
    enddo
  end subroutine cubeadm_cubeid_parse
  !
  subroutine cubeadm_cubeid_user_flag_parse(line,opt,iarg,mandat,sep,user,error)
    use gkernel_interfaces
    use cubetools_parse
    use cubetools_disambiguate
    use cubedag_parameters
    !----------------------------------------------------------------------
    ! 
    !----------------------------------------------------------------------
    character(len=*),         intent(in)    :: line
    type(option_t),           intent(in)    :: opt
    integer(kind=4),          intent(in)    :: iarg
    integer(kind=code_k),     intent(in)    :: mandat
    character(len=*),         intent(in)    :: sep
    type(cubeid_user_cube_t), intent(inout) :: user
    logical,                  intent(inout) :: error
    !
    character(len=*), parameter :: rname='CUBEID>USER>FLAG>PARSE'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    call cubetools_getarg(line,opt,iarg,user%id,mandat.eq.code_arg_mandatory,error)
    if (error) return
    call cubeadm_parse_family_flags(user%id,user%family,user%flags,user%pos,error)
    if (error) return
  end subroutine cubeadm_cubeid_user_flag_parse
  !
  subroutine cubeadm_parse_family_flags(string,family,flags,pos,error)
    use gkernel_interfaces
    use cubedag_parameters
    !----------------------------------------------------------------------
    ! 
    !----------------------------------------------------------------------
    character(len=*), intent(in)    :: string
    character(len=*), intent(inout) :: family
    character(len=*), intent(inout) :: flags
    character(len=*), intent(inout) :: pos
    logical,          intent(inout) :: error
    !
    integer(kind=4) :: firstsep,secondsep
    character(len=*), parameter :: rname='CUBEID>USER>FLAG>PARSE'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    firstsep = index(string,separator_strg)
    if (firstsep.eq.0) then ! Family name only
      family = string
      return
    else
      family = string(1:firstsep-1)
    endif
    !
    secondsep = index(string(firstsep+1:),separator_strg)
    if (secondsep.eq.0) then
      flags = string(firstsep+1:)
      return
    else
      secondsep = firstsep+secondsep
      flags = string(firstsep+1:secondsep-1)
    endif
    !
    pos = string(secondsep+1:)
    !
  end subroutine cubeadm_parse_family_flags
  !
  subroutine cubeadm_cubeid_string2familyflags(string,family,flags,error)
    !----------------------------------------------------------------------
    ! Transforms a string into family name + flags
    !----------------------------------------------------------------------
    character(len=*),          intent(in)    :: string
    character(len=*),          intent(out)   :: family
    type(flag_t), allocatable, intent(out)   :: flags(:)
    logical,                   intent(inout) :: error
    !
    character(len=argu_l) :: stflags,pos
    character(len=*), parameter :: rname='CUBEID>STRING2FAMLIYFLAGS'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    family = ''
    stflags = ''
    pos    = ''
    call cubeadm_parse_family_flags(string,family,stflags,pos,error)
    if (error) return
    if (len_trim(family).eq.0) then
       call cubeadm_message(seve%e,rname,'Family name is empty')
       error =.true.
    endif
    !
    call cubedag_string_toflaglist(stflags,flags,error)
    if (error) return
  end subroutine cubeadm_cubeid_string2familyflags
  !
  !----------------------------------------------------------------------
  !
  subroutine cubeadm_cubeid_user2prog(opt,user,prog,error)
    use cubetools_option
    !----------------------------------------------------------------------
    ! 
    !----------------------------------------------------------------------
    type(option_t),      intent(in)    :: opt
    type(cubeid_user_t), intent(in)    :: user
    type(cubeid_prog_t), intent(inout) :: prog
    logical,             intent(inout) :: error
    !
    integer(kind=cube_k) :: icube
    type(cubeid_arg_t), pointer :: optarg
    character(len=*), parameter :: rname='CUBEID>USER2PROG'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    ! ZZZ There should be a sanity check between the number of registered
    !     arguments in option vs the number of arguments expected in user and
    !     prog types.
    !
    call cubeadm_cubeid_prog_allocate_and_init(user%ncube,prog,error)
    if (error) return
    !
    do icube=1,user%ncube
      optarg => cubeadm_cubeid_arg_ptr(opt%arg%list(icube)%p,error)
      if (error) return
      call cubeadm_cubeid_user2prog_one(optarg,user%cube(icube),prog%id(icube),error)
      if (error) return
    enddo
    !
  end subroutine cubeadm_cubeid_user2prog
  !
  subroutine cubeadm_cubeid_user2prog_one(arg,ucube,id,error)
    use cubedag_link_type
    use cubedag_node_type
    use cubeadm_setup
    !----------------------------------------------------------------------
    !  
    !----------------------------------------------------------------------
    type(cubeid_arg_t),       intent(in)    :: arg
    type(cubeid_user_cube_t), intent(in)    :: ucube
    integer(kind=iden_l),     intent(inout) :: id
    logical,                  intent(inout) :: error
    !
    type(cubedag_find_t) :: find
    type(cubedag_link_t) :: mycx
    integer(kind=iden_l) :: uid
    integer(kind=4) :: ier
    character(len=mess_l) :: mess
    integer(kind=entr_k) :: pos,apos
    logical :: isposition,try_a_number
    class(cubedag_node_object_t), pointer :: dno
    character(len=*), parameter :: rname='CUBEID>USER2PROG>ONE'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    if (ucube%family.eq.strg_unk) then
       call cubeadm_message(seve%e,rname,'ucube structure is not properly initialized')
       error = .true.
       return
    endif
    !
    read(ucube%family,*,iostat=ier)  uid
    try_a_number = ier.eq.0
100 continue
    isposition = .false.
    if (try_a_number) then ! User has typed either a position or an ID
       if (uid.ge.1) then ! User has given an ID
          call cubeadm_cubeid_user2find_id(uid,ucube,find,error)
          if (error) return
       else ! User has given a position
          isposition = .true.
          call cubeadm_cubeid_user2find_position(arg,ucube,find,error)
          if (error) return        
       endif
    else ! User has typed a string
       call cubeadm_cubeid_user2find_family(arg,ucube,find,error)
       if (error) return
    endif    
    !
    if (cubset%index%default.eq.code_index_dag) then
       call cubedag_find_ix2optx(find,mycx,error)
       if (error) return
    else
       call cubedag_find_cx2optx(find,mycx,error)
       if (error) return
    endif
    !
    if (mycx%n.le.0 .and. try_a_number) then
      ! Nothing found. Try with user input as a family string
      try_a_number = .false.
      goto 100
    endif
    !
    if (mycx%n.le.0) then
       ! Nothing found: raise an error
       mess = trim(ucube%family)//':'
       if (ucube%flags.ne.blankstr .and. ucube%flags.ne.strg_star) then
          ! User has given explicit flags
          mess = trim(mess)//trim(ucube%flags)//' not found'
       elseif (allocated(arg%flag)) then
          ! Command requested some flags
          if (arg%flag(1).eq.flag_any) then
             mess = trim(mess)//strg_star//' not found'
          else
             mess = trim(mess)//trim(find%ccflag)//' not found'
          endif
       else
          mess = 'Finds flags not allocated, internal error'
       endif
       call cubeadm_message(seve%e,rname,mess)
       error = .true.
       return
    endif
    !
    ! Position:
    !   1 to N  : natural ordering from first (older) to last (newest)
    !   0       : last (newest)
    !  -1 to N-1: previous last, etc
    if (isposition) then
       pos = uid
    else
       if (ucube%pos.eq.blankstr) then
          pos = 0  ! Default is last
       else
          call sic_math_long(ucube%pos,len_trim(ucube%pos),pos,error)
          if (error) return
       endif
    endif
    if (pos.gt.0) then
      apos = pos
    else
      apos = mycx%n+pos
    endif
    if (apos.lt.1 .or. apos.gt.mycx%n) then
      write(mess,'(3(A,I0))')  &
        'Invalid position specifier ',pos,' (selection has ',mycx%n,' cubes)'
      call cubeadm_message(seve%e,rname,mess)
      error = .true.
      return
    endif
    !
    dno => cubedag_node_ptr(mycx%list(apos)%p,error)
    if (error) return
    id = dno%node%id
    !
    ! Clean
    ! 'mycx' has only allocatable arrays => automatic clean
    !
  end subroutine cubeadm_cubeid_user2prog_one
  !
  subroutine cubeadm_cubeid_user2find(arg,ucube,find,error)
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    type(cubeid_arg_t),       intent(in)    :: arg    ! Command defaults
    type(cubeid_user_cube_t), intent(in)    :: ucube  ! User inputs
    type(cubedag_find_t),     intent(out)   :: find
    logical,                  intent(inout) :: error
    !
    character(len=*), parameter :: rname='CUBEID>USER2FIND'
    integer(kind=iden_l) :: uid
    integer(kind=4) :: ier
    !
    read(ucube%family,*,iostat=ier)  uid
    ! ZZZ This method is not fully satisfying, as this does not support
    ! e.g. SIC variables or more general integer expression. sic_math_inte
    ! deals with this, but raises an annoying error when encountering a
    ! FAMILY name.
    if (ier.eq.0) then
       ! Family is an Id
       find%iiden = uid
       if (ucube%flags.ne.blankstr .and. ucube%flags.ne.strg_star) then
          call cubeadm_message(seve%e,rname,'Cube Id can not be given together with flags')
          error = .true.
          return
       endif
    else
       ! Family is a true family name
       find%cfami = ucube%family
       if (ucube%flags.eq.blankstr) then
         ! No explicit user input, use command defaults
         if (arg%nflag.gt.0) then
           call cubedag_flaglist_tostr(arg%flag,arg%nflag,strflag=find%ccflag,error=error)
           if (error) return
           ! Add * around flag string to allow command,cube to be
           ! taken by commands that ask for cube and other flag
           ! combinations that could be skipped because of precise
           ! flag matching.
           find%ccflag = strg_star//trim(find%ccflag)//strg_star
         endif
       else
         ! Explicit user input
         find%ccflag = ucube%flags
       endif
    endif
  end subroutine cubeadm_cubeid_user2find
  !
  subroutine cubeadm_cubeid_user2find_id(uid,ucube,find,error)
    !----------------------------------------------------------------------
    ! Fills find structure when user has given a positive integer,
    ! i.e. an Id
    !----------------------------------------------------------------------
    integer(kind=iden_l),     intent(in)    :: uid    ! User input (id)
    type(cubeid_user_cube_t), intent(in)    :: ucube  ! User inputs
    type(cubedag_find_t),     intent(out)   :: find
    logical,                  intent(inout) :: error
    !
    character(len=*), parameter :: rname='CUBEID>USER2FIND>ID'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    ! Identifier
    find%iiden = uid
    !
    ! Flags: none allowed, command defaults ignored. Leave default (any)
    if (ucube%flags.ne.blankstr .and. ucube%flags.ne.strg_star) then
       call cubeadm_message(seve%e,rname,'Cube Id can not be given together with flags')
       error = .true.
       return
    endif
  end subroutine cubeadm_cubeid_user2find_id
  !
  subroutine cubeadm_cubeid_user2find_family(arg,ucube,find,error)
    !----------------------------------------------------------------------
    ! Fills find structure when user has given what is supposed to be
    ! a family name and/or flag(s), i.e. a string
    !----------------------------------------------------------------------
    type(cubeid_arg_t),       intent(in)    :: arg    ! Command defaults
    type(cubeid_user_cube_t), intent(in)    :: ucube  ! User inputs
    type(cubedag_find_t),     intent(out)   :: find
    logical,                  intent(inout) :: error
    !
    character(len=*), parameter :: rname='CUBEID>USER2FIND>FAMILY'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    ! Family
    find%cfami = ucube%family
    !
    ! Flags (search by string)
    if (ucube%flags.eq.blankstr) then
       ! No explicit user input, use command defaults
       if (arg%nflag.gt.0) then
          ! Command default
          call cubedag_flaglist_tostr(arg%flag,arg%nflag,strflag=find%ccflag,error=error)
          if (error) return
          ! Add * around flag string to allow command,cube to be
          ! taken by commands that ask for cube and other flag
          ! combinations that could be skipped because of precise
          ! flag matching.
          find%ccflag = strg_star//trim(find%ccflag)//strg_star
       endif
    else
       ! Explicit user input
       find%ccflag = ucube%flags
    endif
  end subroutine cubeadm_cubeid_user2find_family
  !
  subroutine cubeadm_cubeid_user2find_position(arg,ucube,find,error)
    !----------------------------------------------------------------------
    ! Fills find structure when user has given a negative integer,
    ! i.e. a position in the dag from the latest
    !----------------------------------------------------------------------
    type(cubeid_arg_t),       intent(in)    :: arg    ! Command defaults
    type(cubeid_user_cube_t), intent(in)    :: ucube  ! User inputs
    type(cubedag_find_t),     intent(out)   :: find
    logical,                  intent(inout) :: error
    !
    character(len=*), parameter :: rname='CUBEID>USER2FIND>POSITION'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    ! Identifier
    ! => Leave default to find everything, count from the end is done later
    !
    ! Flags: none allowed from user, command defaults ignored. Leave default (any)
    if (ucube%flags.ne.blankstr .and. ucube%flags.ne.strg_star) then
       call cubeadm_message(seve%e,rname,'Cube Id can not be given together with flags')
       error = .true.
       return
    endif
  end subroutine cubeadm_cubeid_user2find_position
  !
  !------------------------------------------------------------------------ 
  !
  subroutine cubeadm_cubeid_familyflags2string(family,flags,string,error)
    !-------------------------------------------------------------------
    ! From family name and flag codes, build the CubeID string
    !-------------------------------------------------------------------
    character(len=*), intent(in)    :: family
    type(flag_t),     intent(in)    :: flags(:)
    character(len=*), intent(out)   :: string
    logical,          intent(inout) :: error
    !
    call cubedag_flaglist_tostr(flags,size(flags),strflag=string,error=error)
    if (error) return
    string = trim(family)//separator_strg//string
  end subroutine cubeadm_cubeid_familyflags2string
  !
  !------------------------------------------------------------------------ 
  !
  subroutine cubeadm_cubeid_defstruct(cubes,struct,error)
    use gkernel_interfaces
    use cubetools_progstruct_types
    !-------------------------------------------------------------------
    ! 
    !-------------------------------------------------------------------
    class(cubeid_prog_t), intent(inout) :: cubes
    type(progstruct_t),   intent(in)    :: struct
    logical,              intent(inout) :: error
    !
    type(progstruct_t) :: cubestruct
    character(len=*), parameter :: rname='CUBEID>DEFSTRUCT'
    !
    call struct%recreate('cubes',cubestruct,error)
    if (error) return
    call sic_def_inte(trim(cubestruct%name)//'n',cubes%nid,0,0,readonly,error)
    if (error) return
    call sic_def_inte(trim(cubestruct%name)//'id',cubes%id,1,cubes%nid,readonly,error)
    if (error) return
  end subroutine cubeadm_cubeid_defstruct
end module cubeadm_cubeid_types
! 
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
