!> @file output_format_list.f90 !> @brief Parse a space/comma-separated output_format list and derive per-format !! filenames. Pure string utility — no solver dependency. module output_format_list implicit none private public :: parse_format_list, ext_for_format, derived_filename, max_formats integer, parameter :: max_formats = 4 contains !> Split `spec` on spaces/commas into deduped tokens validated against !! `allowed`. Assumes `spec` is already lowercased by the config layer. subroutine parse_format_list(spec, allowed, tokens, n, is_ok, message) character(len=*), intent(in) :: spec character(len=*), intent(in) :: allowed(:) character(len=8), intent(out) :: tokens(max_formats) integer, intent(out) :: n logical, intent(out) :: is_ok character(len=*), intent(out) :: message character(len=len(spec)) :: work integer :: i, j, k character(len=8) :: tok logical :: is_allowed, is_dup is_ok = .false.; message = ''; n = 0; tokens = '' work = spec do i = 1, len(work) if (work(i:i) == ',') work(i:i) = ' ' end do i = 1 do do while (i <= len(work)) if (work(i:i) /= ' ') exit i = i + 1 end do if (i > len(work)) exit j = i do while (j <= len(work)) if (work(j:j) == ' ') exit j = j + 1 end do if (j - i > len(tok)) then message = 'output_format: token too long'; return end if tok = work(i:j - 1) i = j is_allowed = .false. do k = 1, size(allowed) if (trim(tok) == trim(allowed(k))) then is_allowed = .true.; exit end if end do if (.not. is_allowed) then message = 'output_format: unknown token "'//trim(tok)//'"'; return end if is_dup = .false. do k = 1, n if (trim(tokens(k)) == trim(tok)) then is_dup = .true.; exit end if end do if (.not. is_dup) then if (n == max_formats) then message = 'output_format: too many formats (max 4)'; return end if n = n + 1 tokens(n) = tok end if end do if (n == 0) then message = 'output_format: empty list'; return end if is_ok = .true. end subroutine parse_format_list !> Canonical file extension for a format token. pure function ext_for_format(token) result(ext) character(len=*), intent(in) :: token character(len=4) :: ext select case (trim(token)) case ('dat'); ext = '.dat' case ('vtk'); ext = '.vtk' case ('tec'); ext = '.tec' case ('plt'); ext = '.plt' case default; ext = '.dat' end select end function ext_for_format !> Derive the output filename for `token`. One format -> base verbatim; !! many formats -> stem (a trailing known extension stripped) + canonical ext. function derived_filename(base, token, n_formats) result(fname) character(len=*), intent(in) :: base, token integer, intent(in) :: n_formats character(len=:), allocatable :: fname character(len=:), allocatable :: stem integer :: m if (n_formats <= 1) then fname = trim(base) return end if stem = trim(base) m = len(stem) if (m >= 4) then select case (stem(m - 3:m)) case ('.dat', '.vtk', '.tec', '.plt') stem = stem(1:m - 4) end select end if fname = stem//trim(ext_for_format(token)) end function derived_filename end module output_format_list