1 !***********************************************************************
2 !* GNU Lesser General Public License
4 !* This file is part of the GFDL Flexible Modeling System (FMS).
6 !* FMS is free software: you can redistribute it and/or modify it under
7 !* the terms of the GNU Lesser General Public License as published by
8 !* the Free Software Foundation, either version 3 of the License, or (at
9 !* your option) any later version.
11 !* FMS is distributed in the hope that it will be useful, but WITHOUT
12 !* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 !* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 !* You should have received a copy of the GNU Lesser General Public
17 !* License along with FMS. If not, see <http://www.gnu.org/licenses/>.
18 !***********************************************************************
20 !> @defgroup fms_string_utils_mod fms_string_utils_mod
21 !> @ingroup string_utils
22 !> @brief Routines to use for string manipulation
25 !> @brief File for @ref fms_string_utils_mod
27 !> @addtogroup fms_string_utils_mod
29 module fms_string_utils_mod
30 use, intrinsic :: iso_c_binding
31 use platform_mod, only: r4_kind, r8_kind, i4_kind, i8_kind
37 public :: fms_array_to_pointer
38 public :: fms_pointer_to_array
39 public :: fms_sort_this
40 public :: fms_find_my_string
41 public :: fms_find_unique
42 public :: fms_c2f_string
43 public :: fms_f2c_string
44 public :: fms_cstring2cpointer
51 !> @brief Sorts an array of pointers (my pointer) of size (p_size) in
52 !! alphabetical order.
53 subroutine fms_sort_this(my_pointer, p_size, indices) bind(c)
56 type(c_ptr), intent(inout) :: my_pointer(*) !< IN: Array of c pointers to sort
57 !! OUT: Sorted array of c pointers
58 integer(kind=c_int), intent(in) :: p_size !< Size of the array
59 integer(kind=c_int), intent(inout) :: indices(*) !< IN: Array of the indices of my_pointer
60 !! OUT: Sorted array of indices
61 end subroutine fms_sort_this
63 !> @brief Private c function that finds a string in a SORTED array of c pointers
64 !! @return Indices of my_pointer where the string was found as a string!!!
65 function fms_find_my_string_binding(my_pointer, p_size, string_to_find, nfound) bind(c) &
69 type(c_ptr), intent(in) :: my_pointer(*) !< Array of sorted c pointer
70 integer(kind=c_int), intent(in) :: p_size !< Size of the array
71 character(kind=c_char), intent(in) :: string_to_find(*) !< String to find
72 integer(kind=c_int), intent(inout) :: nfound !< Number of times the array was found
74 type(c_ptr) :: indices
75 end function fms_find_my_string_binding
77 !> @brief c function that finds the number of unique strings in an array of c pointers
78 !! @return number of unique strings
79 function fms_find_unique(my_pointer, p_size) bind(c)&
83 type(c_ptr), intent(in) :: my_pointer(*) !< Array of sorted c pointer
84 integer(kind=c_int), intent(in) :: p_size !< Size of the array
85 integer(kind=c_int) :: ntimes
87 end function fms_find_unique
89 !> @brief converts a kind=c_char to type c_ptr
90 pure function fms_cstring2cpointer (cs) result (cp) bind(c, name="cstring2cpointer")
92 character(kind=c_char), intent(in) :: cs(*) !< C string input
93 type (c_ptr) :: cp !< C pointer
94 end function fms_cstring2cpointer
96 !> @brief Finds the length of a C-string
97 integer(c_size_t) pure function c_strlen(s) bind(c,name="strlen")
98 import c_size_t, c_ptr
99 type(c_ptr), intent(in), value :: s !< A C-string whose size is desired
102 !> @brief Frees a C pointer
103 subroutine c_free(ptr) bind(c,name="free")
105 type(c_ptr), value :: ptr !< A C-pointer to free
110 !> Converts a C string to a Fortran string
112 interface fms_c2f_string
113 module procedure cstring_fortran_conversion
114 module procedure cpointer_fortran_conversion
117 !> Converts an array of real numbers to a string
120 module procedure stringify_1d_r4, stringify_1d_r8
121 module procedure stringify_2d_r4, stringify_2d_r8
122 module procedure stringify_3d_r4, stringify_3d_r8
125 !> @addtogroup fms_string_utils_mod
129 !> @brief Converts a character array to an array of c pointers!
130 !! @return An array of c pointers
131 function fms_array_to_pointer(my_array) &
133 character(len=*), target :: my_array(:) !!< Array of strings to convert
134 type(c_ptr), allocatable :: my_pointer(:)
136 integer :: i !< For do loops
138 if (allocated(my_pointer)) call mpp_error(FATAL, "The c pointer array is &
139 already allocated. Deallocated before calling fms_array_to_pointer")
140 allocate(my_pointer(size(my_array)))
142 do i = 1, size(my_array)
143 my_pointer(i) = c_loc(my_array(i))
145 end function fms_array_to_pointer
147 !> @brief Convert an array of c pointers back to a character array
148 !! @return A character array
149 function fms_pointer_to_array(my_pointer, narray) &
151 type(c_ptr), intent(in) :: my_pointer(*) !< Array of c pointer
152 integer, intent(in) :: narray !< Length of the array
153 character(len=:), allocatable :: my_array(:)
155 character(len=:), allocatable :: buffer !< Buffer to store a string
156 integer :: i !< For do loops
158 allocate(character(len=255) :: my_array(narray))
160 buffer = fms_c2f_string(my_pointer(i))
164 end function fms_pointer_to_array
166 !> @brief Searches through a SORTED array of pointers for a string
167 !! @return the indices where the array was found
168 !! If the string was not found, indices will be indices(1) = -999
169 !> <br>Example usage:
170 !! my_pointer = fms_array_to_pointer(my_array)
171 !! call fms_sort_this(my_pointer, n_array, indices)
172 !! ifind = fms_find_my_string(my_pointer, n_array, string_to_find)
173 function fms_find_my_string(my_pointer, narray, string_to_find) &
175 type(c_ptr), intent(in) :: my_pointer(*) !< Array of c pointer
176 integer, intent(in) :: narray !< Length of the array
177 character(len=*), intent(in) :: string_to_find !< string to find
178 integer, allocatable :: ifind(:)
180 integer :: nfind !< number of times the string was found
181 character(len=:), allocatable :: buffer !< buffer to read the indices into
183 buffer = fms_c2f_string(&
184 fms_find_my_string_binding(my_pointer, narray, trim(string_to_find)//c_null_char, nfind))
186 if (allocated(ifind)) call mpp_error(FATAL, "The indices array is already allocated. &
187 Deallocate it before calling fms_find_my_string")
189 if (nfind .gt. 0) then
190 allocate(ifind(nfind))
197 end function fms_find_my_string
199 !> \brief Converts a C-string to a pointer and then to a Fortran string
200 function cstring_fortran_conversion (cstring) result(fstring)
201 character (kind=c_char), intent(in) :: cstring (*) !< Input C-string
202 character(len=:), allocatable :: fstring !< The fortran string returned
203 fstring = cpointer_fortran_conversion(fms_cstring2cpointer(cstring))
204 end function cstring_fortran_conversion
206 !> \brief Converts a C-string returned from a TYPE(C_PTR) function to
207 !! a fortran string with type character.
208 function cpointer_fortran_conversion (cstring) result(fstring)
209 type (c_ptr), intent(in) :: cstring !< Input C-pointer
210 character(len=:), allocatable :: fstring !< The fortran string returned
211 character(len=:,kind=c_char), pointer :: string_buffer !< A temporary pointer to between C and Fortran
212 integer(c_size_t) :: length !< The string length
214 length = c_strlen(cstring)
215 allocate (character(len=length, kind=c_char) :: string_buffer)
217 character(len=length,kind=c_char), pointer :: s
218 call c_f_pointer(cstring,s) ! Recovers a view of the C string
219 string_buffer = s ! Copies the string contents
222 allocate(character(len=length) :: fstring) !> Set the length of fstring
223 fstring = string_buffer
224 deallocate(string_buffer)
225 end function cpointer_fortran_conversion
227 !> @brief Copies a Fortran string into a C string and puts c_null_char in any trailing spaces
228 subroutine fms_f2c_string (dest, str_in)
229 character (c_char), intent (out) :: dest (:) !< C String to be copied into
230 character (len=*), intent (in) :: str_in !< Fortran string to copy to C string
231 integer :: i !< for looping
232 !> Drop an error if the C string is not large enough to hold the input and the c_null_char at the end.
233 if (len(trim(str_in)) .ge. size(dest)) call mpp_error(FATAL, &
234 "The string "//trim(str_in)//" is larger than the destination C string")
235 !> Copy c_null_char into each spot in dest
237 !> Loop though and put each character of the Fortran string into the C string array
238 do i = 1, len(trim(str_in))
239 dest(i) = str_in(i:i)
241 end subroutine fms_f2c_string
243 !> @brief Converts a number or a Boolean value to a string
244 !> @return The argument as a string
245 function string(v, fmt)
246 class(*), intent(in) :: v !< Value to be converted to a string
247 character(*), intent(in), optional :: fmt !< Optional format string for a real or integral argument
248 character(:), allocatable :: string
252 if (present(fmt)) then
253 call mpp_error(WARNING, "string(): Ignoring `fmt` argument for type `logical`")
261 type is (integer(i4_kind))
262 allocate(character(32) :: string)
263 if (present(fmt)) then
264 write(string, "(" // fmt // ")") v
266 write(string, '(i0)') v
268 string = trim(adjustl(string))
270 type is (integer(i8_kind))
271 allocate(character(32) :: string)
272 if (present(fmt)) then
273 write(string, "(" // fmt // ")") v
275 write(string, '(i0)') v
277 string = trim(adjustl(string))
279 type is (real(r4_kind))
280 allocate(character(32) :: string)
281 if (present(fmt)) then
282 write(string, "(" // fmt // ")") v
286 string = trim(adjustl(string))
288 type is (real(r8_kind))
289 allocate(character(32) :: string)
290 if (present(fmt)) then
291 write(string, "(" // fmt // ")") v
295 string = trim(adjustl(string))
298 call mpp_error(FATAL, "string(): Called with incompatible argument type. Possible types &
299 &include integer(4), integer(8), real(4), real(8), or logical.")
303 !> @brief Safely copy a string from one buffer to another.
304 subroutine string_copy(dest, source, check_for_null)
305 character(len=*), intent(inout) :: dest !< Destination string.
306 character(len=*), intent(in) :: source !< Source string.
307 logical, intent(in), optional :: check_for_null !<Flag indicating to test for null character
310 logical :: check_null
313 if (present(check_for_null)) check_null = check_for_null
317 i = index(source, char(0)) - 1
320 if (i < 1 ) i = len_trim(source)
322 if (len_trim(source(1:i)) .gt. len(dest)) then
323 call mpp_error(FATAL, "The input destination string is not big enough to" &
324 //" to hold the input source string.")
327 dest = adjustl(trim(source(1:i)))
328 end subroutine string_copy
330 #include "fms_string_utils_r4.fh"
331 #include "fms_string_utils_r8.fh"
333 end module fms_string_utils_mod
335 ! close documentation grouping