module m_charset
!
! One-byte only, sorry
!
private

integer, parameter, private  :: small_int = selected_int_kind(1)

!--------------------------------------------------------------------------
type, public :: charset_t
! private
      integer(kind=small_int), dimension(0:255) :: mask
end type charset_t


public  :: operator(.in.), operator(+)
public  :: assignment(=)
public  :: print_charset, reset_charset

interface operator(.in.)
      module procedure belongs
end interface
private :: belongs

interface assignment(=)
      module procedure set_string_to_charset, set_codes_to_charset
end interface
private ::  set_string_to_charset, set_codes_to_charset

interface operator(+)
      module procedure add_string_to_charset, &
                       add_code_to_charset, add_codes_to_charset
end interface
private :: add_string_to_charset, add_code_to_charset, add_codes_to_charset

!--------------------------------------------------------------------------

character(len=*), parameter, private :: &
            lowercase = "abcdefghijklmnopqrstuvwxyz",  & 
            uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ", &
            digits    = "0123456789"

integer, parameter, public   :: SPACE           = 32
integer, parameter, public   :: NEWLINE         = 10
integer, parameter, public   :: CARRIAGE_RETURN = 13
integer, parameter, public   :: TAB             =  9

type(charset_t), public    :: initial_name_chars
type(charset_t), public    :: name_chars
type(charset_t), public    :: whitespace
type(charset_t), public    :: valid_chars
type(charset_t), public    :: uppercase_chars

public  :: setup_xml_charsets


CONTAINS !==========================================================

!--------------------------------------------------------------
function belongs(c,charset)  result(res)
character(len=1), intent(in)  :: c
type(charset_t), intent(in)   :: charset
logical                       :: res

integer  :: code

code = ichar(c)
res = (charset%mask(code) == 1)

end function belongs

!--------------------------------------------------------------

function add_string_to_charset(charset,str)  result (sum)
type(charset_t), intent(in)      :: charset
character(len=*), intent(in)     :: str
type(charset_t)                  :: sum

integer :: length, code, i

sum%mask = charset%mask

length = len_trim(str)
do i = 1, length
      code = ichar(str(i:i))
      sum%mask(code) = 1
enddo
end function add_string_to_charset

!--------------------------------------------------------------

function add_code_to_charset(charset,code) result(sum)
type(charset_t), intent(in)      :: charset
integer, intent(in)              :: code
type(charset_t)                  :: sum

if ((code > 255) .or. (code < 0)) return
sum%mask = charset%mask
sum%mask(code) = 1

end function add_code_to_charset

!--------------------------------------------------------------
function add_codes_to_charset(charset,codes) result(sum)
type(charset_t), intent(in)        :: charset
integer, dimension(:), intent(in)  :: codes
type(charset_t)                    :: sum

integer  :: i

sum%mask = charset%mask
do i = 1, size(codes)
      if ((codes(i) > 255) .or. (codes(i) < 0)) cycle
      sum%mask(codes(i)) = 1
enddo
end function add_codes_to_charset

!--------------------------------------------------------------

subroutine set_string_to_charset(charset,str)      
type(charset_t), intent(out)   :: charset
character(len=*), intent(in)   :: str


integer :: length, code, i

charset%mask = 0

length = len_trim(str)
do i = 1, length
      code = ichar(str(i:i))
      charset%mask(code) = 1
enddo

end subroutine set_string_to_charset

!--------------------------------------------------------------

subroutine set_codes_to_charset(charset,codes)      
type(charset_t), intent(out)   :: charset
integer, dimension(:), intent(in)  :: codes

integer :: i

charset%mask = 0

do i = 1, size(codes)
      charset%mask(codes(i)) = 1
enddo

end subroutine set_codes_to_charset


!--------------------------------------------------------------
subroutine print_charset(charset)      
type(charset_t), intent(in)   :: charset

integer :: i

do i = 0, 255
      if (charset%mask(i) == 1) print *, "Code: ", i
enddo
end subroutine print_charset

!--------------------------------------------------------------

subroutine reset_charset(charset)      
type(charset_t), intent(inout)   :: charset

integer :: i

do i = 0, 255
      charset%mask(i) = 0
enddo
end subroutine reset_charset

!--------------------------------------------------------------

!--------------------------------------------------------
subroutine setup_xml_charsets()

integer :: i

uppercase_chars = uppercase
initial_name_chars = (lowercase  // uppercase //  "_:" )
name_chars = initial_name_chars + ( digits // ".-")
whitespace = (/ SPACE, NEWLINE, TAB, CARRIAGE_RETURN /)

valid_chars = whitespace + (/ (i, i=33,255) /)

end subroutine setup_xml_charsets
!--------------------------------------------------------

end module m_charset









