subroutine sic_analyse(command,line,nline,error)
  use gildas_def
  use sic_interfaces, except_this=>sic_analyse
  use sic_expressions
  use sic_structures
  !---------------------------------------------------------------------
  ! @ public
  ! SIC command line analyser. First entry point:
  ! SIC_ANALYSE     Look for all languages including "library"
  !---------------------------------------------------------------------
  character(len=*), intent(out)   :: command  ! The command found, if any
  character(len=*), intent(inout) :: line     ! The line to be analysed (updated)
  integer(kind=4),  intent(inout) :: nline    ! Length of LINE (updated)
  logical,          intent(inout) :: error    ! Logical error flag
  ! Local
  character(len=commandline_length) :: order
  !
  ! Reset argument counters (see sic_desc_inca)
  next_arg = 0
  next_st = 0
  !
  library_mode = .true.
  call sic_parse_line(line,nline,.false.,sic_quiet,ccomm,order,error)
  command = ccomm%command
  call sic_upper(command)
end subroutine sic_analyse
!
subroutine sic_find(command,line,nline,error)
  use gildas_def
  use sic_interfaces, except_this=>sic_find
  use sic_expressions
  use sic_structures
  !---------------------------------------------------------------------
  ! @ public
  ! SIC command line analyser. Second entry point:
  ! SIC_FIND        Look only for interactive languages.
  !---------------------------------------------------------------------
  character(len=*), intent(out)   :: command  ! The command found, if any
  character(len=*), intent(inout) :: line     ! The line to be analysed (updated)
  integer(kind=4),  intent(inout) :: nline    ! Length of LINE (updated)
  logical,          intent(inout) :: error    ! Logical error flag
  ! Local
  character(len=commandline_length) :: order
  !
  ! Reset argument counters (see sic_desc_inca)
  next_arg = 0
  next_st = 0
  !
  call sic_parse_line(line,nline,.true.,sic_quiet,ccomm,order,error)
  command = ccomm%command
  !
  ! The number of options of this command would be NOPT(CCOMM%ICOM)
end subroutine sic_find
!
subroutine sic_parse_line(line,nline,library,quiet,command,ordre,error)
  use gildas_def
  use gbl_message
  use sic_dependencies_interfaces
  use sic_interfaces, except_this=>sic_parse_line
  use sic_types
  use sic_dictionaries
  !---------------------------------------------------------------------
  ! @ private
  !  SIC command line analyser
  !  IBEG    pointer in LINE
  !  M       pointer in ORDRE
  !  JOPT    Option number   (0=command)
  !  JARG    Argument number (0=option or command)
  !  COMM%POPT(Jopt)               Pointer of option in argument list
  !  COMM%IBEG(POPT(Jopt)+Jarg)    Start pointer of argument
  !  COMM%IEND(POPT(Jopt)+Jarg)    End pointer of argument
  !
  !  The character string describing an argument is thus
  !   LINE(COMM%IBEG(COMM%POPT(Jopt)+Jarg):COMM%IEND(COMM%POPT(Jopt)+Jarg))
  ! provided COMM%JOPT < 10 and Ipoint(Jopt)+Jarg < 100 . More arguments
  ! may exist, but a special procedure is required to find them, as no
  ! pointers are defined for these. The language is included in the
  ! command line on return.
  !  Argument 'COMMAND' is inout so that it is not fully reset (for
  ! efficiency purpose) since it has been done on first call. From your
  ! side you may consider SAVing the input variable in order to enable
  ! this optimisation.
  !---------------------------------------------------------------------
  character(len=*),    intent(inout) :: line     ! The line to be analysed (updated)
  integer(kind=4),     intent(inout) :: nline    ! Length of line (updated)
  logical,             intent(in)    :: library  ! Library mode
  logical,             intent(in)    :: quiet    !
  type(sic_command_t), intent(inout) :: command  ! The parsed command line
  character(len=*)                   :: ordre    ! Work buffer
  logical,             intent(inout) :: error    ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='PARSE'
  logical :: inchain,inarg
  integer(kind=4) :: jopt,jarg,icha,m,ibeg,iquiet
  !
  if (quiet) then
    iquiet = 1
  else
    iquiet = 0
  endif
  !
  ! Setup pointers
  command%aline = locstr(line)
  command%icom = 0
  command%command = ' '
  if (command%initialized) then
    ! If already initialized, reset only the elements which were used
    ! before, for efficiency purpose
    command%narg(0:command%mopt) = -1
    command%popt(0:command%mopt) = 0
    command%ibeg(1:command%nword) = 0
    command%iend(1:command%nword) = 0
  else
    ! Not initialized: reset everything
    command%narg(:) = -1
    command%popt(:) = 0
    command%ibeg(:) = 0  ! ibeg(iarg).eq.0 => argument not present (see sic_present)
    command%iend(:) = 0
    command%initialized = .true.
  endif
  command%mopt = 0
  command%nword = 0
  !
  m = 0
  inchain = .false.
  inarg = .false.
  !
  ! Parse the line
  do icha=1,nline
    !
    ! Check the character strings
    if (line(icha:icha).eq.'"') then
      if (.not.inarg)  call start_argument()
      inchain = .not.inchain
      cycle  ! icha
    endif
    if (inchain) cycle  ! icha
    !
    if (line(icha:icha).eq.' ') then
      ! Found a blank
      if (icha.eq.1) then
        if (.not.quiet)  call sic_message(seve%e,rname,'No command on line')
        error = .true.
        nline = 1
        return
      endif
      if (inarg) then
        ! An argument was being traversed. The blank marks its end.
        call stop_argument(error)
        if (error)  return
      endif
    else
      ! Found something not a blank
      if (.not.inarg)  call start_argument()
    endif
    !
  enddo  ! icha
  !
  if (inchain) then
    if (.not.quiet)  call sic_message(seve%e,rname,'Unbalanced quote count')
    error = .true.
    return
  endif
  !
  ! Terminate last argument
  if (inarg) then
    call stop_argument(error)
    if (error)  return
  endif
  !
  ! Restore the expanded line
  line = ordre(1:m)
  nline = m-1 ! Remove trailing blank
  !
contains
  !
  subroutine start_argument()
    !-------------------------------------------------------------------
    ! Set up some of the variables when a new argument begins
    !-------------------------------------------------------------------
    ibeg = icha
    if (command%icom.eq.0) then
      ! Command not yet found: this must be the command
      jarg = 0
    elseif (line(ibeg:ibeg).ne.'/' .or. hasopt(command%icom).eq.-1) then
      ! New argument is a standard argument: increment current counter
      jarg = jarg+1
    else
      ! Next argument is an option
      jarg = 0
    endif
    inarg = .true.
  end subroutine start_argument
  !
  subroutine stop_argument(error)
    !-------------------------------------------------------------------
    ! Set up the remaining variables when a new argument ends
    !-------------------------------------------------------------------
    logical, intent(inout) :: error
    ! Local
    integer(kind=4), parameter :: isig=1  ! Skip leading "/" for options (old value ISIG=2)
    character(len=1), parameter :: backslash=char(92)
    character(len=command_length) :: name
    character(len=language_length+command_length) :: tempo
    integer(kind=4) :: iopt,nl,ocode
    !
    if (command%nword.eq.mword) then
      call sic_message(seve%e,rname,'Too many words in line')
      error = .true.
      return
    endif
    command%nword = command%nword+1
    !
    if (jarg.eq.0) then
      ! Argument is the command or an option
      tempo = line(ibeg:icha-1)
      call sic_upper(tempo)
      if (command%icom.eq.0) then
        ! Search for the command
        call sic_parse_command(tempo,icha-ibeg,iquiet,library,command%ilang,  &
          command%icom,ocode,error)
        if (error)  return
        jopt = 0  ! Option #0 is command
        command%lang = languages(command%ilang)%name
        command%command = vocab(command%icom)(2:)  ! Command name
        name = command%command
        command%mopt = nopt(command%icom)
        ! Prefix with the language name:
        m = languages(command%ilang)%lname  ! Stored for optimisation
        ordre = languages(command%ilang)%name(1:m)//backslash
        m = m+1
      else
        ! Search for an option
        call sic_parse_option(tempo,icha-ibeg,iquiet,command%icom,iopt,error)
        if (error)  return
        jopt = iopt-command%icom  ! Relative number from command (1 to N)
        name = vocab(iopt)(isig:)  ! Option name
      endif
      if (command%popt(jopt).ne.0) then
        call sic_message(seve%e,rname,'Option '//trim(name)//' is specified twice')
        error = .true.
        return
      endif
      command%popt(jopt) = command%nword  ! jopt==0: command, else option
      nl = len_trim(name)
      ordre(m+1:m+nl+1) = name(1:nl)
    else
      ! Argument is a standard argument
      ordre(m+1:m+1-ibeg+icha) = line(ibeg:icha)
      nl = icha-ibeg
    endif
    !
    ! Set the pointers for previous argument
    command%ibeg(command%nword) = m+1
    command%iend(command%nword) = m+nl
    command%narg(jopt) = jarg
    m = m+nl+1
    inarg = .false.
  end subroutine stop_argument
  !
end subroutine sic_parse_line
!
subroutine sic_parse_command(line,nline,iquiet,library,ilang,icom,ocode,error)
  use gbl_message
  use sic_dependencies_interfaces
  use sic_interfaces, except_this=>sic_parse_command
  use sic_dictionaries
  use sic_interactions
  !---------------------------------------------------------------------
  ! @ private
  !  Parse the input string of the form:
  !    LANG\COMMAND
  ! and decode the language and command.
  ! No global variable modified here
  !
  ! - IQUIET behaviour:
  !    IQUIET = 0: Verbose
  !    IQUIET = 1: Quiet
  !    IQUIET = 2: Verbose on ambiguity
  !
  ! - OCODE is used to return a thiner information on error than the
  !   on/off ERROR flag:
  !    OCODE = 0: no error
  !    OCODE = 1: Unknown command
  !    OCODE = 2: No options allowed for command
  !    OCODE = 3: Name is fully ambiguous
  !    OCODE = 4: Name is partially ambiguous
  !---------------------------------------------------------------------
  character(len=*),    intent(in)    :: line     ! String to parse
  integer(kind=4),     intent(in)    :: nline    ! Length of string
  integer(kind=4),     intent(in)    :: iquiet   ! Verbosity
  logical,             intent(in)    :: library  ! Library mode?
  integer(kind=4),     intent(out)   :: ilang    ! Language found
  integer(kind=4),     intent(out)   :: icom     ! Command found
  integer(kind=4),     intent(out)   :: ocode    ! Error code
  logical,             intent(inout) :: error    ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='PARSE'
  integer(kind=4) :: il,nmatch,nfull,iprio,jlang,i,j,nc,nlan,mcom,n
  integer(kind=4) :: width,nperline,tt_width
  character(len=1), parameter :: backslash=char(92)
  character(len=language_length) :: lang
  character(len=message_length) :: chain,mess
  integer(kind=4) :: sever(0:2)
  ! Data
  data sever /seve%e,seve%w,seve%w/
  !
  ! NOPT(0) is the actual vocabulary length
  if (hasopt(0).eq.0) then
    if (iquiet.eq.0)  &
      call sic_message(seve%e,rname,'No language loaded, no command found')
    error = .true.
    ocode = 2
    return
  endif
  !
  ocode = 0
  !
  ! Max width used to print language + command (on ambiguity):
  ! Might truncate command if needed.
  width = language_length+command_length-6 ! 12+12-6 = 18
  ! Reserve 1 blank between each, keep floor value:
  tt_width = sic_ttyncol()
  nperline = tt_width/(width+1)    ! 76/(18+1) = 4
  !
  chain = ' '
  nc = 1
  nmatch = 0
  nfull = 0
  !
  ! Language specified, examine all the vocabulary of which the language
  ! field is an abbreviation
  il = index(line(1:nline),backslash)
  !
  do iprio=1,nprio
    do jlang = 1,klang(iprio)
      i = olang(jlang,iprio)
      ! the above 4 lines
      ! OR the next 1 line
      ! Avoid languages not specified
      if (il.ne.0) then
        if (line(1:il-1).ne.languages(i)%name(1:il-1)) cycle ! JLANG
      endif
      !
      ! "LIBRARY" flag set : avoid "library only" or sleeping
      ! languages unless the full language name is specified
      !
      if (library) then
        if (languages(i)%asleep .or. languages(i)%libmod) then
          if (il.ne.0) then
            if (line(1:il-1).ne.languages(i)%name) cycle ! JLANG
          else
            cycle              ! JLANG
          endif
        endif
      endif
      !
      ! Setup the language to be examined
      j = mbuilt(i-1)
      mcom = mbuilt(i)
      lang = languages(i)%name
      nlan = languages(i)%lname  ! Stored for optimisation
      if (nline-il.gt.command_length-1) then   ! -ISIG?
        if (iquiet.eq.0) then
          n = min(il+command_length-1,nline)
          write(mess,107) line(1:n),'...'
          call sic_message(seve%e,rname,mess)
        endif
        ocode = 1
        goto 99
      endif
      !
      ! Now loop over the commands
400   j = j+1
      !
      if (line(il+1:nline).eq.vocab(j)(2:)) then
        ! Full match:
        if (nfull.ne.0) then
          ocode = 3
          if (nfull.eq.1) then
            call sic_message(seve%e,rname,'Fully ambiguous name. Use '//  &
            'language to distinguish.')
          endif
          if (nmatch.eq.1) then
            ! Previous match, even partial (look in previously saved ILANG and ICOM)
            chain(1:width) = languages(ilang)%name(1:languages(ilang)%lname)//  &
              backslash//vocab(icom)(2:)
            nc = width+2       ! Pointer position (keep a blank)
          endif
          if (nc+width-1.gt.tt_width) then ! Need flush?
            if (nmatch.le.nperline) then
              call sic_message(sever(iquiet),rname,  &
                'Ambiguous command could be :')
            endif
            call sicmsg(chain(1:nc-2))
            nc = 1
            chain = ' '
          endif
          ! Append current full match
          ! NC = 1st char position; NC+WIDTH-1 = last char position; total width = WIDTH
          chain(nc:nc+width-1) = lang(1:nlan)//backslash//vocab(j)(2:)
          nc = nc+width+1
        endif
        nfull = nfull+1
        nmatch = nmatch+1
        icom = j
        ilang = i
        !
      elseif (line(il+1:nline).eq.vocab(j)(2:nline-il+1)) then
        ! Partial match:
        if (nmatch.eq.0) then
          nmatch = 1
          icom = j
          ilang = i
        else
          ocode = 4
          if (iquiet.ne.1) then
            if (nmatch.eq.1) then
              ! Previous match (look in previously saved ILANG and ICOM)
              chain(1:width) =  &
                languages(ilang)%name(1:languages(ilang)%lname)//  &
                backslash//vocab(icom)(2:)
              nc = width+2
            endif
            if (nc+width-1.gt.tt_width) then   ! Need flush?
              if (nmatch.le.nperline) then
                ! Buffer is full, need to flush. Assume there will be 0
                ! or 2+ full ambiguity besides this partial ambiguity.
                call sic_message(sever(iquiet),rname,  &
                  'Ambiguous command could be :')
              endif
              call sicmsg(chain(1:nc-2))
              nc = 1
              chain = ' '
            endif
            chain(nc:nc+width-1) = lang(1:nlan)//backslash//vocab(j)(2:)
            nc = nc+width+1
          endif
          nmatch = nmatch+1
        endif
      endif
      !
      ! Skip over options
      j = j+nopt(j)
      if (j.lt.mcom) goto 400
      ! or the next 2 lines
    enddo  ! jlang
    if (nmatch.ne.0)  exit
  enddo  ! iprio
  !
  ! End of analysis
  if (nc.gt.1 .and. nfull.ne.1) then ! Is there any command pending?
    if (nmatch.le.nperline) then
      call sic_message(sever(iquiet),rname,'Ambiguous command could be :')
    endif
    call sicmsg(chain(1:nc-2))
    nc = 1
    chain = ' '
  endif
  if (nmatch.eq.0) then
    if (iquiet.eq.0) then
      write(mess,107) line(1:min(il+command_length-1,nline))
      call sic_message(seve%e,rname,mess)
    endif
    ocode = 1
    goto 99
  endif
  if (nfull.eq.1) nmatch = 1 ! If just one complete match, this is OK...
  if (nmatch.ne.1) goto 99
  !
  return
  !
99 error = .true.
  return
  !
107 format ('Unknown command ',10(a))
end subroutine sic_parse_command
!
subroutine sic_parse_option(line,nline,iquiet,jcom,iopt,error)
  use gbl_message
  use sic_dependencies_interfaces
  use sic_interfaces, except_this=>sic_parse_option
  use sic_dictionaries
  use sic_interactions
  !---------------------------------------------------------------------
  ! @ private
  !  Analyse an option of the /OPTION.
  ! No global variable modified here.
  !
  ! - IQUIET behaviour:
  !    IQUIET = 0: Verbose
  !    IQUIET = 1: Quiet
  !    IQUIET = 2: Verbose on ambiguity
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: line     ! String to parse
  integer(kind=4),  intent(in)    :: nline    ! Length of string
  integer(kind=4),  intent(in)    :: iquiet   ! Verbosity
  integer(kind=4),  intent(in)    :: jcom     ! Parent command
  integer(kind=4),  intent(out)   :: iopt     ! Option found
  logical,          intent(inout) :: error    ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='PARSE'
  integer(kind=4) :: j,nmatch
  integer(kind=4), parameter :: isig=1  ! Skip leading "/" for options (old value ISIG=2)
  character(len=message_length) :: mess
  integer(kind=4) :: sever(0:2)
  integer(kind=4) :: ikeys(30)  ! At most 30 ambiguous options
  ! Data
  data sever /seve%e,seve%w,seve%w/
  !
  ! Check if there are any options to this command
  if (hasopt(jcom).eq.0) then
    ! No option declared, no option allowed
    if (iquiet.eq.0) then
      write(mess,'(A,A)') 'No options allowed for command ',vocab(jcom)(2:)
      call sic_message(seve%e,rname,mess)
    endif
    error = .true.
    return
  endif
  !
  iopt = 0
  nmatch = 0
  if (nline.gt.command_length+1-isig) then
    if (iquiet.eq.0) then
      write(mess,209) line(1:command_length+1-isig),vocab(jcom)(isig:)
      call sic_message(seve%e,rname,mess)
    endif
    error = .true.
    return
  endif
  !
  ! Now loop over the options
  do j=jcom+1,jcom+nopt(jcom)
    if (line(1:nline).eq.vocab(j)(1:nline)) then
      if (nmatch.eq.0) then
        nmatch = 1
        iopt = j
      else
        if (iquiet.ne.1) then
          ! Keep list of ambiguous matching options
          if (nmatch.eq.1)  ikeys(nmatch) = iopt  ! First match
          ikeys(nmatch+1) = j                     ! New match
        endif
        nmatch = nmatch+1
      endif
    endif
  enddo
  !
  ! End of analysis
  if (nmatch.eq.0) then
    if (iquiet.eq.0) then
      write(mess,109) line(1:min(command_length+1-isig,nline)),vocab(jcom)(isig:)
      call sic_message(seve%e,rname,mess)
    endif
    error = .true.
  elseif (nmatch.gt.1)  then
    if (iquiet.ne.1) then
      ! Print list of ambiguous matching options
      call sic_ambigs_list(rname,sever(iquiet),  &
        'Ambiguous option for command '//trim(vocab(jcom)(2:))//' could be :',  &
        vocab,ikeys(1:nmatch))
    endif
    error = .true.
  endif
  !
109 format ('Unknown option ',a,' for command ',a)
209 format ('Unknown option ',a,'... for command ',a)
end subroutine sic_parse_option
!
subroutine sic_lang(current_language)
  use sic_dictionaries
  !---------------------------------------------------------------------
  ! @ public
  !  Returns the language of the current command
  !---------------------------------------------------------------------
  character(len=*) :: current_language  !
  current_language = ccomm%lang
end subroutine sic_lang
!
subroutine sic_ilang(ilang,language)
  use sic_dictionaries
  !---------------------------------------------------------------------
  ! @ public
  !  Returns the name of the i-th language
  !---------------------------------------------------------------------
  integer(kind=4),  intent(in)  :: ilang
  character(len=*), intent(out) :: language
  language = languages(ilang)%name
end subroutine sic_ilang
!
subroutine sic_comm(current_command)
  use sic_dictionaries
  !---------------------------------------------------------------------
  ! @ public
  !  Return the current command number (number in language, options
  ! excluded).
  !---------------------------------------------------------------------
  integer(kind=4) :: current_command
  ! Local
  integer(kind=4) :: jcomm
  !
  ! The internal buffers do not store directly this information. Start
  ! from the beginning of the current language, and run through commands
  ! to find the correct one.
  !
  current_command = 1
  jcomm = mbuilt(ccomm%ilang-1)+1  ! Command position in the mbuilt vocabulary
  do
    if (jcomm.eq.ccomm%icom)  exit
    jcomm = jcomm+nopt(jcomm)+1  ! Skip over options
    current_command = current_command+1
  enddo
  !
end subroutine sic_comm
