source: trunk/LMDZ.TITAN/libf/muphytitan/string_op.F90 @ 3026

Last change on this file since 3026 was 1897, checked in by jvatant, 7 years ago

Making Titan's hazy again - part II
+ Major updates of J.Burgalat YAMMS library and optical coupling, including :
++ Added the routines for haze optics inside YAMMS
++ Calling rad. transf. with interactive haze is plugged
in but should stay unactive as long as the microphysics is
in test phase : cf "uncoupl_optic_haze" flag : true for now !
++ Also some sanity checks for negative tendencies and
some others upkeep of YAMMS model
+ Also added a temporary CPP key USE_QTEST in physiq_mod
that enables to have microphysical tendencies separated
from dynamics for debugging and test phases
-- JVO and JB

File size: 102.0 KB
Line 
1! Copyright Jérémie Burgalat (2013-2015,2017)
2!
3! jeremie.burgalat@univ-reims.fr
4!
5! This software is a computer program whose purpose is to provide configuration
6! file and command line arguments parsing features to Fortran programs.
7!
8! This software is governed by the CeCILL-B license under French law and
9! abiding by the rules of distribution of free software.  You can  use,
10! modify and/ or redistribute the software under the terms of the CeCILL-B
11! license as circulated by CEA, CNRS and INRIA at the following URL
12! "http://www.cecill.info".
13!
14! As a counterpart to the access to the source code and  rights to copy,
15! modify and redistribute granted by the license, users are provided only
16! with a limited warranty  and the software's author,  the holder of the
17! economic rights,  and the successive licensors  have only  limited
18! liability.
19!
20! In this respect, the user's attention is drawn to the risks associated
21! with loading,  using,  modifying and/or developing or reproducing the
22! software by the user in light of its specific status of free software,
23! that may mean  that it is complicated to manipulate,  and  that  also
24! therefore means  that it is reserved for developers  and  experienced
25! professionals having in-depth computer knowledge. Users are therefore
26! encouraged to load and test the software's suitability as regards their
27! requirements in conditions enabling the security of their systems and/or
28! data to be ensured and,  more generally, to use and operate it in the
29! same conditions as regards security.
30!
31! The fact that you are presently reading this means that you have had
32! knowledge of the CeCILL-B license and that you accept its terms.
33
34!! file: strings.F90
35!! summary: Strings manipulation source file
36!! author: J. Burgalat
37!! date: 2013-2015,2017
38
39#include "defined.h"
40
41MODULE STRING_OP
42  !! Fortran strings manipulation module
43  !!
44  !! This module provides methods and objects to manipulate Fortran (allocatable) strings. It defines
45  !! a doubly linked-list of strings, [[string_op(module):words(type)]] and several methods to format
46  !! strings or convert them in other intrinsic types.
47  USE ERRORS
48  IMPLICIT NONE
49 
50  PRIVATE
51
52
53
54  PUBLIC ::  str2dble_sc,str2dble_ve,str2real_sc,str2real_ve
55  ! errors module (not used but propagated)
56  PUBLIC :: stdout,stderr,noerror,error, error_to_string,aborting
57 
58  ! misc module methods
59  PUBLIC :: to_string, from_string, string_is, remove_quotes, format_string,     &
60            format_paragraph, strip_newline, tokenize, str_length, endswith, &
61            startswith, to_lower, to_upper, add_csi,      &
62            del_csi, reset_csi, str_remove, str_replace,&
63            fancy
64
65  ! words object related methods
66  PUBLIC :: words_length, words_insert, words_append, words_prepend, words_get, &
67            words_set, words_get_max_width, words_get_total_width, words_pop,   &
68            words_remove, words_next, words_previous, words_reset,              &
69            words_valid, words_current, words_extend, words_reverse,            &
70            words_reversed, words_dump, words_to_string, words_to_vector,       &
71            words_clear
72
73  PRIVATE :: fis_affect_int, fis_affect_bool, fis_affect_real,        &
74             fis_affect_double, fis_affect_cplx, fis_affect_dcplx,    &
75             fis_cat_int, fis_cat_bool, fis_cat_real, fis_cat_double, &
76             fis_cat_cplx, fis_cat_dcplx, fis_cat_int_inv,            &
77             fis_cat_bool_inv, fis_cat_real_inv, fis_cat_double_inv,  &
78             fis_cat_cplx_inv, fis_cat_dcplx_inv
79
80  ! Operators
81  PUBLIC :: ASSIGNMENT(=), OPERATOR(/=), OPERATOR(==)
82
83  INTEGER, PUBLIC, PARAMETER :: st_string  = 1 !! String type ID
84  INTEGER, PUBLIC, PARAMETER :: st_logical = 2 !! Logical type ID
85  INTEGER, PUBLIC, PARAMETER :: st_complex = 3 !! Complex type ID
86  INTEGER, PUBLIC, PARAMETER :: st_integer = 4 !! Integer type ID
87  INTEGER, PUBLIC, PARAMETER :: st_real    = 5 !! Real type ID
88 
89  !> List of types names
90  CHARACTER(len=*), DIMENSION(5), PARAMETER, PUBLIC :: st_type_names = &
91  (/ 'string ', 'logical', 'complex', 'integer', 'real   '/)
92
93  INTEGER, PUBLIC, PARAMETER :: st_slen = SSLEN !! Maximum short string length
94  INTEGER, PUBLIC, PARAMETER :: st_llen = SLLEN !! Maximum long string length
95
96
97 
98  INTEGER, PUBLIC, PARAMETER :: FC_BLACK     = 30 !! Black foreground csi code
99  INTEGER, PUBLIC, PARAMETER :: FC_RED       = 31 !! Red foreground csi code
100  INTEGER, PUBLIC, PARAMETER :: FC_GREEN     = 32 !! Green foreground csi code
101  INTEGER, PUBLIC, PARAMETER :: FC_YELLOW    = 33 !! Yellow foreground csi code
102  INTEGER, PUBLIC, PARAMETER :: FC_BLUE      = 34 !! Blue foreground csi code
103  INTEGER, PUBLIC, PARAMETER :: FC_MAGENTA   = 35 !! Magenta foreground csi code
104  INTEGER, PUBLIC, PARAMETER :: FC_CYAN      = 36 !! Cyan foreground csi code
105  INTEGER, PUBLIC, PARAMETER :: FC_WHITE     = 37 !! White foreground csi code
106  INTEGER, PUBLIC, PARAMETER :: BG_BLACK     = 40 !! Black foreground csi code
107  INTEGER, PUBLIC, PARAMETER :: BG_RED       = 41 !! Black background csi code
108  INTEGER, PUBLIC, PARAMETER :: BG_GREEN     = 42 !! Green background csi code
109  INTEGER, PUBLIC, PARAMETER :: BG_YELLOW    = 43 !! Yellow background csi code
110  INTEGER, PUBLIC, PARAMETER :: BG_BLUE      = 44 !! Blue background csi code
111  INTEGER, PUBLIC, PARAMETER :: BG_MAGENTA   = 45 !! Magenta background csi code
112  INTEGER, PUBLIC, PARAMETER :: BG_CYAN      = 46 !! Cyan background csi code
113  INTEGER, PUBLIC, PARAMETER :: BG_WHITE     = 47 !! White background csi code
114  INTEGER, PUBLIC, PARAMETER :: ST_NORMAL    =  0 !! Normal (regular) attribute
115  INTEGER, PUBLIC, PARAMETER :: ST_BOLD      =  1 !! Bold (brighter) attribute
116  INTEGER, PUBLIC, PARAMETER :: ST_ITALIC    =  3 !! Italic attribute (sometimes reverse video or underline)
117  INTEGER, PUBLIC, PARAMETER :: ST_UNDERLINE =  4 !! Underline attribute
118  INTEGER, PUBLIC, PARAMETER :: ST_BLINK     =  5 !! Slow blink mode
119  !> List of all attributes in a vector
120  INTEGER, PUBLIC, PARAMETER, DIMENSION(21) :: attributes = [FC_BLACK,     &
121                                                             FC_RED,       &
122                                                             FC_GREEN,     &
123                                                             FC_YELLOW,    &
124                                                             FC_BLUE,      &
125                                                             FC_MAGENTA,   &
126                                                             FC_CYAN,      &
127                                                             FC_WHITE,     &
128                                                             BG_BLACK,     &
129                                                             BG_RED,       &
130                                                             BG_GREEN,     &
131                                                             BG_YELLOW,    &
132                                                             BG_BLUE,      &
133                                                             BG_MAGENTA,   &
134                                                             BG_CYAN,      &
135                                                             BG_WHITE,     &
136                                                             ST_NORMAL,    &
137                                                             ST_BOLD,      &
138                                                             ST_ITALIC,    &
139                                                             ST_UNDERLINE, &
140                                                             ST_BLINK      &
141                                                            ]     
142 
143  !> Aliases for CSI codes.
144  CHARACTER(len=2), DIMENSION(21), PARAMETER, PUBLIC :: csis =(/ &
145                "fk", "fr", "fg", "fy", "fb", "fm", "fc", "fw", &
146                "bk", "br", "bg", "by", "bb", "bm", "bc", "bw", &
147                 "sn", "sb", "si", "su", "sk"/)
148
149  !> [[words(type)]] object assignement interface
150  INTERFACE ASSIGNMENT(=)
151    MODULE PROCEDURE ws_affect
152  END INTERFACE
153 
154  !> Clear either a scalar or a vector of list of [[words(type)]]
155  !!
156  !! The interface encapsulates words _destructors_, that deallocate memory used
157  !! by the given list(s) of words. This method should be called anytime words
158  !! object(s) is no longer used to avoid memory leaks.
159  !! @note
160  !! If the library support Derived type finalization, calling destructor is not
161  !! mandatory.
162  INTERFACE words_clear
163    MODULE PROCEDURE ws_clear_sc, ws_clear_ve
164  END INTERFACE
165
166  !> Extend a given [[words(type)]] object either by another or by a string
167  !!
168  !! The interface encapsulates two subroutines:
169  !!
170  !! - [[ws_extend_ws(subroutine)]](this,other) which extends __this__ by __other__
171  !!   (both are words objects).
172  !! - [[ws_extend_str(subroutine)]](this,str,delimiter,merge) which splits __str__
173  !!   according to __delimiter__ (and optionally __merge__) and then extends
174  !!   __this__ with the resulting tokens.
175  INTERFACE words_extend
176    MODULE PROCEDURE ws_extend_ws,ws_extend_str
177  END INTERFACE
178
179  !> Convert an intrinsic type value to a string
180  !!
181  !! This (very) generic interface provides conversion functions from
182  !! intrinsic types to ALLOCATED string.
183  !!
184  !! ```
185  !! (1)  FUNCTION to_string(value)     RESULT(str)
186  !! (2)  FUNCTION to_string(value,fmt) RESULT(str)
187  !! ```
188  !! Where :
189  !!
190  !! - __value__ is the value to convert
191  !! - __fmt__ is a string the format descriptor of the output string. Surrounding
192  !!   parenthesis can be omitted.
193  !! - __str__ is an allocatable string with the converted value in output, or an empty
194  !!   string if the conversion failed.
195  INTERFACE to_string
196    MODULE PROCEDURE int2str_as,int2str_fs
197    MODULE PROCEDURE log2str_as,log2str_fs
198    MODULE PROCEDURE real2str_as,real2str_fs
199    MODULE PROCEDURE dble2str_as,dble2str_fs
200    MODULE PROCEDURE cplx2str_as,cplx2str_fs
201    MODULE PROCEDURE dcplx2str_as,dcplx2str_fs
202  END INTERFACE
203 
204  !> Convert a string into an intrisinc type
205  !!
206  !! All methods defined in the interface are functions which take in arguments,
207  !! a string (input) and an output variable with the relevant type (or vectors of both).
208  !! They always return an error object which is set to -5 error code (i.e. cannot cast value)
209  !! on error, otherwise [[errors(module):noerror(variable)]].
210  INTERFACE from_string
211    MODULE PROCEDURE str2int_sc,str2log_sc,str2real_sc,str2dble_sc,str2cplx_sc
212    MODULE PROCEDURE str2int_ve,str2log_ve,str2real_ve,str2dble_ve,str2cplx_ve
213  END INTERFACE
214
215  !> Compute a fancy string
216  !!
217  !! The generic interface adds CSI codes to the given value and returns a fortran intrinsic string.
218  !!
219  !! This is convinient wrapper to [[string_op(module):to_string(interface)]] and
220  !! [[string_op(module):add_csi(function)]].
221  !!
222  !! ```fortran
223  !! FUNCTION fancy(value, flags) RESULT(str)
224  !! ```
225  !!
226  !! **value** can be either a INTEGER,  REAL, REAL(kind=8), COMPLEX, COMPLEX(kind=8) or STRING.
227  !!
228  !! **flags is a vector of (string) attributes that can take the values as defined in the second
229  !! column of the [list of csi attributes](|url|/page/swift/p02_strings.html#fancy-strings).
230  INTERFACE fancy
231    MODULE PROCEDURE fancy_fstr, fancy_int, fancy_real, fancy_double, fancy_cplx, fancy_dcplx
232  END INTERFACE
233
234  !> Overloaded string assignment operator interface
235  INTERFACE ASSIGNMENT(=)
236    MODULE PROCEDURE fis_affect_int, fis_affect_bool, fis_affect_real,    &
237                     fis_affect_double, fis_affect_cplx, fis_affect_dcplx
238  END INTERFACE
239
240  !> Overloaded string concatentation operator interface
241  INTERFACE OPERATOR(//)
242    MODULE PROCEDURE fis_cat_int, fis_cat_bool, fis_cat_real, fis_cat_double, &
243                     fis_cat_cplx, fis_cat_dcplx
244    MODULE PROCEDURE fis_cat_int_inv, fis_cat_bool_inv, fis_cat_real_inv,     &
245                     fis_cat_double_inv, fis_cat_cplx_inv, fis_cat_dcplx_inv
246  END INTERFACE
247
248  !> Define a linked word
249  !!
250  !! Linked words are only intended to be used within a words type.
251  !! It's part of the doubly linked list words.
252  TYPE, PUBLIC :: word
253#if HAVE_FTNDTSTR   
254    CHARACTER(len=:), ALLOCATABLE :: value !! Value of the word
255#else   
256    !> Value of the word
257    !!
258    !! @warning
259    !! It is always limited to [[string_op(module):st_slen(variable)]] characters.
260    CHARACTER(len=st_slen)        :: value = ''
261#endif
262    TYPE(word), PRIVATE, POINTER  :: next => null() !! Next word in the list of words
263    TYPE(word), PRIVATE, POINTER  :: prev => null() !! Previous word in the list of words
264  END TYPE word
265 
266  !> Define a list of words
267  TYPE, PUBLIC :: words
268    INTEGER, PRIVATE             :: nw = 0         !! Number of word in the list
269    TYPE(word), PRIVATE, POINTER :: head => null() !! First word in the list
270    TYPE(word), PRIVATE, POINTER :: tail => null() !! Last word in the list
271    TYPE(word), PRIVATE, POINTER :: iter => null() !! Current word (iterator)
272#if HAVE_FTNPROC
273    CONTAINS
274    PROCEDURE, PRIVATE :: ws_extend_ws
275    PROCEDURE, PRIVATE :: ws_extend_str
276    PROCEDURE, PUBLIC :: length      => words_length
277      !! Get the number of words in the list
278    PROCEDURE, PUBLIC :: insert      => words_insert
279      !! Insert a word at given index
280    PROCEDURE, PUBLIC :: append      => words_append
281      !! Append a word at the end of the list
282    PROCEDURE, PUBLIC :: prepend     => words_prepend
283      !! Prepend a word at the beginning of the list
284    PROCEDURE, PUBLIC :: get         => words_get
285      !! Get the word at given index
286    PROCEDURE, PUBLIC :: set         => words_set
287      !! Set a word at given index
288    PROCEDURE, PUBLIC :: max_width   => words_get_max_width
289      !! Get the width of the biggest word in the list
290    PROCEDURE, PUBLIC :: total_width => words_get_total_width
291      !! Get the total width of the words stored in the list
292    PROCEDURE, PUBLIC :: reverse     => words_reverse
293      !! Reverse the list in place
294    PROCEDURE, PUBLIC :: reversed    => words_reversed
295      !! Get a reversed copy of the list
296    PROCEDURE, PUBLIC :: dump        => words_dump
297      !! Dump words of the list (on per line)
298    PROCEDURE, PUBLIC :: tostring    => words_to_string
299      !! Convert the list in a single string
300    PROCEDURE, PUBLIC :: to_vector   => words_to_vector
301      !! Convert the list in a vector
302    PROCEDURE, PUBLIC :: pop         => words_pop
303      !! Pop a word from the list and returns it
304    PROCEDURE, PUBLIC :: remove      => words_remove
305      !! Remove a word from the list
306    PROCEDURE, PUBLIC :: next        => words_next
307      !! Go to the next word in the list
308    PROCEDURE, PUBLIC :: previous    => words_previous
309      !! Go to the previous word in the list
310    PROCEDURE, PUBLIC :: reset       => words_reset
311      !! Reset the list's iterator
312    PROCEDURE, PUBLIC :: valid       => words_valid
313      !! Check if iterator position is valid
314    PROCEDURE, PUBLIC :: current     => words_current
315      !! Get the current word in the list
316    GENERIC, PUBLIC :: extend => ws_extend_ws,ws_extend_str
317      !! Extend a list with either a string or another list of words
318#endif
319  END TYPE words
320 
321  CONTAINS
322 
323  FUNCTION word_length(this) RESULT(lgth)
324    !! Get the trimmed length of the word object
325    TYPE(word), INTENT(in) :: this
326      !! A word object
327    INTEGER :: lgth
328      !! The length of the word's value (without trailing spaces)
329#if HAVE_FTNDTSTR   
330    IF (.NOT.ALLOCATED(this%value)) THEN
331      lgth = 0 ; RETURN
332    ENDIF
333#endif   
334    lgth = LEN_TRIM(this%value)
335    RETURN
336  END FUNCTION word_length
337 
338  SUBROUTINE disconnect_word(this)
339    !! Disconnect a word object
340    !!
341    !! The object is no more connected to its neighbours which are connected together.
342    !! @note
343    !! After this method is called the object is no longer connected to its parent words
344    !! object and should be deallocated in order to avoid memory leaks.
345    TYPE(word), INTENT(inout) :: this
346      !! A word object to disconnect
347    TYPE(word), POINTER :: pw,nw
348    nw => this%next ; pw => this%prev
349    IF (ASSOCIATED(nw)) nw%prev => pw
350    IF (ASSOCIATED(pw)) pw%next => nw
351    RETURN
352  END SUBROUTINE disconnect_word
353 
354  SUBROUTINE ws_affect(this,other)
355    !! words object assignment operator subroutine
356    TYPE(words), INTENT(out) :: this
357      !! A words object to be assigned
358    TYPE(words), INTENT(in)  :: other
359      !! A words object to assign
360    TYPE(word), POINTER :: cur
361    CALL ws_clear_sc(this)
362    IF (other%nw == 0) THEN
363      RETURN
364    ELSE
365      cur => other%head
366      DO WHILE(associated(cur))
367#if HAVE_FTNDTSTR     
368        IF (.NOT.ALLOCATED(cur%value)) THEN
369          CALL words_append(this,"")
370        ELSE
371          CALL words_append(this,cur%value)
372        ENDIF
373#else
374        CALL words_append(this,cur%value)
375#endif
376        IF (ASSOCIATED(cur,other%iter)) this%iter => this%tail
377        cur => cur%next
378      ENDDO
379    ENDIF
380    RETURN
381  END SUBROUTINE ws_affect
382 
383  SUBROUTINE ini_word(this,value)
384    !! Initialize the first word of a list of words
385    !!
386    !! This subroutine is not a constructor. It is only intended to set the first word
387    !! object in a words object.
388    TYPE(words), INTENT(inout)   :: this
389      !! A words object reference
390    CHARACTER(len=*), INTENT(in) :: value
391      !! A string with the word used to initialize the list
392    ALLOCATE(this%head)
393    this%tail => this%head
394    ASSIGN_DTSTR(value,this%tail%value)
395    this%nw = 1
396    RETURN
397  END SUBROUTINE ini_word
398
399  SUBROUTINE ws_clear_sc(obj)
400    !! Clear a list of words
401    !!
402    !! This subroutine deallocates all memory used by the given words object.
403    !! @warning
404    !! The subroutine should be called whenever a words is no more used (e.g. at
405    !! the end of the current scope), otherwise memory leaks could occur.
406    TYPE(words),INTENT(inout), TARGET :: obj
407      !! A words object to clear
408    TYPE(word), POINTER :: cur,next
409    IF (obj%nw == 0) RETURN
410    cur => obj%head
411    DO WHILE(ASSOCIATED(cur))
412      next => cur%next
413      CALL disconnect_word(cur)
414#if HAVE_FTNDTSTR
415      IF (ALLOCATED(cur%value)) DEALLOCATE(cur%value)
416#endif
417      DEALLOCATE(cur)
418      cur => next
419    ENDDO
420    obj%nw = 0
421    obj%head => null() ; obj%tail => null() ; obj%iter => null()
422  END SUBROUTINE ws_clear_sc
423
424  SUBROUTINE ws_clear_ve(objs)
425    !! Clear a vector of list of words
426    !!
427    !! This subroutine deallocates all memory used by the given vector of words objects.
428    !! @warning
429    !! The subroutine should be called whenever a words is no more used (e.g. at the end
430    !! of the current scope), otherwise memory leaks could occur.
431    TYPE(words),INTENT(inout), DIMENSION(:) :: objs
432      !! A vector of words objects to clear
433    TYPE(word), POINTER :: cur,next
434    INTEGER             :: i
435    DO i=1,SIZE(objs)
436      call ws_clear_sc(objs(i))
437    ENDDO
438  END SUBROUTINE ws_clear_ve
439
440  SUBROUTINE ws_extend_ws(this, other)
441    !! Extend a list of words with another one
442    OBJECT(words), INTENT(inout) :: this
443      !! A words object to extend
444    TYPE(words), INTENT(in)     :: other
445      !! A words object to extend with
446    TYPE(word), POINTER :: cw
447    IF (other%nw == 0) RETURN
448    cw => other%head
449    DO WHILE(ASSOCIATED(cw))
450      CALL words_append(this,cw%value) ; cw => cw%next
451    ENDDO
452    RETURN
453  END SUBROUTINE ws_extend_ws
454
455  SUBROUTINE ws_extend_str(this,str,delimiter,merge,protect)
456    !> Extend a list of word with a given string
457    !! @details The method adds a new list of words to the current list by
458    !! splitting a string using a set of delimiters.
459    !!
460    !!   - If __delimiter__ is not given, THEN blank space is used.
461    !!   - __delimiter__ can be a string of any length, but each character of
462    !!     the sequence is seen as a single delimiter. Each time one of these
463    !!     special character is seen on the string, it is splitted.
464    !!   - If __protect__ is set to .true. THEN delimiter enclosed by
465    !!     either single or double quotes are protected.
466    !!   - The optional argument __merge__ instructs the method wether to merge
467    !!     or not successive delimiters in the string.
468    !!
469    !! For example, considering the following string:
470    !! <center>@verbatim "I like coffee and bananas." @endverbatim</center>
471    !!   - Used with only __delimiter__ = " e", the method returns the list:
472    !!     <center>"I","lik","","coff","","","and","bananas"</center>
473    !!   - Used with both __delimiter__ = " e" and __merge__ = .true. :
474    !!     <center>"I","lik","coff","and","bananas"</center>
475    !! @warning
476    !! The method does not trim or adjust the input string. Consequently, it can
477    !! add several empty words at the end of the list if the string is not well
478    !! defined.
479    !! @warning To avoid such problems, consider using TRIM() and ADJUSTL()
480    !! function on __str__ actual argument when calling this subroutine.
481    OBJECT(words), INTENT(inout), TARGET   :: this
482      !! A words object to extend
483    CHARACTER(len=*), INTENT(in)           :: str
484      !! A string to split in words
485    CHARACTER(len=*), INTENT(in), OPTIONAL :: delimiter
486      !! An optional string with the words delimiters (default to blank space).
487    LOGICAL, INTENT(in), OPTIONAL          :: merge
488      !! An optional boolean control flag that instructs the method
489      !! wether to merge or not successive delimiters (default to .false.)
490    LOGICAL, INTENT(in), OPTIONAL          :: protect
491      !! An optional boolean flag with .true. to indicate that
492      !! delimiter characters between quotes are protected
493    ! - LOCAL
494    INTEGER                       :: sl,p,i,j,stat
495    LOGICAL                       :: zmerge,zprotect,indq,insq,outer
496    CHARACTER(len=:), ALLOCATABLE :: seps
497    CHARACTER(len=:), ALLOCATABLE :: curw
498    CHARACTER(len=1), PARAMETER   :: sq = CHAR(39) ! single quote ascii code
499    CHARACTER(len=1), PARAMETER   :: dq = CHAR(34) ! double quotes ascii code
500    stat=0 ; p=1 ; indq = .false. ; insq = .false.
501    seps = ' '
502    zmerge = .false. ; IF (PRESENT(merge)) zmerge = merge
503    zprotect = .true. ; IF (PRESENT(protect)) zprotect = protect
504    IF (PRESENT(delimiter)) THEN
505      IF (LEN(delimiter) > 0) seps = delimiter
506    ENDIF
507    sl = LEN(str) ; IF (sl == 0) RETURN
508    outer =     (INDEX(str,sq) == 1 .AND. INDEX(str,sq,.true.) == LEN(str)) &
509            .OR.(INDEX(str,dq) == 1 .AND. INDEX(str,dq,.true.) == LEN(str))
510    ! no delimiter found or (have outer quotes and should protect)
511    IF (SCAN(str,seps) == 0.OR.(outer.AND.zprotect)) THEN
512      CALL words_append(this,remove_quotes(str))
513      RETURN
514    ENDIF
515    ! We have to loop...
516    i = 1 ; curw=''
517    DO
518      IF (i > sl) EXIT
519      p = SCAN(str(i:),seps) ! position du delimiteur
520      IF (p == 0) THEN
521        ! a gerer
522        curw = curw//TRIM(str(i:))
523        CALL words_append(this,TRIM(str(i:))) ; EXIT
524        curw=''
525      ELSE
526        IF (zprotect) THEN
527          j=i
528          ! starting state
529          DO WHILE(j<i+p)
530            IF (str(j:j) == sq.AND. .NOT.indq) insq = .NOT.insq
531            IF (str(j:j) == dq.AND. .NOT.insq) indq = .NOT.indq
532            j = j+1
533          ENDDO
534          IF ((insq.AND.INDEX(str(j:),"'")/=0) .OR. &
535              (indq.AND.INDEX(str(j:),'"')/=0)) THEN
536            curw=curw//str(i:i+p-1)
537            i=i+p ; CYCLE
538          ENDIF
539        ENDIF
540        IF (p == 1) THEN
541          IF (.NOT.zmerge) THEN
542            curw=''
543            CALL words_append(this,curw)
544          ENDIF
545          i = i + 1 ; CYCLE
546        ELSE
547          curw=curw//str(i:i+p-2)
548          CALL words_append(this,curw)
549          curw = ''
550          i = i + p
551        ENDIF
552      ENDIF
553    ENDDO
554    DEALLOCATE(curw) ; DEALLOCATE(seps)
555    !IF (zprotect) THEN
556    !  ! catching unbalanced quotes
557    !  IF (insq .OR. indq) &
558    !  WRITE(*,'(a)') "extends:warning: unbalanced quotes"
559    !ENDIF
560    RETURN
561  END SUBROUTINE ws_extend_str
562
563  FUNCTION ws_get_ptr(this,idx) RESULT(pted)
564    !! Get the pointer of the word object at given index
565    !!
566    !! The method returns the pointer of the word object at the given index.
567    !! If index is out of range a null poitner is returned.
568    OBJECT(words), INTENT(in) :: this
569      !! A words object
570    INTEGER, INTENT(in)       :: idx
571      !! An integer with the index of the desired object in __this__
572    TYPE(word), POINTER :: pted
573      !! A pointer to the selected word object.
574    INTEGER :: i
575    pted => null()
576    IF (idx < 1 .OR. idx > words_length(this)) THEN
577      RETURN
578    ENDIF
579    IF (idx > (this%nw+1)/2) THEN
580      pted => this%tail
581      DO i=1,this%nw - idx ; pted => pted%prev ; ENDDO
582    ELSE
583      pted => this%head
584      DO i=1,idx-1 ; pted => pted%next ; ENDDO
585    ENDIF
586    RETURN
587  END FUNCTION ws_get_ptr
588
589  FUNCTION words_length(this) RESULT(res)
590    !! Get the size of the words object.
591    !!
592    !! The method returns the number of words stored in the given list of words.
593    OBJECT(words), INTENT(in) :: this !! A words object.
594    INTEGER :: res                    !! The number of words in the object.
595    res = this%nw
596    RETURN
597  END FUNCTION words_length
598
599  SUBROUTINE words_insert(this, idx, value)
600    !! Insert a word before given index in a list of words.
601    !!
602    !! The method inserts a new word before the given index in the list of words. If the given index is out
603    !! of range, the method prepends/appends the object based on the index value.
604    OBJECT(words), INTENT(inout)  :: this
605      !! A words object.
606    INTEGER, INTENT(in)          :: idx
607      !! An integer with the index of an object in the list. The new object will be inserted before that index.
608    CHARACTER(len=*), INTENT(in) :: value
609      !! A string with the word to insert in the list.
610    TYPE(word), POINTER :: welt,nx,pv
611    INTEGER             :: i
612    welt => null() ; nx => null() ; pv => null()
613    IF (this%nw == 0) THEN
614      CALL ini_word(this,value)
615    ELSE IF (idx > this%nw) THEN
616      this%nw = this%nw + 1
617      welt => this%tail
618      allocate(this%tail)
619      ASSIGN_DTSTR(value,this%tail%value)
620      this%tail%prev => welt
621      this%tail%prev%next => this%tail
622    ELSE IF (idx <= 1) THEN
623      this%nw = this%nw + 1
624      welt => this%head
625      allocate(this%head)
626      ASSIGN_DTSTR(value,this%head%value)
627      this%head%next => welt
628      this%head%next%prev => this%head
629    ELSE
630      IF (idx > (this%nw+1)/2) THEN
631        nx => this%tail
632        DO i=1, this%nw - idx ; nx => nx%prev ; ENDDO
633      ELSE
634        nx => this%head
635        DO i=1, idx-1 ; nx => nx%next ; ENDDO
636      ENDIF
637      pv => nx%prev
638      allocate(welt)
639      ASSIGN_DTSTR(value,welt%value)
640      welt%prev => pv ; welt%next => nx
641      pv%next => welt ; nx%prev => welt
642      this%nw = this%nw + 1
643    ENDIF
644    RETURN
645  END SUBROUTINE words_insert
646
647  SUBROUTINE words_append(this,value)
648    !! Append a word to the list of word
649    !!
650    !! The method appends a word to the list of word. This is a convinient wrapper to
651    !! [[string_op(module)::words_insert(subroutine)]] to add a new word at the beginning of the list.
652    OBJECT(words), INTENT(inout) :: this  !! A words object
653    CHARACTER(len=*), INTENT(in) :: value !! A string to append
654    !CALL words_insert(this,this%nw+1,value)
655    type(word), pointer :: np
656
657    call words_insert(this,this%nw+1,value)
658
659    !! If the list is empty
660    !if (this%nw == 0) then
661    !  call ini_word(this, value)
662    !  return
663    !end if
664    !! Add new element to the end
665    !this%nw = this%nw + 1
666    !np => this%tail
667    !allocate(this%tail)
668    !this%tail%value = TRIM(value)
669    !this%tail%prev => np
670    !this%tail%prev%next => this%tail
671    RETURN
672  END SUBROUTINE words_append
673
674  SUBROUTINE words_prepend(this,value)
675    !! Prepend a word to the list of word
676    !!
677    !! The method prepends a word to the list of word. This is a convinient wrapper to
678    !! [[string_op(module)::words_insert(subroutine)]] to add a new word at the end of the list.
679    OBJECT(words), INTENT(inout) :: this  !! A words object
680    CHARACTER(len=*), INTENT(in) :: value !! A string to prepend
681    CALL words_insert(this,0,value)
682    RETURN
683  END SUBROUTINE words_prepend
684
685  FUNCTION words_get(this,idx,case) RESULT (res)
686    !! Get the word's value at given index
687    !!
688    !! The method attempts to get the word's value at the given index. If index is out of range
689    !! an empty string is returned.
690    !! @note
691    !! The returned string is always trimmed.
692    OBJECT(words), INTENT(in)              :: this
693      !! A words object reference
694    INTEGER, INTENT(in)                    :: idx
695      !! An integer with the index of a word in the list
696    CHARACTER(len=5), INTENT(in), OPTIONAL :: case
697      !! An optional string with either 'upper' or 'lower' to get the value converted in the relevant case
698    CHARACTER(len=:), ALLOCATABLE :: res
699      !! The value of the word stored at given index in the list of words
700    TYPE(word), POINTER :: cur
701    cur => ws_get_ptr(this,idx)
702    IF (.not.associated(cur)) THEN
703      res = '' ; RETURN
704    ENDIF
705    IF (PRESENT(case)) THEN
706      IF (case == "upper") res = to_upper(cur%value)
707      IF (case == "lower") res = to_lower(cur%value)
708    ELSE
709      res = TRIM(cur%value)
710    ENDIF
711    RETURN
712  END FUNCTION words_get
713
714  SUBROUTINE words_set(this,idx,value)
715    !! Set a new value to a word object in the list of words at given index
716    !!
717    !! The method sets a new word at given index. If index is out of range, the method simply does nothing.
718    OBJECT(words), INTENT(inout) :: this  !! A words object
719    INTEGER, INTENT(in)          :: idx   !! An integer with the index of the word object to modify in the list
720    CHARACTER(len=*), INTENT(in) :: value !! A string with the new value to set
721    TYPE(word), POINTER :: cur
722    cur => ws_get_ptr(this,idx)
723    IF (.NOT.ASSOCIATED(cur)) RETURN
724    cur%value = value
725  END SUBROUTINE words_set
726
727  FUNCTION words_get_max_width(this) RESULT(res)
728    !! Get the longest word's width in the words object
729    !!
730    !! The method computes and returns the longest (trimmed) word's width in the words object.
731    OBJECT(words), INTENT(in) :: this !! A words object
732    INTEGER :: res                    !! An integer with the maximum width (0 if the list is empty)
733    TYPE(word), POINTER :: cur
734    res = 0
735    IF (this%nw == 0) RETURN
736    cur => this%head ; res = word_length(cur)
737    DO WHILE(ASSOCIATED(cur%next))
738      cur => cur%next
739      IF (word_length(cur) > res) res = word_length(cur)
740    ENDDO
741    RETURN
742  END FUNCTION words_get_max_width
743
744  FUNCTION words_get_total_width(this) RESULT(width)
745    !! Get the total width of all words stored in the list of words
746    !!
747    !! The method computes and returns the total width of all words stored in
748    !! the list of words.
749    !! @note
750    !! Total width is computed using strings::word_length so it only takes
751    !! into account trimmed words (without trailing blanks)
752    !! @note
753    !! If csi codes have been added to words elements they are counted in the width.
754    OBJECT(words), INTENT(in) :: this !! A words object
755    INTEGER :: width                  !! Total length of the list of words
756    TYPE(word), POINTER :: cur
757    width = 0
758    IF (this%nw == 0) RETURN
759    cur => this%head ; width = word_length(cur)
760    DO WHILE(ASSOCIATED(cur%next))
761      cur => cur%next
762      width = width + word_length(cur)
763    ENDDO
764    cur => null()
765    RETURN
766  END FUNCTION words_get_total_width
767
768  SUBROUTINE words_reverse(this)
769    !! Reverse the list of words in-place
770    OBJECT(words), INTENT(inout) :: this
771      !! A words object to reverse
772    TYPE(word), POINTER :: loop,iwc,iwp
773    IF (this%nw <= 1) RETURN
774    loop => this%head ; iwc=> this%head ; iwp=> null()
775    DO WHILE(ASSOCIATED(loop%next))
776      loop => loop%next
777      iwp => iwc%prev ; iwc%prev => iwc%next ; iwc%next => iwp
778      iwc => loop
779    ENDDO
780    iwp=>this%tail%prev ; this%tail%prev=>this%tail%next ; this%tail%next=>iwp
781    iwc => this%head ; this%head => this%tail ; this%tail => iwc
782    loop => null() ; iwc => null() ; iwp => null()
783    RETURN
784  END SUBROUTINE words_reverse
785
786  FUNCTION words_reversed(this) RESULT(res)
787    !! Get a reversed copy of the list of words
788    OBJECT(words), INTENT(in) :: this
789      !! A words object to reverse
790    TYPE(words) :: res
791      !! A reversed copy of the input list of words
792    TYPE(word),POINTER  :: cur
793    IF(this%nw == 0) RETURN
794    cur => this%tail
795    DO WHILE(ASSOCIATED(cur))
796      CALL words_append(res,cur%value)
797      IF (ASSOCIATED(cur,this%iter)) res%iter => res%tail
798      cur => cur%prev
799    ENDDO
800    cur => null()
801    RETURN
802  END FUNCTION words_reversed
803
804  SUBROUTINE words_dump(this,lun)
805    !! Dump the list of words
806    !!
807    !! The method dumps on the given logical unit the elements of the list one by line.
808    OBJECT(words), INTENT(in)     :: this
809      !! A words object to dump
810    INTEGER, INTENT(in), OPTIONAL :: lun
811      !! An optional integer with the printing logical unit. If not given, the list is dumped on
812      !! standard output stream.
813    TYPE(word), POINTER :: cur
814    INTEGER             :: lu
815    IF (this%nw == 0) RETURN
816    lu=6 ; IF (PRESENT(lun)) lu = lun
817    cur => this%head
818    DO WHILE(ASSOCIATED(cur))
819      WRITE(lu,'(a)') TRIM(cur%value)
820      cur => cur%next
821    ENDDO
822    cur => null()
823    RETURN
824  END SUBROUTINE words_dump
825
826  FUNCTION words_to_string(this, delimiter,protect) RESULT(str)
827    !! Convert the list of words into a string
828    !!
829    !! The method converts the list of words into a string. In output, string is always
830    !! allocated even if the list is empty.
831    !!
832    !! If `protect` is set to .true. (default to .false.), each word is enclose between
833    !! double quotes. This option can be used to perform operation on the string before
834    !! setting it back as values of the list of words.
835    OBJECT(words), INTENT(in)              :: this
836      !! A words object
837    CHARACTER(len=*), INTENT(in), OPTIONAL :: delimiter
838      !! An optional string used as delimiter between each words
839    LOGICAL, INTENT(in), OPTIONAL :: protect
840      !! Optional control flag with .true. to protect each word during concatentation.
841    CHARACTER(len=:), ALLOCATABLE :: str
842      !! An allocatable string with the list of words joined by the given delimiter (if any)
843    TYPE(word), POINTER :: cur
844    LOGICAL :: zprotect
845    zprotect = .false. ; IF (PRESENT(protect)) zprotect = protect
846    str = ''
847    IF (this%nw == 0) RETURN
848    cur => this%head
849    DO WHILE(ASSOCIATED(cur))
850      IF (zprotect) THEN
851        str=str//'"'//TRIM(cur%value)//'"'
852      ELSE
853        str=str//TRIM(cur%value)
854      ENDIF
855      IF (PRESENT(delimiter).AND..NOT.ASSOCIATED(cur,this%tail)) &
856        str=str//delimiter
857      cur => cur%next
858    ENDDO
859    RETURN
860  END FUNCTION words_to_string
861
862  FUNCTION words_to_vector(this,ret) RESULT(ok)
863    !! Convert the list of words into a vector of strings
864    !!
865    !! The method attempts to convert the list of words in a vector of strings.
866    !! If _this_ list of words is empty, the output vector is allocated with 0 elements and the method returns
867    !! .false., otherwise it returns .true.
868    !! @note
869    !! If elements in __this__ words object are wider than [[string_op(module):st_slen(variable)]], output
870    !! values will be truncated.
871    OBJECT(words), INTENT(in)                                      :: this
872      !! A words object reference
873    CHARACTER(len=st_slen), INTENT(out), ALLOCATABLE, DIMENSION(:) :: ret
874      !! An allocatable vector of assumed length string with the words of __this__
875    LOGICAL             :: ok
876      !! Return status.
877    INTEGER             :: l,mw
878    TYPE(word), POINTER :: iw
879    ok = .true.
880    l = words_length(this)
881    IF (l == 0) THEN
882      ALLOCATE(ret(0))
883      ok = .false.
884      RETURN
885    ENDIF
886    ALLOCATE(ret(l)) ; mw = LEN(ret(l))
887    ret(1:l) = ' ' ! really needed ?
888    iw => this%head ; l=1
889    DO WHILE(ASSOCIATED(iw))
890       ret(l) = TRIM(iw%value) ; l=l+1 ; iw => iw%next
891    ENDDO
892  END FUNCTION words_to_vector
893
894  FUNCTION words_pop(this,idx,move_forward) RESULT(value)
895    !! Pop a word in the list of words
896    !!
897    !! The method removes the word of the list at given index and returns it. If no index is given,
898    !! last word of the list is removed.
899    !!
900    !! If the index is out of range, the method does nothing and returns an empty string.
901    !!
902    !! By default, if the iterator is located on the item to be removed, it is moved backward before
903    !! deletion occurs. If __move\_forward__ is set to .true., the iterator is moved forward.
904    OBJECT(words), INTENT(inout)  :: this
905     !! A words object
906    INTEGER, INTENT(in), OPTIONAL :: idx
907      !! Optional index of the word to delete
908    LOGICAL, INTENT(in), OPTIONAL :: move_forward
909      !! Move the iterator forward if needed. By default the iterator is moved backward.
910    CHARACTER(len=:), ALLOCATABLE :: value
911      !! The word's value at given index
912    LOGICAL             :: zforward
913    INTEGER             :: zidx
914    TYPE(word), POINTER :: cur
915    zidx=words_length(this) ; IF (PRESENT(idx)) zidx = idx
916    zforward = .false. ; IF (PRESENT(move_forward)) zforward = move_forward
917    cur => ws_get_ptr(this,zidx)
918    IF (.NOT.ASSOCIATED(cur)) THEN
919      value = '' ; RETURN
920    ELSE IF (ASSOCIATED(cur,this%iter)) THEN
921      IF (zforward) THEN
922        CALL words_next(this)
923      ELSE
924        CALL words_previous(this)
925      ENDIF
926    ENDIF
927    value = TRIM(cur%value)
928    CALL disconnect_word(cur)
929    DEALLOCATE(cur)
930    this%nw = this%nw - 1
931    RETURN
932  END FUNCTION words_pop
933
934  SUBROUTINE words_remove(this,idx,move_forward)
935    !! Remove the word of the list at given index
936    !!
937    !! The method removes the word of the list at given index. If no index is given, last word
938    !! of the list is removed.
939    !!
940    !! If the index is out of range, the method does nothing.
941    !!
942    !! By default, if the iterator is located on the item to be removed, it is moved backward before
943    !! deletion occurs. If __move\_forward__ is set to .true., the iterator is moved forward.
944    OBJECT(words), INTENT(inout)  :: this
945      !! A words object
946    INTEGER, INTENT(in), OPTIONAL :: idx
947      !! Index of the word to delete
948    LOGICAL, INTENT(in), OPTIONAL :: move_forward
949      !! Move the iterator forward if needed. By default the iterator is moved backward.
950    LOGICAL             :: zforward
951    INTEGER             :: zidx
952    TYPE(word), POINTER :: cur
953    zidx=words_length(this) ; IF(PRESENT(idx)) zidx = idx
954    zforward = .false. ; IF (PRESENT(move_forward)) zforward = move_forward
955    cur => ws_get_ptr(this,idx)
956    IF (.NOT.ASSOCIATED(cur)) THEN
957      RETURN
958    ELSE IF (ASSOCIATED(cur,this%iter)) THEN
959      IF (zforward) THEN
960        CALL words_next(this)
961      ELSE
962        CALL words_previous(this)
963      ENDIF
964    ENDIF
965    CALL disconnect_word(cur)
966    DEALLOCATE(cur)
967    this%nw = this%nw - 1
968    RETURN
969  END SUBROUTINE words_remove
970
971  SUBROUTINE words_next(this)
972    !! Go to the next word in the list
973    OBJECT(words), INTENT(inout) :: this !! A words object
974    IF (ASSOCIATED(this%iter)) this%iter => this%iter%next
975  END SUBROUTINE words_next
976
977  SUBROUTINE words_previous(this)
978    !! Go to the previous word in the list
979    OBJECT(words), INTENT(inout) :: this !! A words object
980    IF (ASSOCIATED(this%iter)) this%iter => this%iter%prev
981  END SUBROUTINE words_previous
982
983  FUNCTION words_valid(this) RESULT(ret)
984    !! Check if the current iterated word is valid
985    OBJECT(words), INTENT(in) :: this !! A words object
986    LOGICAL :: ret                    !! A logical flag with .true. if the current iterated word is valid
987    ret = associated(this%iter)
988  END FUNCTION words_valid
989
990  FUNCTION words_current(this) RESULT(wrd)
991    !! Get current word value
992    OBJECT(words), INTENT(in) :: this
993      !! A words object
994    CHARACTER(len=:), ALLOCATABLE :: wrd
995      !! A string with the value of the current word or __an unallocated string__ if current word
996      !! is not valid (see [[string_op(module):words_valid(function)]]).
997    IF (ASSOCIATED(this%iter)) THEN
998      wrd = this%iter%value
999    ENDIF
1000  END FUNCTION words_current
1001
1002  SUBROUTINE words_reset(this,to_end)
1003    !! Reset the iterator
1004    !!
1005    !! The method resets the iterator either at the beginning or at the end of the list of words
1006    !! (if __to_end__ is set to .true.).
1007    OBJECT(words), INTENT(inout)  :: this   !! A words object
1008    LOGICAL, INTENT(in), OPTIONAL :: to_end !! An optional logical flag with .true. to reset the iterator at the end of the list
1009    this%iter => this%head
1010    IF (PRESENT(to_end)) THEN
1011      IF (to_end) this%iter => this%tail
1012    ENDIF
1013  END SUBROUTINE words_reset
1014
1015  ! Fancy string methods
1016  ! --------------------
1017
1018  FUNCTION tokenize(str,vector,delimiter,merge,protect) RESULT(ok)
1019    !! Tokenize a string.
1020    CHARACTER(len=*), INTENT(in)                             :: str
1021      !! A string to tokenize
1022    CHARACTER(len=*), INTENT(out), DIMENSION(:), ALLOCATABLE :: vector
1023      !! An allocatable vector of strings with the tokens found. If string cannot be tokenized,
1024      !! the vector is __allocated to 0 elements__ and the method returns .false..
1025    CHARACTER(len=*), INTENT(in), OPTIONAL                   :: delimiter
1026      !! An optional string with the words delimiters. It is set to blank space by default.
1027      !! Note that each character is seen as a single delimiter.
1028    LOGICAL, INTENT(in), OPTIONAL                            :: merge
1029      !! An optional boolean control flag with .true. that instructs the method whether to
1030      !! merge or not successive delimiters. Default to .false.
1031    LOGICAL, INTENT(in), OPTIONAL                            :: protect
1032      !! An optional boolean flag with .true. to indicate that delimiter characters between
1033      !! quotes are protected. Default to .true.
1034    LOGICAL :: ok
1035      !! Return status (.true. on success).
1036    CHARACTER(len=:), ALLOCATABLE :: seps
1037    TYPE(words)                   :: tmp
1038    LOGICAL                       :: zmerge,zprotect
1039    TYPE(word), POINTER           :: wrd
1040    integer                       :: i,nw
1041    ok = .true.
1042    zmerge = .false. ; zprotect = .true. ; seps = ' '
1043    IF (PRESENT(merge)) zmerge = merge
1044    IF (PRESENT(protect)) zprotect = protect
1045    IF (PRESENT(delimiter)) THEN
1046      IF (LEN(delimiter) > 0 ) seps = delimiter
1047    ENDIF
1048    call words_extend(tmp,str,seps,zmerge,zprotect)
1049    nw = tmp%nw
1050    i =  1
1051    ALLOCATE(vector(tmp%nw))
1052    IF (nw > 0) THEN
1053      wrd => tmp%head
1054      DO WHILE(ASSOCIATED(wrd))
1055        vector(i) = TRIM(wrd%value)
1056        wrd => wrd%next
1057        i = i+1
1058      ENDDO
1059    ELSE
1060      ok = .false.
1061    ENDIF
1062    call words_clear(tmp)
1063    RETURN
1064  END FUNCTION tokenize
1065
1066  FUNCTION remove_quotes(str) RESULT(ostr)
1067    !! Strips outer quotes from string
1068    !!
1069    !! The function removes only external quotes from the input string
1070    !! and returns the result in an allocatable string.
1071    !! Quotes are removed only if they are the first and last non blank
1072    !! characters. Either double and single quotes are stripped without distinction.
1073    !! The output string is trimmed from leading and trailing blank spaces (after quotes removal !)
1074    CHARACTER(len=*), INTENT(in)  :: str  !! A string to check
1075    CHARACTER(len=:), ALLOCATABLE :: ostr !! A string without external quotes (if any). 
1076    CHARACTER(len=1), PARAMETER   :: sq=CHAR(39), dq=CHAR(34)
1077    CHARACTER(len=2), PARAMETER   :: dsq=CHAR(39)//CHAR(34)
1078    INTEGER                       :: i, j
1079    IF (LEN_TRIM(str) == 0) RETURN
1080    ostr = TRIM(ADJUSTL(str))
1081    i = SCAN(ostr,sq//dq) ; j = SCAN(ostr,sq//dq,.true.)
1082    IF (i == j) RETURN
1083    IF (i /= 1) i = 0
1084    IF (j /= LEN(ostr)) j = LEN(ostr)+1
1085    ostr = TRIM(ostr(i+1:j-1))
1086    RETURN
1087  END FUNCTION remove_quotes
1088
1089  FUNCTION string_is(str) RESULT(ret)
1090    !! Check if string represents an intrinsic type
1091    !!
1092    !! The method checks if the given string represents an intrinsic type. Both logical and complex type
1093    !! are checked in a strict way :
1094    !!
1095    !! - A string is a logical if it is one of the following value: __.false.__, __.true.__, __F__, __T__.
1096    !! - A string is potentially a complex if it has the following format: __(\*\*\*,\*\*\*)__ where
1097    !!   __\*\*\*__ is checked to see wether it is numerical or not.
1098    !!
1099    !! Valid numerical values can take the following forms:
1100    !! ```
1101    !!   [0-9]
1102    !!   [0-9]*.?[0-9]*?([ed][+-]?[0-9]+)?
1103    !! ```
1104    !! Obviously if returned value is greater than 3, the string can be converted in
1105    !! floating point value.
1106    !!
1107    !! Empty input string is simply considered to be of string type ! 
1108    CHARACTER(len=*), INTENT(in) :: str
1109      !! A string to check
1110    INTEGER :: ret
1111      !! An integer with the intrinsic type related to the string.
1112      !!
1113      !! Types are one of the following parameters
1114      !!
1115      !! - [[string_op(module):st_string(variable)]] (1) for string
1116      !! - [[string_op(module):st_logical(variable)]] (2) for logical
1117      !! - [[string_op(module):st_complex(variable)]] (3) for complex
1118      !! - [[string_op(module):st_integer(variable)]] (4) for integer
1119      !! - [[string_op(module):st_real(variable)]] (5) for floating point value
1120    CHARACTER(len=:), ALLOCATABLE :: zs,zzs
1121    INTEGER :: j,l
1122    ret = 1 ; IF (LEN_TRIM(str) == 0) RETURN
1123    zs = to_lower(TRIM(ADJUSTL(str))) ; j = INDEX(zs,',') ; l = len(zs)
1124    IF (zs(1:1)=='('.AND.zs(l:l) == ')'.AND.j==INDEX(zs,',')) THEN
1125      IF (j == 2 .OR. j == l-1) RETURN
1126      zzs = TRIM(ADJUSTL(zs(2:j-1))) ; IF (what_(zzs) < 3) RETURN
1127      zzs = TRIM(ADJUSTL(zs(j+1:l-1))) ; ret = what_(zzs)
1128      IF (ret > 3) THEN ; ret = 3 ; ELSE ; ret = 1 ; ENDIF
1129    ELSE
1130      ret = what_(zs)
1131    ENDIF
1132    CONTAINS
1133      FUNCTION what_(s) RESULT(is)
1134        !! Check if the given string is numerical, logical or a simple string
1135        !! @note
1136        !! Input string should be in lower case, otherwise, the method will give a a wrong result.
1137        !! @warning
1138        !! The test performed for logical checking is quite strict : A string is considered as logical
1139        !! if and only if it is one of the following values : __.false.__, __.true.__, __F__, __T__.
1140        CHARACTER(len=*), INTENT(in) :: s
1141          !! A string to check
1142        INTEGER :: is
1143          !! An integer with : __1__ for string, __2__ for logical, __4__ for integer and __5__ for real
1144        LOGICAL                      :: dec,fdot,fexp
1145        INTEGER                      :: i
1146        CHARACTER(len=24), PARAMETER :: aset='abcfghijklmnopqrstuvwxyz'
1147        CHARACTER(len=10), PARAMETER :: iset='1234567890'
1148        CHARACTER(len=2),  PARAMETER :: dset='ed'
1149        CHARACTER(len=2),  PARAMETER :: sset='+-'
1150        CHARACTER(len=7),  PARAMETER :: slog(4) = (/'.true. ','.false.',&
1151                                                    't      ','f      '/)
1152        is = -1 ; dec = .false. ; fdot = dec ; fexp = fdot
1153        DO i = 1,LEN(s)
1154          IF (i == 1) THEN
1155            ! string does not start by [+-\.\d]
1156            IF (VERIFY(s(i:i),'.'//iset//sset) /= 0) THEN
1157              is = 1 ; EXIT
1158            ENDIF
1159            ! update control flag for decimal part
1160            dec = s(i:i) == '.' ; fdot = dec
1161          ELSE
1162            ! check if char is in [a-z]
1163            IF(VERIFY(s(i:i),aset) == 0) THEN
1164              dec=.false. ; is = 1 ; EXIT
1165            ELSE IF (s(i:i) == '.') THEN
1166              ! check for dot in decimal/exponent part (==> not a number
1167              IF (fdot.OR.fexp) THEN
1168                dec = .false. ; is = 1 ; EXIT
1169              ENDIF
1170            ELSE IF (VERIFY(s(i:i),dset)==0) THEN
1171              IF (fexp) THEN
1172                dec = .false. ; is = 1 ; EXIT
1173              ENDIF
1174            ELSE IF (VERIFY(s(i:i),sset) == 0) THEN
1175              IF (VERIFY(s(i-1:i-1),dset) /= 0) THEN
1176                dec = .false. ; is = 1 ; EXIT
1177              ENDIF
1178            ENDIF
1179            fdot = (fdot .OR. s(i:i) == '.')
1180            fexp = (fexp .OR. VERIFY(s(i:i), dset) == 0)
1181          ENDIF
1182        ENDDO
1183        ! it is a string
1184        IF (is == 1) THEN
1185          ! but have the format of a logical
1186          IF (any(slog == s)) is = 2
1187        ELSE
1188          IF ((fexp.AND.SCAN(s(LEN(s):LEN(s)),dset) /= 0)) THEN
1189            is = 1
1190          ELSE
1191            is = 4
1192            IF (fdot.OR.fexp) is = 5
1193          ENDIF
1194        ENDIF
1195      END FUNCTION what_
1196  END FUNCTION string_is
1197
1198  FUNCTION format_string(str,idt1,idto) RESULT(output)
1199    !! Format the given string
1200    !!
1201    !! This function only replaces all '\\n' escape sequence in the given string by NEW_LINE() character.
1202    !! The output string is eventually indented if optional arguments are set.
1203    !! @warning
1204    !! __idto__ is relative to __idt1__ !
1205    CHARACTER(len=*), INTENT(in)  :: str     !! The string to format
1206    INTEGER, INTENT(in), OPTIONAL :: idt1, & !! An optional integer with the indentation level of the first output line (default to 0)
1207                                     idto    !! An optional integer with the indentation level of all other output lines (default to 0)
1208    CHARACTER(len=:), ALLOCATABLE :: output  !! An allocatable string with the output formatted string.
1209    ! - LOCAL
1210    INTEGER :: i,c,ti,mx
1211    CHARACTER(len=:), ALLOCATABLE :: idts
1212    IF (LEN_TRIM(str) == 0) THEN
1213      ALLOCATE(output,source='') ; RETURN
1214    ENDIF
1215    i=0 ; IF (PRESENT(idt1)) i = MAX(i,idt1)
1216    ALLOCATE(CHARACTER(len=i) :: output)
1217    IF (i > 0) output(1:i) = CHAR(32)
1218    ! i0 is relative to i1 and must be >= 0
1219    IF (PRESENT(idto)) i = MAX(i+idto,0)
1220    ALLOCATE(CHARACTER(len=i+1) :: idts)
1221    idts(1:1) = NEW_LINE('A') ; IF (i>1) idts(2:) = CHAR(32)
1222    ! Builds output string
1223    c=1 ; mx = LEN_TRIM(str)
1224    i = INDEX(str(c:),'\n') ; ti = c+i-1
1225    IF (i == 0) THEN
1226      output=output//TRIM(str(ti+1:mx))
1227    ELSE
1228      output=output//TRIM(str(c:ti-1)) ; c=ti+2
1229      DO
1230        i = INDEX(str(c:),"\n") ; ti = c+i-1
1231        IF (i == 0) THEN
1232          output=output//TRIM(str(ti+1:mx)) ; c = mx+1
1233        ELSE
1234          output=output//idts//str(c:ti-1) ; c = ti+2
1235        ENDIF
1236        IF (c > mx) EXIT
1237      ENDDO
1238    ENDIF
1239    ! print a newline if we have \n at the end of the string
1240    IF (INDEX(TRIM(str),'\n',.true.) == mx-1.AND.TRIM(str) /= '\n') &
1241    output=output//idts(1:1)
1242  END FUNCTION format_string
1243
1244  FUNCTION format_paragraph(str,width,idt1,idto) RESULT(output)
1245    !! Split and format a string over several lines
1246    !!
1247    !! The function splits an input string in words so output lines fit (almost) in __width__ characters.
1248    !! The method handles indentation level (defined as leading blank spaces). It also accounts for known
1249    !! csi (see [[string_op(module):attributes(variable)]]).
1250    !! @note
1251    !! Words are considered indivisible and thus output lines can sometimes exceed the maximum width if
1252    !! there is not enough space to put a word (with the associated indentation if given). The default
1253    !! behavior in that case is to print the word in a new line (with the correct leading blank spaces).
1254    !! @warning
1255    !! If __width__, __idt1__ and/or __idto__ have inconsistent values (e.g. __width__ <= __idt1__), the
1256    !! method still computes the paragraph, but each words will be set on a new line with the appropriate
1257    !! indentation.
1258    CHARACTER(len=*), INTENT(in)  :: str    !! string with the content to split
1259    INTEGER, INTENT(in)           :: width  !! An positive integer with the maximum width of a line
1260    INTEGER, INTENT(in), OPTIONAL :: idt1   !! An optional integer with the indentation level of the first output line
1261    INTEGER, INTENT(in), OPTIONAL :: idto   !! An optional integer with the indentation level of the other output lines
1262    CHARACTER(len=:), ALLOCATABLE :: output !! An allocatable string with the output content
1263    CHARACTER(len=:), ALLOCATABLE :: idts,zs
1264    INTEGER                       :: l1,lo,zmx,zw,cc,j,jj,l
1265    zw = abs(width) ; zs = strip_newline(str)
1266    zmx = LEN_TRIM(zs)
1267    IF (zmx == 0) THEN
1268      ALLOCATE(output,source='') ; RETURN
1269    ENDIF
1270    l1=0 ; IF (PRESENT(idt1)) l1 = MAX(l1,idt1)
1271    ALLOCATE(CHARACTER(len=l1) :: output)
1272    IF (l1 > 0) output(1:l1) = CHAR(32)
1273    lo=l1 ; IF (PRESENT(idto)) lo = MAX(l1+idto,0)
1274    ALLOCATE(CHARACTER(len=lo+1) :: idts)
1275    idts(1:1) = NEW_LINE('A') ; IF (lo>=1) idts(2:len(idts)) = CHAR(32)
1276    ! Prints a message if user is just stupid...
1277    IF (lo+1 > zw .OR. l1+1 > zw) THEN
1278      output = str ; RETURN
1279    ENDIF
1280    ! check if can just return the string as is
1281    IF (zmx + l1 <= zw) THEN
1282      output=output//TRIM(zs) ; RETURN
1283    ENDIF
1284    j=1 ; jj=1+l1
1285    DO
1286      ! Gets next blank in input string
1287      cc = INDEX(TRIM(zs(j:)),CHAR(32))
1288      ! no more blank
1289      ! Gets total length of csi between zs(j:j+cc-1)
1290      ! this value will be substracted to each length test
1291      IF (cc == 0) THEN
1292        l = csis_length(zs(j:))
1293        IF (jj-1+LEN_TRIM(zs(j:))-l > zw) THEN
1294          output = output//idts
1295        ENDIF
1296        output=output//TRIM(zs(j:))
1297        EXIT ! we are at the last word : we must exit the infinite loop !
1298      ELSE
1299        l = csis_length(zs(j:j+cc-1))
1300        IF (cc+jj-1-l > zw) THEN
1301          output=output//idts//zs(j:j+cc-1) ; jj = lo+1+cc+1 - l
1302        ELSE
1303          output=output//zs(j:j+cc-1) ; jj = jj + cc - l
1304        ENDIF
1305      ENDIF
1306      j = j + cc
1307    ENDDO
1308    CONTAINS
1309    FUNCTION csis_length(str) RESULT(value)
1310      ! - DUMMY
1311      CHARACTER(len=*), INTENT(in) :: str
1312      ! - RESULT
1313      INTEGER :: value
1314      ! - LOCAL
1315      INTEGER :: jc,iesc,im
1316      LOGICAL :: tcsi
1317      value = 0
1318      jc=1
1319      DO
1320        IF (jc>LEN(str)) EXIT
1321        ! search for escape
1322        iesc = INDEX(str(jc:),CHAR(27))
1323        IF (iesc == 0) EXIT
1324        ! search for m
1325        im = INDEX(str(jc+iesc:),"m")
1326        ! no m in the string after ESC --> this could not be a csi
1327        IF (im == 0) EXIT
1328        ! check if this is really a csi and updates length
1329        tcsi = is_csi(str(jc+iesc-1:jc+iesc+im-1))
1330        jc = jc + iesc
1331        IF (tcsi) THEN
1332          value=value+im+1
1333          jc=jc+im
1334        ENDIF
1335      ENDDO
1336    END FUNCTION csis_length
1337  END FUNCTION format_paragraph
1338
1339  FUNCTION strip_newline(str,rpl) RESULT(stripped)
1340    !! Replace newline escape sequences by spaces
1341    !!
1342    !! The function replaces newline (both '\\n' escape sequence and Fortran NEW_LINE() character) in the
1343    !! given string and returns the resulting string.
1344    CHARACTER(len=*), INTENT(in)           :: str !! A string to process
1345    CHARACTER(len=1), INTENT(in), OPTIONAL :: rpl !! A optional single character used as substitution of escape sequences (blank space by default)
1346    CHARACTER(len=:), ALLOCATABLE :: stripped     !! An allocatable string with all newline sequences replaced by blank space or __rpl__ if given
1347    CHARACTER(len=1) :: zrp
1348    INTEGER          :: i, j, ns
1349    zrp = CHAR(32) ; IF(PRESENT(rpl)) zrp = rpl
1350    IF (str == NEW_LINE('A')) THEN
1351      stripped = zrp ; RETURN
1352    ENDIF
1353    ns = LEN_TRIM(str)
1354    IF (ns == 0) THEN
1355      ALLOCATE(stripped,source='') ; RETURN
1356    ENDIF
1357    ALLOCATE(CHARACTER(len=ns) :: stripped) ; stripped(1:ns) = CHAR(32)
1358    i=1 ; j=1
1359    DO
1360      IF (str(i:i) == NEW_LINE('A')) THEN
1361        stripped(j:j) = zrp
1362      ELSE IF (i < ns) THEN
1363          IF (str(i:i+1) == "\n") THEN
1364            stripped(j:j) = zrp ; i=i+1
1365          ELSE
1366            stripped(j:j) = str(i:i)
1367          ENDIF
1368      ELSE
1369        stripped(j:j) = str(i:i)
1370      ENDIF
1371      j=j+1 ; i=i+1
1372      IF (i > ns .OR. j > ns) EXIT
1373    ENDDO
1374    IF (j < ns) stripped = stripped(1:j)
1375    RETURN
1376  END FUNCTION strip_newline
1377
1378  FUNCTION str_length(str) RESULT(res)
1379    !! Get the length of the string object
1380    !!
1381    !! The method computes the length of the string. It differs from LEN intrinsic function as
1382    !! it does not account for extra-characters of csi codes.
1383    CHARACTER(len=*), INTENT(in) :: str !! String to process
1384    INTEGER :: res                      !! The actual length of string (i.e. does not account for csi codes)
1385    CHARACTER(len=:), ALLOCATABLE :: tmp
1386    res = 0
1387    IF (LEN(str) /= 0) THEN
1388      tmp = reset_csi(str)
1389      res = LEN(tmp)
1390      DEALLOCATE(tmp)
1391    ENDIF
1392    RETURN
1393  END FUNCTION str_length
1394
1395  FUNCTION to_lower(str1) RESULT(str)
1396    !! Convert the string in lower case
1397    !!
1398    !! The method converts the input string in lower case and accounts for
1399    !! possible csi codes in the string.
1400    CHARACTER(len=*), INTENT(in) :: str1 !! Input string to convert
1401    CHARACTER(len=:), ALLOCATABLE :: str !! A copy of the string in lower case
1402    INTEGER :: i,ic
1403    IF (LEN(str1) /= 0) THEN
1404      str = str1
1405      DO i = 1, len(str1)
1406        ic = ichar(str1(i:i))
1407        IF (ic >= 65 .AND. ic < 90) str(i:i) = char(ic + 32)
1408      ENDDO
1409    ELSE
1410      str=''
1411    ENDIF
1412  END FUNCTION to_lower
1413
1414  FUNCTION to_upper(str1) RESULT(str)
1415    !! Convert the string in upper case
1416    !!
1417    !! The method converts the input string in upper case and accounts for
1418    !! possible csi codes in the string.
1419    CHARACTER(len=*), INTENT(in) :: str1 !! Input string to convert
1420    CHARACTER(len=:), ALLOCATABLE :: str !! A copy of the string in upper case
1421    INTEGER :: j,i,ic,icsi,lcsi
1422    IF (LEN(str1) > 0) THEN
1423      str = str1
1424      i = 1
1425      DO
1426        IF (i > LEN(str)) EXIT
1427        icsi = str_index_of_csi(str(i:),lcsi)
1428        IF (icsi == 0) THEN
1429          ! no more csi the end of string is upper case converted
1430          DO j=i,LEN(str)
1431            ic = ichar(str(j:j))
1432            IF (ic >= 97 .AND. ic < 122) str(j:j) = char(ic-32)
1433          ENDDO
1434          RETURN
1435        ELSE IF (icsi == 1) THEN
1436          i = i + lcsi
1437        ELSE IF (icsi > 1) THEN
1438          ! csi is not the first word: we convert in upper case until its
1439          ! position THEN copy the csi and get back in the loop
1440          DO j=i,i+icsi-2
1441            ic = ichar(str(j:j))
1442            IF (ic >= 97 .AND. ic < 122) str(j:j) = char(ic-32)
1443          ENDDO
1444          i = i + icsi + lcsi-1
1445        ENDIF
1446      ENDDO
1447    ELSE
1448      str=''
1449    ENDIF
1450  END FUNCTION to_upper
1451
1452 FUNCTION str_remove(string,substring,back,all) RESULT(str)
1453   !! Remove substring from current string
1454   !!
1455   !! The function removes the first occurence of __substring__ in __string__ or all
1456   !! its occurences if __all__ is explicitly set to .true..
1457    CHARACTER(len=*), INTENT(in)  :: string    !! A string to search in
1458    CHARACTER(len=*), INTENT(in)  :: substring !! A string to search and removes from __string__
1459    LOGICAL, INTENT(in), OPTIONAL :: back, &   !! An optional boolean flag with .true. to begin search at the end of the string
1460                                     all       !! An optional boolean flag with .true. to remove all occurences of __substring__
1461    CHARACTER(len=:), ALLOCATABLE :: str       !! An allocatable string with __substring__ occurence(s) removed
1462    LOGICAL :: zb,za
1463    INTEGER :: is,j,zboff
1464    str=''
1465    zb = .false. ; za = .false.
1466    IF (PRESENT(back)) zb = back
1467    IF (PRESENT(all)) za = all
1468    IF (za) zb=.false.
1469    zboff = 0 ; IF (zb) zboff = 1
1470    IF (LEN(string) == 0) RETURN
1471    j=1
1472    DO
1473      IF (j>LEN(string)) EXIT
1474      ! search for substring
1475      is = INDEX(string(j:),substring,back=zb)
1476      IF (is == 0) THEN
1477        ! substring is not found : we get the last part of the string and return
1478        str = str//string(j:) ; RETURN
1479      ELSE IF (is == 1) THEN
1480        j = j + LEN(substring)
1481      ELSE
1482        ! substring is not at the begin of the string : saves the string
1483        str = str//string(j:j+is-2)
1484        j = j + is+LEN(substring)-1
1485      ENDIF
1486      ! if we only want to str_remove ONE occurence we exit if substring
1487      ! has been found
1488      IF (.NOT.(is==0.OR.za)) EXIT
1489    ENDDO
1490    IF (j <= LEN(string).AND..NOT.zb) str=str//string(j:)
1491    RETURN
1492  END FUNCTION str_remove
1493
1494 FUNCTION str_replace(string,old,new,back,all) RESULT(str)
1495    !! Replace substring from current string
1496    !!
1497    !! The function replaces the first occurence of __old__ in __string__ by
1498    !! __new__ or all its occurence(s) if __all__ is explicitly set to .true..
1499    CHARACTER(len=*), INTENT(in)  :: string  !! A string to search in
1500    CHARACTER(len=*), INTENT(in)  :: old,  & !! A string to search and replace
1501                                     new     !! A string to substitute to __old__
1502    LOGICAL, INTENT(in), OPTIONAL :: back, & !! An optional boolean flag with .true. to begin search at the end of the string
1503                                     all     !! An optional boolean flag with .true. to replace all occurences of __old__
1504    CHARACTER(len=:), ALLOCATABLE :: str     !! An allocatable string with occurence(s) of __old__ replaced by __new__
1505    LOGICAL :: zb,za
1506    INTEGER :: is,j
1507    str=''
1508    zb = .false. ; za = .false.
1509    IF (PRESENT(back)) zb = back
1510    IF (PRESENT(all)) za = all
1511    IF (za) zb = .NOT.za
1512    IF (LEN(string) == 0) RETURN
1513    j=1
1514    DO
1515      IF (j>LEN(string)) EXIT
1516      ! search for "old"
1517      is = INDEX(string(j:),old,back=zb)
1518      IF (is == 0) THEN
1519        ! "old" is not found : we get the last part of the string and return
1520        str = str//string(j:) ; RETURN
1521      ELSE IF (is == 1) THEN
1522        str = str//new
1523        j = j + LEN(old)
1524      ELSE
1525        ! "old" is not at the begin of the string : saves the string
1526        str = str//string(j:j+is-2)//new
1527        j = j + is + LEN(old) - 1
1528      ENDIF
1529      IF (.NOT.(is==0.OR.za)) EXIT
1530    ENDDO
1531    IF (j <= LEN(str)) str=str//string(j:)
1532    RETURN
1533  END FUNCTION str_replace
1534
1535  FUNCTION endswith(string,substring,icase) RESULT(ret)
1536    !! Check if string ends by substring
1537    CHARACTER(len=*), INTENT(in)  :: string
1538      !! @param[in] string A string to check
1539    CHARACTER(len=*), INTENT(in)  :: substring
1540      !! A string to search in __string__
1541    LOGICAL, INTENT(in), OPTIONAL :: icase
1542      !! An optional boolean flag with .true. to perform insensitive case search
1543    LOGICAL :: ret
1544      !! .true. if __string__ ends by __substring__, .false. otherwise.
1545    CHARACTER(len=:), ALLOCATABLE :: zthis,zstr
1546    INTEGER                       :: idx
1547    LOGICAL                       :: noc
1548    ret = .false.
1549    noc = .false. ; IF (PRESENT(icase)) noc = icase
1550    IF (LEN(string) == 0 .OR. LEN(substring) == 0) RETURN
1551    zthis = reset_csi(string) ; zstr=reset_csi(substring)
1552    IF (noc) THEN
1553      idx = INDEX(to_lower(zthis),to_lower(zstr),.true.)
1554    ELSE
1555      idx = INDEX(zthis,zstr,.true.)
1556    ENDIF
1557    IF (idx == 0.OR.idx+str_length(zstr)-1 /= str_length(zthis)) RETURN
1558    ret=.true.
1559  END FUNCTION endswith
1560
1561  FUNCTION startswith(string,substring,icase) RESULT(ret)
1562    !! Check if string starts by substring
1563    CHARACTER(len=*), INTENT(in)  :: string
1564      !! A string to check
1565    CHARACTER(len=*), INTENT(in)  :: substring
1566      !! A string to search in __string__
1567    LOGICAL, INTENT(in), OPTIONAL :: icase
1568      !! An optional boolean flag with .true. to perform insensitive case search
1569    LOGICAL :: ret
1570      !! .true. if __string__ starts by __substring__, .false. otherwise.
1571    CHARACTER(len=:), ALLOCATABLE :: zthis,zstr
1572    INTEGER                       :: idx
1573    LOGICAL                       :: noc
1574    ret = .false.
1575    noc = .false. ; IF (PRESENT(icase)) noc = icase
1576    IF (LEN(string) == 0 .OR. LEN(substring) == 0) RETURN
1577    zthis = reset_csi(string) ; zstr=reset_csi(substring)
1578    IF (noc) THEN
1579      idx = INDEX(to_lower(zthis),to_lower(zstr))
1580    ELSE
1581      idx = INDEX(zthis,zstr)
1582    ENDIF
1583    IF (idx /= 1) RETURN
1584    ret=.true.
1585  END FUNCTION startswith
1586
1587  ! CSI related functions
1588  ! ---------------------
1589
1590  FUNCTION add_csi(string,attrs) RESULT(str)
1591    !! Set csi attributes to the given string object
1592    !!
1593    !! The function adds csi (ANSI escape sequences) to the given string and
1594    !! returns a copy of it.
1595    CHARACTER(len=*), INTENT(in)      :: string
1596      !! @param[in] string A string object reference
1597    INTEGER, INTENT(in), DIMENSION(:) :: attrs
1598      !! A vector of integers with the code to add. Each __attrs__ value should refers to one i
1599      !! of [[string_op(module):attributes(variable)]] values.
1600    CHARACTER(len=:), ALLOCATABLE :: str
1601      !! An allocatable string with new csi codes added.
1602    INTEGER                       :: j,iesc,im
1603    CHARACTER(len=:), ALLOCATABLE :: tmp,csi
1604    CHARACTER(len=4), PARAMETER   :: rcsi = CHAR(27)//"[0m"
1605    str=''
1606    ! 1) Check for input string
1607    IF (LEN(string) == 0) RETURN
1608    ! 2) Removes last <ESC>[0m if any and initializes output string
1609    ! we must remove only the last <ESC>[0m if any
1610    IF (INDEX(string,rcsi,.true.) == LEN(string)-3) THEN
1611      tmp = str_remove(string,rcsi,back=.true.)
1612    ELSE
1613      tmp = string
1614    ENDIF
1615    ! 3) Add all the given csi preceded by <ESC>[0m at the beginning of the string
1616    !    if it does not start by an ANSI sequence
1617    IF (INDEX(tmp,CHAR(27)//"[") /= 1) &
1618    tmp = str_add_to_csi(rcsi,attrs)//tmp
1619    ! Loops on new string and updates csi codes
1620    j=1
1621    DO
1622      IF (j>LEN(tmp)) EXIT
1623      ! search for escape
1624      iesc = INDEX(tmp(j:),CHAR(27))
1625      IF (iesc == 0) THEN
1626        ! no more ESC : cat until end of input string and exit
1627        str = str//tmp(j:) ; EXIT
1628      ELSE IF (iesc > 1) THEN
1629        ! ESC is not first char: copy until ESC
1630        str = str//tmp(j:j+iesc-2)
1631      ENDIF
1632      ! search for m
1633      im = INDEX(tmp(j+iesc:),"m")
1634      ! no m in the string after ESC --> copy string (INCLUDING ESC) and leave
1635      IF (im == 0) THEN
1636        str = str//tmp(j+iesc-1:)
1637        RETURN
1638      ENDIF
1639      csi = tmp(j+iesc-1:j+iesc+im-1)
1640      ! we have a csi: we add new codes to it
1641      IF (is_csi(csi)) THEN
1642        csi = str_add_to_csi(csi,attrs)
1643      ENDIF
1644      str = str//csi
1645      j = j + iesc + im
1646    ENDDO
1647    IF (INDEX(str,rcsi,.true.) /= LEN(str)-3) str = str//rcsi
1648    RETURN
1649  END FUNCTION add_csi
1650
1651  FUNCTION del_csi(string,attrs) RESULT(str)
1652    !! Remove attributes to the given string
1653    !!
1654    !! The function removes list of csi (ANSI escape sequences) from the given
1655    !! string and returns a copy of it.
1656    CHARACTER(len=*), INTENT(in)      :: string
1657      !! Input string
1658    INTEGER, INTENT(in), DIMENSION(:) :: attrs
1659      !! A vector of integers with the code to remove. Each __attrs__ value should
1660      !! refers to one of [[string_op(module):attributes(variable)]] values.
1661    CHARACTER(len=:), ALLOCATABLE :: str
1662      !! An allocatable string with csi codes from __list__ removed
1663    LOGICAL                                           :: ok
1664    INTEGER                                           :: j,iesc,im
1665    CHARACTER(len=:), ALLOCATABLE                     :: tmp,csi,csis
1666    CHARACTER(len=st_slen), DIMENSION(:), ALLOCATABLE :: tks
1667    CHARACTER(len=4), PARAMETER                       :: rcsi = CHAR(27)//"[0m"
1668    str=''
1669    IF (LEN(string) == 0) RETURN
1670    ! remove last <ESC>[0m if found at the end of the string
1671    IF (INDEX(string,rcsi,.true.) == LEN(string)-3) THEN
1672      tmp = str_remove(string,rcsi,back=.true.)
1673    ELSE
1674      tmp = string
1675    ENDIF
1676    ! Loops on new string and updates csi codes
1677    j=1 ; csis=""
1678    DO
1679      IF (j>LEN(tmp)) EXIT
1680      ! search for escape
1681      iesc = INDEX(tmp(j:),CHAR(27))
1682      IF (iesc == 0) THEN
1683        ! no more ESC : cat until end of input string and exit
1684        str = str//tmp(j:) ; EXIT
1685      ELSE IF (iesc > 1) THEN
1686        ! ESC is not first char: copy until ESC
1687        str = str//tmp(j:j+iesc-2)
1688      ENDIF
1689      ! search for m
1690      im = INDEX(tmp(j+iesc:),"m")
1691      ! no m in the string after ESC --> copy string (INCLUDING ESC) and leave
1692      IF (im == 0) THEN
1693        str = str//tmp(j+iesc-1:)
1694        RETURN
1695      ENDIF
1696      csi = tmp(j+iesc-1:j+iesc+im-1)
1697      ! we have a csi: we add new codes to it
1698      IF (is_csi(csi)) THEN
1699        csi = str_del_from_csi(csi,attrs)
1700      ENDIF
1701      csis=csis//csi//"|"
1702      str = str//csi
1703      j = j + iesc + im
1704    ENDDO
1705    ! Add <ESC>[0m at the end of string if not found
1706    IF (INDEX(str,rcsi,.true.) /= LEN(str)-3) str = str//rcsi
1707    ! resets all attributes if we only have <ESC>[0m in final list
1708    ok = tokenize(csis(1:LEN(csis)-1),tks,"|")
1709    IF (ALL(tks == rcsi)) str = reset_csi(str)
1710    DEALLOCATE(tks)
1711    RETURN
1712  END FUNCTION del_csi
1713
1714  FUNCTION reset_csi(string) RESULT(str)
1715    !! Reset all csi codes of the string
1716    !!
1717    !! The method removes __all__ the known escape sequences from the input string.
1718    CHARACTER(len=*), INTENT(in) :: string
1719      !! Input string
1720    CHARACTER(len=:), ALLOCATABLE :: str
1721      !! An allocatable string with the copy of input string stripped off csi codes.
1722    INTEGER :: j,iesc,im
1723    LOGICAL :: tcsi
1724    str = ""
1725    IF (LEN(string) == 0) RETURN
1726    j=1
1727    DO
1728      IF (j>LEN(string)) EXIT
1729      ! search for escape
1730      iesc = INDEX(string(j:),CHAR(27))
1731      IF (iesc == 0) THEN
1732        str = str//string(j:) ; EXIT
1733      ENDIF
1734      ! search for m
1735      im = INDEX(string(j+iesc:),"m")
1736      ! no m in the string after ESC --> copy string (INCLUDING ESC) and leave
1737      IF (im == 0) THEN
1738        str = str//string(j+iesc-1:)
1739        RETURN
1740      ENDIF
1741      ! csi includes everything between ESC and m (excluding them):
1742      ! to check for csi it should begin by [ and then be a list of integers
1743      ! separated by ;
1744      tcsi = is_csi(string(j+iesc-1:j+iesc+im-1))
1745      IF (iesc > 1) THEN
1746        str = str//string(j:j+iesc-2)
1747      ENDIF
1748      j = j + iesc ; IF (tcsi) j=j+im
1749    ENDDO
1750    RETURN
1751  END FUNCTION reset_csi
1752
1753  FUNCTION is_csi(value) RESULT(yes)
1754    !! Check if string is a known csi
1755    !!
1756    !! The function only check for known csi code which are defined in [[string_op(module):attributes(variable)]].
1757    CHARACTER(len=*), INTENT(in) :: value
1758      !! A Fortran intrinsic string to check
1759    LOGICAL :: yes
1760      !! .true. if it is a known csi, .false. otherwise
1761    LOGICAL                                           :: ok
1762    CHARACTER(len=:), ALLOCATABLE                     :: tmp
1763    TYPE(words)                                       :: wtks
1764    CHARACTER(len=st_slen), DIMENSION(:), ALLOCATABLE :: stks
1765    INTEGER, DIMENSION(:), ALLOCATABLE                :: nums
1766    INTEGER                                           :: i
1767    yes = .false.
1768    IF (LEN(value) < 4) RETURN
1769    tmp = value(3:len(value)-1)
1770    call words_extend(wtks,tmp,';')
1771    ok = words_to_vector(wtks,stks)
1772    CALL ws_clear_sc(wtks)
1773    IF (.NOT.ok) RETURN
1774    ! if we cannot convert strings to integers : it is not a csi
1775    IF (.NOT.from_string(stks,nums)) RETURN
1776    DEALLOCATE(stks)
1777    DO i=1, SIZE(nums)
1778      IF (.NOT.ANY(attributes == nums(i))) RETURN
1779    ENDDO
1780    yes = .true.
1781  END FUNCTION is_csi
1782
1783  FUNCTION str_add_to_csi(csi,list) RESULT(ncsi)
1784    !! Add a new list of codes to the input csi string
1785    !!
1786    !! The method adds all the csi codes given in __list__ that are known by the module and not
1787    !! already present in the input csi.
1788    CHARACTER(len=*), INTENT(in)      :: csi
1789      !! A string with the input csi. It __must__ begin with "<ESC>[" and ends with "m".
1790    INTEGER, INTENT(in), DIMENSION(:) :: list
1791      !! A vector of integers with the csi code to add. Each value of __list__ should be one of
1792      !! [[string_op(module):attributes(variable)]] values. All unknown values are filtered out as well
1793      !! as csi code already present in input __csi__.
1794    CHARACTER(len=:), ALLOCATABLE :: ncsi
1795      !! A new csi string or the input __csi__ if some "errors" occured (the input csi could not
1796      !! be tokenized or none of __list__ values are left after filtering).
1797    LOGICAL                                            :: ok
1798    CHARACTER(len=st_slen), DIMENSION(:), ALLOCATABLE  :: tks
1799    CHARACTER(len=:), ALLOCATABLE                      :: tmp
1800    INTEGER, DIMENSION(:), ALLOCATABLE                 :: zlist,nums
1801    INTEGER                                            :: i,j,ni,no
1802    ! 1) Filter input list :
1803    ! 1.1) Gets the list of current csi codes
1804    ncsi = csi(3:len(csi)-1)
1805    ok = tokenize(ncsi,tks,"; ",merge=.true.)
1806    IF (.NOT.from_string(tks,nums)) THEN
1807      ncsi = csi
1808      RETURN
1809    ENDIF
1810    DEALLOCATE(tks)
1811    ! 1.2) Filter input list of new flags to add
1812    ! counts number of valid flags
1813    j=0
1814    DO i=1,SIZE(list)
1815      ! new flags must be in attributes but NOT in nums
1816      IF (ANY(attributes==list(i).AND..NOT.ANY(nums == list(i)))) j=j+1
1817    ENDDO
1818    ! No "valid" flags -> returns old csi
1819    IF (j == 0) THEN ; ncsi = csi ; RETURN ; ENDIF
1820    ni = SIZE(nums) ; no = j + ni
1821    ALLOCATE(zlist(no)) ; zlist(1:ni) = nums(:) ; j = ni
1822    DO i=1,SIZE(list)
1823      ! new flags must be in attributes but NOT in nums
1824      IF (ANY(attributes==list(i).AND..NOT.ANY(nums == list(i)))) THEN
1825        j=j+1 ; zlist(j) = list(i)
1826      ENDIF
1827    ENDDO
1828    DEALLOCATE(nums)
1829    ! 2) Builds new csi
1830    !    Here we explictly set the first flag to 0 (i.e. reset attributes)...
1831    ncsi = CHAR(27)//"[0;"
1832    DO i=1,no
1833      ! ... So we get rid of all "0" flag in the list
1834      IF (zlist(i) /= 0) THEN
1835        tmp = to_string(zlist(i))
1836        IF (LEN_TRIM(tmp) == 0) THEN
1837          ncsi = csi ; RETURN
1838        ENDIF
1839        ncsi = ncsi//tmp
1840        IF (i /= no) ncsi = ncsi//";"
1841      ENDIF
1842    ENDDO
1843    ncsi = ncsi//"m"
1844  END FUNCTION str_add_to_csi
1845
1846  FUNCTION str_del_from_csi(csi,list) RESULT(ncsi)
1847    !! Remove a list of codes from the input csi string
1848    !!
1849    !! The method removes all the csi codes given in __list__ that are known by the
1850    !! module and already present in the input csi.
1851    CHARACTER(len=*), INTENT(in)      :: csi
1852      !! An intrinsic Fortran string with the input csi. It __must__ begin with "<ESC>[" and ends with "m".
1853    INTEGER, INTENT(in), DIMENSION(:) :: list
1854      !! A vector of integers with the csi code to remove. Each value of __list__ should be one of
1855      !! [[string_op(module):attributes(variable)]] values. All unknown values are filtered out.
1856    CHARACTER(len=:), ALLOCATABLE :: ncsi
1857      !! A new csi string or the input __csi__ if some "errors" occured (the input csi could not
1858      !! be tokenized or none of __list__ values are left after filtering).
1859    LOGICAL                                            :: ok
1860    CHARACTER(len=LEN(csi)), DIMENSION(:), ALLOCATABLE :: tks
1861    CHARACTER(len=:), ALLOCATABLE                      :: tmp
1862    INTEGER, DIMENSION(:), ALLOCATABLE                 :: nums
1863    INTEGER                                            :: i
1864    ncsi = csi(3:len(csi)-1)
1865    ok = tokenize(ncsi,tks,"; ",merge=.true.)
1866    IF (.NOT.from_string(tks,nums)) THEN
1867      ncsi = csi
1868      RETURN
1869    ENDIF
1870    DEALLOCATE(tks)
1871    tmp=""
1872    DO i=1, SIZE(nums)
1873      IF (ALL(nums(i) /= list).AND.nums(i) /= 0) THEN
1874        ! no need to check for to_string status : it is always ok !
1875        tmp=tmp//to_string(nums(i))//";"
1876      ENDIF
1877    ENDDO
1878    IF (LEN_TRIM(tmp) /= 0) THEN
1879      ncsi=CHAR(27)//"[0;"//tmp(1:LEN(tmp)-1)//"m"
1880    ELSE
1881      ncsi=CHAR(27)//"[0m"
1882    ENDIF
1883  END FUNCTION str_del_from_csi
1884
1885  FUNCTION str_index_of_csi(str,length) RESULT(pos)
1886    !! Get the position of the first known csi in string
1887    !!
1888    !! The method searches for the first known csi in string. The csi must contain known codes
1889    !! (i.e. values of [[string_op(module):attributes(variable)]]).
1890    CHARACTER(len=*), INTENT(in) :: str    !! A string to search in
1891    INTEGER, INTENT(out)         :: length !! Length of the csi in the string
1892    INTEGER                      :: pos    !! Position of the first csi found. It is set to 0 if no csi has been found.
1893    INTEGER :: iesc,im
1894    pos = 0 ; length = 0
1895    ! we need at least 4 chars to create a csi
1896    IF (LEN_TRIM(str) < 4) RETURN
1897    iesc = INDEX(str,CHAR(27))
1898    IF (iesc == 0) RETURN
1899    ! search for m
1900    im = INDEX(str(iesc:),"m")
1901    ! no m in the string after ESC --> copy string (INCLUDING ESC) and leave
1902    IF (im == 0) RETURN
1903    IF (.NOT.is_csi(str(iesc:iesc+im-1))) RETURN
1904    pos = iesc ; length = im
1905  END FUNCTION str_index_of_csi
1906
1907  ! String conversion functions
1908  ! ---------------------------
1909
1910  FUNCTION str2int_sc(str, value) RESULT(ret)
1911    !! Convert string value to integer value (scalar)
1912    CHARACTER(len=*), INTENT(in) :: str   !! String to convert
1913    INTEGER, INTENT(out)         :: value !! Output value
1914    LOGICAL :: ret                        !! Return status (.true. on success)
1915    CHARACTER(len=:), ALLOCATABLE :: zs
1916    ret = .true. ; zs = remove_quotes(str)
1917    IF (string_is(zs) /= st_integer) THEN
1918      ret = .false.
1919    ELSE
1920      READ(zs, *) value
1921    ENDIF
1922    RETURN
1923  END FUNCTION str2int_sc
1924
1925  FUNCTION str2log_sc(str, value) RESULT(ret)
1926    !! Convert string value to logical value (scalar)
1927    CHARACTER(len=*), INTENT(in) :: str   !! String to convert
1928    LOGICAL, INTENT(out)         :: value !! Output value
1929    LOGICAL :: ret                        !! Return status (.true. on success)
1930    CHARACTER(len=:), ALLOCATABLE :: zs
1931    integer :: r
1932    ret = .true. ; zs = remove_quotes(str)
1933    r = string_is(zs)
1934    IF (string_is(zs) /= st_logical) THEN
1935      ret = .false.
1936    ELSE
1937      READ(zs, *) value
1938    ENDIF
1939    RETURN
1940  END FUNCTION str2log_sc
1941
1942  FUNCTION str2real_sc(str, value) RESULT(ret)
1943    !! Convert string value to simple precision floating precision value (scalar)
1944    CHARACTER(len=*), INTENT(in) :: str   !! String to convert
1945    REAL(kind=4), INTENT(out)    :: value !! Output value
1946    LOGICAL :: ret                        !! Return status (.true. on success)
1947    CHARACTER(len=:), ALLOCATABLE :: zs
1948    ret = .true.; zs = remove_quotes(str)
1949    IF (string_is(zs) < st_integer) THEN
1950      ret = .false.
1951    ELSE
1952      READ(zs, *) value
1953    ENDIF
1954    RETURN
1955  END FUNCTION str2real_sc
1956
1957  FUNCTION str2dble_sc(str, value) RESULT(ret)
1958    !! Convert string value to double precision floating precision value (scalar)
1959    CHARACTER(len=*), INTENT(in) :: str   !! String to convert
1960    REAL(kind=8), INTENT(out)    :: value !! Output value
1961    LOGICAL :: ret                        !! Return status (.true. on success)
1962    CHARACTER(len=:), ALLOCATABLE :: zs
1963    ret = .true. ; zs = remove_quotes(str)
1964    IF (string_is(zs) < st_integer) THEN
1965      ret = .false.
1966    ELSE
1967      READ(zs, *) value
1968    ENDIF
1969    RETURN
1970  END FUNCTION str2dble_sc
1971
1972  FUNCTION str2cplx_sc(str, value) RESULT(ret)
1973    !! Convert string value to complex value (scalar)
1974    CHARACTER(len=*), INTENT(in) :: str   !! String to convert
1975    COMPLEX(kind=4), INTENT(out) :: value !! Output value
1976    LOGICAL :: ret                        !! Return status (.true. on success)
1977    ! - LOCAL
1978    CHARACTER(len=:), ALLOCATABLE :: zs
1979    ret = .true. ; zs = remove_quotes(str)
1980    IF (string_is(zs) /= st_complex) THEN
1981      ret = .false.
1982    ELSE
1983      READ(zs, *) value
1984    ENDIF
1985    RETURN
1986  END FUNCTION str2cplx_sc
1987
1988  FUNCTION str2int_ve(str, value) RESULT(ret)
1989    !! Convert strings values to integer values (vector)
1990    CHARACTER(len=*), INTENT(in), DIMENSION(:)      :: str   !! Vector of strings to convert
1991    INTEGER, INTENT(out), DIMENSION(:), ALLOCATABLE :: value !! Vector of output values
1992    LOGICAL :: ret                                           !! Return status (.true. on success)
1993    INTEGER                       :: i,ns
1994    CHARACTER(len=:), ALLOCATABLE :: zs
1995    ret = .true. ; ns = SIZE(str) ; ALLOCATE(value(ns))
1996    DO i=1,ns
1997      zs = remove_quotes(str(i))
1998      IF (string_is(zs) /= st_integer) THEN
1999        ret = .false. ; DEALLOCATE(value) ; RETURN
2000      ELSE
2001        READ(zs, *) value(i)
2002      ENDIF
2003    ENDDO
2004    RETURN
2005  END FUNCTION str2int_ve
2006
2007  FUNCTION str2log_ve(str, value) RESULT(ret)
2008    !! Convert strings values to logical values (vector)
2009    CHARACTER(len=*), INTENT(in), DIMENSION(:)      :: str   !! Vector of strings to convert
2010    LOGICAL, INTENT(out), DIMENSION(:), ALLOCATABLE :: value !! Vector of output values
2011    LOGICAL :: ret                                           !! Return status (.true. on success)
2012    INTEGER                       :: i,ns
2013    CHARACTER(len=:), ALLOCATABLE :: zs
2014    ret = .true. ; ns = SIZE(str) ; ALLOCATE(value(ns))
2015    DO i=1,ns
2016      zs = remove_quotes(str(i))
2017      IF (string_is(zs) /= st_logical) THEN
2018        ret = .false. ; DEALLOCATE(value) ; RETURN
2019      ELSE
2020        READ(zs, *) value(i)
2021      ENDIF
2022    ENDDO
2023    RETURN
2024  END FUNCTION str2log_ve
2025
2026  FUNCTION str2real_ve(str, value) RESULT(ret)
2027    !! Convert strings values to simple precision floating point values (vector)
2028    CHARACTER(len=*), INTENT(in), DIMENSION(:)           :: str   !! Vector of strings to convert
2029    REAL(kind=4), INTENT(out), DIMENSION(:), ALLOCATABLE :: value !! Vector of output values
2030    LOGICAL :: ret                                                !! Return status (.true. on success)
2031    INTEGER                       :: i,ns
2032    CHARACTER(len=:), ALLOCATABLE :: zs
2033    ret = .true. ; ns = SIZE(str) ; ALLOCATE(value(ns))
2034    DO i=1,ns
2035      IF (string_is(zs) < st_integer) THEN
2036        ret = .false. ; DEALLOCATE(value) ; RETURN
2037      ELSE
2038        READ(zs, *) value(i)
2039      ENDIF
2040    ENDDO
2041    RETURN
2042  END FUNCTION str2real_ve
2043
2044  FUNCTION str2dble_ve(str, value) RESULT(ret)
2045    !! Convert strings values to double precision floating point values (vector)
2046    CHARACTER(len=*), INTENT(in), DIMENSION(:)           :: str   !! Vector of strings to convert
2047    REAL(kind=8), INTENT(out), DIMENSION(:), ALLOCATABLE :: value !! Vector of output values
2048    LOGICAL :: ret                                                !! Return status (.true. on success)
2049    INTEGER                       :: i,ns
2050    CHARACTER(len=:), ALLOCATABLE :: zs
2051    ret = .true. ; ns = SIZE(str) ; ALLOCATE(value(ns))
2052    DO i=1,ns
2053      zs = remove_quotes(str(i))
2054      IF (string_is(zs) < st_integer) THEN
2055        ret = .false. ; DEALLOCATE(value) ; RETURN
2056      ELSE
2057        READ(zs, *) value(i)
2058      ENDIF
2059    ENDDO
2060    RETURN
2061  END FUNCTION str2dble_ve
2062
2063  FUNCTION str2cplx_ve(str, value) RESULT(ret)
2064    !! Convert strings values to complex values (vector)
2065    CHARACTER(len=*), INTENT(in), DIMENSION(:)              :: str   !! Vector of strings to convert
2066    COMPLEX(kind=4), INTENT(out), DIMENSION(:), ALLOCATABLE :: value !! Vector of output values
2067    LOGICAL :: ret                                                   !! Return status (.true. on success)
2068    INTEGER                       :: i,ns
2069    CHARACTER(len=:), ALLOCATABLE :: zs
2070    ret = .true. ; ns = SIZE(str) ; ALLOCATE(value(ns))
2071    DO i=1,ns
2072      zs = remove_quotes(str(i))
2073      IF (string_is(zs) /= st_complex) THEN
2074        ret = .false. ; DEALLOCATE(value) ; RETURN
2075      ELSE
2076        READ(zs, *) value(i)
2077      ENDIF
2078    ENDDO
2079    RETURN
2080  END FUNCTION str2cplx_ve
2081
2082  FUNCTION int2str_as(value) RESULT(str)
2083    !! Convert an integer value to string (auto format / string result)
2084    INTEGER, INTENT(in)           :: value !! Value to convert
2085    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
2086    INTEGER :: err
2087    ALLOCATE(CHARACTER(len=DIGITS(value)) :: str)
2088    WRITE(str,*,iostat=err) value
2089    str = TRIM(ADJUSTL(str))
2090    IF (err /= 0) str = ''
2091    RETURN
2092  END FUNCTION int2str_as
2093
2094  FUNCTION log2str_as(value) RESULT(str)
2095    !! Convert a logical value to string (auto format / string result)
2096    LOGICAL, INTENT(in)           :: value !! Value to convert
2097    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
2098    INTEGER :: err
2099    ALLOCATE(CHARACTER(len=2) :: str)
2100    WRITE(str, *, IOSTAT = err) value
2101    str=TRIM(ADJUSTL(str))
2102    IF (err /= 0) str = ''
2103    RETURN
2104  END FUNCTION log2str_as
2105
2106  FUNCTION real2str_as(value) RESULT(str)
2107    !! Convert a simple precision floating point value to string (auto format / string result)
2108    REAL(kind=4), INTENT(in)      :: value !! Value to convert
2109    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
2110    INTEGER :: err
2111    ALLOCATE(CHARACTER(len=DIGITS(value)) ::str)
2112    WRITE(str,*, IOSTAT = err) value
2113    str=TRIM(ADJUSTL(str))
2114    IF (err /= 0)  str = ''
2115    RETURN
2116  END FUNCTION real2str_as
2117
2118  FUNCTION dble2str_as(value) RESULT(str)
2119    !! Convert a double precision floating point value to string (auto format / string result)
2120    REAL(kind=8), INTENT(in)      :: value !! Value to convert
2121    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
2122    INTEGER :: err
2123    ALLOCATE(CHARACTER(len=DIGITS(value)) ::str)
2124    WRITE(str,*, IOSTAT = err) value
2125    str=TRIM(ADJUSTL(str))
2126    IF (err /= 0) str = ''
2127    RETURN
2128  END FUNCTION dble2str_as
2129
2130  FUNCTION cplx2str_as(value) RESULT(str)
2131    !! Convert a complex value to string (auto format / string result)
2132    COMPLEX(kind=4), INTENT(in)   :: value !! Value to convert
2133    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
2134    INTEGER :: err,sl
2135    sl = DIGITS(REAL(value))*2+3
2136    ALLOCATE(CHARACTER(len=sl) :: str)
2137    WRITE(str, *, IOSTAT = err) value
2138    str = TRIM(ADJUSTL(str))
2139    IF (err /= 0) str = ''
2140    RETURN
2141  END FUNCTION cplx2str_as
2142
2143  FUNCTION dcplx2str_as(value) RESULT(str)
2144    !! Convert a complex value to string (auto format / string result)
2145    COMPLEX(kind=8), INTENT(in)   :: value !! Value to convert
2146    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
2147    INTEGER :: err,sl
2148    sl = DIGITS(REAL(value))*2+3
2149    ALLOCATE(CHARACTER(len=sl) :: str)
2150    WRITE(str, *, IOSTAT = err) value
2151    str = TRIM(ADJUSTL(str))
2152    IF (err /= 0) str = ''
2153    RETURN
2154  END FUNCTION dcplx2str_as
2155
2156  FUNCTION int2str_fs(value, fmt) RESULT(str)
2157    !! Convert an integer value to string (user format / string result)
2158    INTEGER, INTENT(in)           :: value !! Value to convert
2159    CHARACTER(len=*), INTENT(in)  :: fmt   !! String format
2160    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
2161    INTEGER :: err
2162    ALLOCATE(CHARACTER(len=st_slen) :: str)
2163    WRITE(str, '('//fmt//')', IOSTAT = err) value
2164    str = TRIM(ADJUSTL(str))
2165    IF (err /= 0) str = ''
2166    RETURN
2167  END FUNCTION int2str_fs
2168
2169  FUNCTION log2str_fs(value, fmt) RESULT(str)
2170    !! Convert a logical value to string (user format / string result)
2171    LOGICAL, INTENT(in)           :: value !! Value to convert
2172    CHARACTER(len=*), INTENT(in)  :: fmt   !! String format
2173    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
2174    INTEGER :: err
2175    ALLOCATE(CHARACTER(len=st_slen) :: str)
2176    WRITE(str, '('//fmt//')', IOSTAT = err) value
2177    str=TRIM(ADJUSTL(str))
2178    IF (err /= 0) str = ''
2179    RETURN
2180  END FUNCTION log2str_fs
2181
2182  FUNCTION real2str_fs(value, fmt) RESULT(str)
2183    !! Convert a simple precision floating point value to string (user format / string result)
2184    REAL(kind=4), INTENT(in)      :: value !! Value to convert
2185    CHARACTER(len=*), INTENT(in)  :: fmt   !! String format
2186    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
2187    INTEGER :: err
2188    ALLOCATE(CHARACTER(len=st_slen) :: str)
2189    WRITE(str, '('//fmt//')', IOSTAT = err) value
2190    str = TRIM(ADJUSTL(str))
2191    IF (err /= 0) str = ''
2192    RETURN
2193  END FUNCTION real2str_fs
2194
2195  FUNCTION dble2str_fs(value, fmt) RESULT(str)
2196    !! Convert a double precision floating point value to string (user format / string result)
2197    REAL(kind=8), INTENT(in)      :: value !! Value to convert
2198    CHARACTER(len=*), INTENT(in)  :: fmt   !! String format
2199    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
2200    INTEGER :: err
2201    ALLOCATE(CHARACTER(len=st_slen) :: str)
2202    WRITE(str, '('//fmt//')', IOSTAT = err) value
2203    str = TRIM(ADJUSTL(str))
2204    IF (err /= 0) str = ''
2205    RETURN
2206  END FUNCTION dble2str_fs
2207
2208  FUNCTION cplx2str_fs(value, fmt) RESULT(str)
2209    !! Convert a complex value to string (user format / string result)
2210    COMPLEX(kind=4), INTENT(in)   :: value !! Value to convert
2211    CHARACTER(len=*), INTENT(in)  :: fmt   !! String format
2212    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
2213    INTEGER :: err
2214    ALLOCATE(CHARACTER(len=st_slen) :: str)
2215    WRITE(str, '('//fmt//')', IOSTAT = err) value
2216    str = TRIM(ADJUSTL(str))
2217    IF (err /= 0) str = ''
2218    RETURN
2219  END FUNCTION cplx2str_fs
2220
2221  FUNCTION dcplx2str_fs(value, fmt) RESULT(str)
2222    !! Convert a complex value to string (user format / string result)
2223    COMPLEX(kind=8), INTENT(in)   :: value !! Value to convert
2224    CHARACTER(len=*), INTENT(in)  :: fmt   !! String format
2225    CHARACTER(len=:), ALLOCATABLE :: str   !! String with the converted value in output
2226    INTEGER :: err
2227    ALLOCATE(CHARACTER(len=st_slen) :: str)
2228    WRITE(str, '('//fmt//')', IOSTAT = err) value
2229    str = TRIM(ADJUSTL(str))
2230    IF (err /= 0) str = ''
2231    RETURN
2232  END FUNCTION dcplx2str_fs
2233
2234  ! Extended strings features
2235  ! ---------------------------
2236
2237  FUNCTION fis_cat_int(str1,int2) RESULT(str)
2238    !! Concatenate a fortran intrinsic string with a integer.
2239    CHARACTER(len=*), INTENT(in)  :: str1 !! String to concatenate
2240    INTEGER, INTENT(in)           :: int2 !! Integer to concatenate
2241    CHARACTER(len=:), ALLOCATABLE :: str  !! Output string
2242    ALLOCATE(CHARACTER(len=DIGITS(int2)) :: str)
2243    WRITE(str,*) int2 ; str = TRIM(ADJUSTL(str))
2244    IF (LEN(str1) /= 0) str = str1//str
2245    RETURN
2246  END FUNCTION fis_cat_int
2247
2248    !! @param[in] int2 An integer to concatenate
2249    !! @param[in] str1 A string to concatenate
2250    !! @return An allocatable string with the concatenation of input values.
2251  FUNCTION fis_cat_int_inv(int2,str1) RESULT(str)
2252    !! Concatenate a fortran intrinsic string with a integer (reversed).
2253    INTEGER, INTENT(in)           :: int2 !! Integer to concatenate
2254    CHARACTER(len=*), INTENT(in)  :: str1 !! String to concatenate
2255    CHARACTER(len=:), ALLOCATABLE :: str  !! Output string
2256    ALLOCATE(CHARACTER(len=DIGITS(int2)) :: str)
2257    WRITE(str,*) int2 ; str = TRIM(ADJUSTL(str))
2258    IF (LEN(str1) /= 0) str = str//str1
2259    RETURN
2260  END FUNCTION fis_cat_int_inv
2261
2262  FUNCTION fis_cat_bool(str1,bool2) RESULT(str)
2263    !! Concatenate a string with a logical
2264    CHARACTER(len=*), INTENT(in)  :: str1  !! String to concatenate
2265    LOGICAL, INTENT(in)           :: bool2 !! Logical to concatenate
2266    CHARACTER(len=:), ALLOCATABLE :: str   !! Output string
2267    CHARACTER(len=2) ::tmp
2268    WRITE(tmp,*) bool2
2269    str=TRIM(ADJUSTL(tmp))
2270    IF (LEN(str1) /= 0) str = str1//str
2271    RETURN
2272  END FUNCTION fis_cat_bool
2273
2274  FUNCTION fis_cat_bool_inv(bool2,str1) RESULT(str)
2275    !! Concatenate a string with a logical (reversed)
2276    LOGICAL, INTENT(in)           :: bool2 !! Logical to concatenate
2277    CHARACTER(len=*), INTENT(in)  :: str1    !! String to concatenate
2278    CHARACTER(len=:), ALLOCATABLE :: str     !! Output string
2279    CHARACTER(len=2) ::tmp
2280    WRITE(tmp,*) bool2
2281    str = TRIM(ADJUSTL(tmp))
2282    IF (LEN(str1) /= 0) str = str//str1
2283    RETURN
2284  END FUNCTION fis_cat_bool_inv
2285
2286  FUNCTION fis_cat_real(str1,real2) RESULT(str)
2287    !! Concatenate a string with a real simple precision
2288    CHARACTER(len=*), INTENT(in)  :: str1    !! String to concatenate
2289    REAL(kind=4), INTENT(in)      :: real2 !! Simple precision real to concatenate
2290    CHARACTER(len=:), ALLOCATABLE :: str     !! Output string
2291    ALLOCATE(CHARACTER(len=DIGITS(real2)) :: str)
2292    WRITE(str,*) real2 ; str = TRIM(ADJUSTL(str))
2293    IF (LEN(str1) /= 0) str=str1//str
2294    RETURN
2295  END FUNCTION fis_cat_real
2296
2297  FUNCTION fis_cat_real_inv(real2,str1) RESULT(str)
2298    !! Concatenate a string with a real simple precision (reversed)
2299    REAL(kind=4), INTENT(in)      :: real2 !! Simple precision real to concatenate
2300    CHARACTER(len=*), INTENT(in)  :: str1  !! String to concatenate
2301    CHARACTER(len=:), ALLOCATABLE :: str   !! Output string
2302    ALLOCATE(CHARACTER(len=DIGITS(real2)) :: str)
2303    WRITE(str,*) real2  ; str = TRIM(ADJUSTL(str))
2304    IF (LEN(str1) /= 0) str = str//str1
2305    RETURN
2306  END FUNCTION fis_cat_real_inv
2307
2308  FUNCTION fis_cat_double(str1,double2) RESULT(str)
2309    !! Concatenate a string with a real double precision
2310    CHARACTER(len=*), INTENT(in)  :: str1    !! String to concatenate
2311    REAL(kind=8), INTENT(in)      :: double2 !! Double precision real to concatenate
2312    CHARACTER(len=:), ALLOCATABLE :: str     !! Output string
2313    ALLOCATE(CHARACTER(len=DIGITS(double2)) :: str)
2314    WRITE(str,*) double2 ; str = TRIM(ADJUSTL(str))
2315    IF (LEN(str1) /= 0) str=str1//str
2316    RETURN
2317  END FUNCTION fis_cat_double
2318
2319  FUNCTION fis_cat_double_inv(double2,str1) RESULT(str)
2320    !! Concatenate a string with a real double precision (reversed)
2321    REAL(kind=8), INTENT(in)      :: double2 !! Double precision real to concatenate
2322    CHARACTER(len=*), INTENT(in)  :: str1    !! String to concatenate
2323    CHARACTER(len=:), ALLOCATABLE :: str     !! Output string
2324    ALLOCATE(CHARACTER(len=DIGITS(double2)) :: str)
2325    WRITE(str,*) double2  ; str = TRIM(ADJUSTL(str))
2326    IF (LEN(str1) /= 0) str = str//str1
2327    RETURN
2328  END FUNCTION fis_cat_double_inv
2329
2330  FUNCTION fis_cat_cplx(str1,cplx2) RESULT(str)
2331    !! Concatenate a string with a complex
2332    CHARACTER(len=*), INTENT(in)  :: str1  !! String to concatenate
2333    COMPLEX(kind=4), INTENT(in)   :: cplx2 !! Complex value to concatenate
2334    CHARACTER(len=:), ALLOCATABLE :: str   !! Output string
2335    INTEGER :: sl
2336    sl = DIGITS(REAL(cplx2))*2+3
2337    ALLOCATE(CHARACTER(len=sl) :: str)
2338    WRITE(str,*) cplx2 ; str = TRIM(ADJUSTL(str))
2339    IF (LEN(str1) /= 0) str = str//str1
2340    RETURN
2341  END FUNCTION fis_cat_cplx
2342
2343  FUNCTION fis_cat_cplx_inv(cplx2,str1) RESULT(str)
2344    !! Concatenate a string with a complex (reversed)
2345    COMPLEX(kind=4), INTENT(in)   :: cplx2 !! Complex value to concatenate
2346    CHARACTER(len=*), INTENT(in)  :: str1  !! String to concatenate
2347    CHARACTER(len=:), ALLOCATABLE :: str   !! Output string
2348    INTEGER :: sl
2349    sl = DIGITS(REAL(cplx2))*2+3
2350    ALLOCATE(CHARACTER(len=sl) :: str)
2351    WRITE(str,*) cplx2
2352    str = TRIM(ADJUSTL(str))
2353    IF (LEN(str1) /= 0) str = str//str1
2354    RETURN
2355  END FUNCTION fis_cat_cplx_inv
2356
2357  FUNCTION fis_cat_dcplx(str1,dcplx2) RESULT(str)
2358    !! Concatenate a string with a double precision complex
2359    CHARACTER(len=*), INTENT(in)  :: str1   !! String to concatenate
2360    COMPLEX(kind=8), INTENT(in)   :: dcplx2 !! Complex value to concatenate
2361    CHARACTER(len=:), ALLOCATABLE :: str    !! Output string
2362    INTEGER :: sl
2363    sl = DIGITS(REAL(dcplx2))*2+3
2364    ALLOCATE(CHARACTER(len=sl) :: str)
2365    WRITE(str,*) dcplx2 ; str = TRIM(ADJUSTL(str))
2366    IF (LEN(str1) /= 0) str = str//str1
2367    RETURN
2368  END FUNCTION fis_cat_dcplx
2369
2370  FUNCTION fis_cat_dcplx_inv(dcplx2,str1) RESULT(str)
2371    !! Concatenate a string with a double precision complex (reversed)
2372    COMPLEX(kind=8), INTENT(in)   :: dcplx2 !! Complex value to concatenate
2373    CHARACTER(len=*), INTENT(in)  :: str1   !! string to concatenate
2374    CHARACTER(len=:), ALLOCATABLE :: str    !! Output string
2375    INTEGER :: sl
2376    sl = DIGITS(REAL(dcplx2))*2+3
2377    ALLOCATE(CHARACTER(len=sl) :: str)
2378    WRITE(str,*) dcplx2
2379    str = TRIM(ADJUSTL(str))
2380    IF (LEN(str1) /= 0) str = str//str1
2381    RETURN
2382  END FUNCTION fis_cat_dcplx_inv
2383
2384  SUBROUTINE fis_affect_int(str,int)
2385    !! Assignment subroutine (using intrinsic integer)
2386    CHARACTER(len=:), INTENT(out), ALLOCATABLE :: str !! Output string to be assigned
2387    INTEGER, INTENT(in)                        :: int !! Input value to assign
2388    str = fis_cat_int('',int)
2389  END SUBROUTINE fis_affect_int
2390
2391  SUBROUTINE fis_affect_bool(str,bool)
2392    !! Assignment subroutine (using intrinsic logical)
2393    CHARACTER(len=:), INTENT(out), ALLOCATABLE :: str  !! Output string to be assigned
2394    LOGICAL, INTENT(in)                        :: bool !! Input value to assign
2395    str = fis_cat_bool('',bool)
2396  END SUBROUTINE fis_affect_bool
2397
2398  SUBROUTINE fis_affect_real(str,float)
2399    !! Assignment subroutine (using intrinsic real)
2400    CHARACTER(len=:), INTENT(out), ALLOCATABLE :: str   !! Output string to be assigned
2401    REAL(kind=4), INTENT(in)                   :: float !! Input value to assign
2402    str = fis_cat_real('',float)
2403  END SUBROUTINE fis_affect_real
2404
2405  SUBROUTINE fis_affect_double(str,double)
2406    !! Assignment subroutine (using intrinsic real(kind=8))
2407    CHARACTER(len=:), INTENT(out), ALLOCATABLE :: str    !! Output string to be assigned
2408    REAL(kind=8), INTENT(in)                   :: double !! Input value to assign
2409    str = fis_cat_double('',double)
2410  END SUBROUTINE fis_affect_double
2411
2412  SUBROUTINE fis_affect_cplx(str,cplx)
2413    !! Assignment subroutine (using intrinsic complex)
2414    CHARACTER(len=:), INTENT(out), ALLOCATABLE :: str  !! Output string to be assigned
2415    COMPLEX(kind=4), INTENT(in)                :: cplx !! Input value to assign
2416    str = fis_cat_cplx('',cplx)
2417  END SUBROUTINE fis_affect_cplx
2418
2419  SUBROUTINE fis_affect_dcplx(str,dcplx)
2420    !! Assignment subroutine (using intrinsic complex(kind=8))
2421    CHARACTER(len=:), INTENT(out), ALLOCATABLE :: str   !! Output string to be assigned
2422    COMPLEX(kind=8), INTENT(in)                :: dcplx !! Input value to assign
2423    str = fis_cat_dcplx('',dcplx)
2424  END SUBROUTINE fis_affect_dcplx
2425
2426  FUNCTION get_attrs_indexes(flags) RESULT(codes)
2427    !! Convert a list of csi flags into a csi codes.
2428    !!
2429    !! Only know CSI codes are returned. If no known CSI are found the outputput vector is
2430    !! allocated with 0 elements.
2431    CHARACTER(len=2), DIMENSION(:), INTENT(in) :: flags !! CSI attributes flags
2432    INTEGER, DIMENSION(:), ALLOCATABLE :: codes         !! CSI codes.
2433    INTEGER :: i,j,n
2434    n = 0
2435    ALLOCATE(codes(SIZE(flags)))
2436    codes(:) = -1
2437    DO i = 1, SIZE(flags)
2438      DO j = 1, SIZE(attributes)
2439        IF (to_lower(flags(i)) == csis(j)) THEN
2440          n = n + 1
2441          codes(n) = attributes(j)
2442          EXIT
2443        ENDIF
2444      ENDDO
2445    ENDDO
2446    IF (n > 0) THEN
2447      codes = codes(1:n)
2448    ELSE
2449      DEALLOCATE(codes)
2450      ALLOCATE(codes(0))
2451    ENDIF
2452  END FUNCTION get_attrs_indexes
2453
2454  FUNCTION fancy_fstr(value,flags,fmt) RESULT(output)
2455    !! Compute a fancy string from the given (fortran intrinsic) string.
2456    CHARACTER(len=*), INTENT(in)               :: value  !! String object reference
2457    CHARACTER(len=2), DIMENSION(:), INTENT(in) :: flags  !! CSI attributes flags
2458    CHARACTER(len=*), INTENT(in), OPTIONAL     :: fmt    !! Optional format (unused for this overload)
2459    CHARACTER(len=:), ALLOCATABLE              :: output !! Output fortran instrinsic string
2460    INTEGER, DIMENSION(:), ALLOCATABLE :: codes
2461    codes = get_attrs_indexes(flags)
2462    IF (SIZE(codes) == 0) THEN
2463      output = value ; RETURN
2464    ELSE
2465      output = add_csi(value,codes)
2466    ENDIF
2467  END FUNCTION fancy_fstr
2468
2469  FUNCTION fancy_int(value,flags,fmt) RESULT(output)
2470    !! Compute a fancy string from the given integer value.
2471    INTEGER, INTENT(in)                        :: value  !! String object reference
2472    CHARACTER(len=2), DIMENSION(:), INTENT(in) :: flags  !! CSI attributes flags
2473    CHARACTER(len=*), INTENT(in), OPTIONAL     :: fmt    !! Optional format. If given it must be a valid Fortran format.
2474    CHARACTER(len=:), ALLOCATABLE              :: output !! Output fortran instrinsic string
2475    INTEGER, DIMENSION(:), ALLOCATABLE :: codes
2476    CHARACTER(len=:), ALLOCATABLE      :: tmp
2477    codes = get_attrs_indexes(flags)
2478    IF (PRESENT(fmt)) THEN ; tmp = to_string(value,fmt) ; ELSE ; tmp = to_string(value) ; ENDIF
2479    IF (SIZE(codes) /= 0) THEN ; output = add_csi(tmp,codes) ; ELSE ; output = tmp ; ENDIF
2480  END FUNCTION fancy_int
2481
2482  FUNCTION fancy_bool(value,flags,fmt) RESULT(output)
2483    !! Compute a fancy string from the given logical value.
2484    LOGICAL, INTENT(in)                        :: value  !! String object reference
2485    CHARACTER(len=2), DIMENSION(:), INTENT(in) :: flags  !! CSI attributes flags
2486    CHARACTER(len=*), INTENT(in), OPTIONAL     :: fmt    !! Optional format. If given it must be a valid Fortran format.
2487    CHARACTER(len=:), ALLOCATABLE              :: output !! Output fortran instrinsic string
2488    INTEGER, DIMENSION(:), ALLOCATABLE :: codes
2489    CHARACTER(len=:), ALLOCATABLE      :: tmp
2490    codes = get_attrs_indexes(flags)
2491    IF (PRESENT(fmt)) THEN ; tmp = to_string(value,fmt) ; ELSE ; tmp = to_string(value) ; ENDIF
2492    IF (SIZE(codes) /= 0) THEN ; output = add_csi(tmp,codes) ; ELSE ; output = tmp ; ENDIF
2493  END FUNCTION fancy_bool
2494
2495  FUNCTION fancy_real(value,flags,fmt) RESULT(output)
2496    !! Compute a fancy string from the given real value (simple precision).
2497    REAL(kind=4), INTENT(in)                   :: value  !! String object reference
2498    CHARACTER(len=2), DIMENSION(:), INTENT(in) :: flags  !! CSI attributes flags
2499    CHARACTER(len=*), INTENT(in), OPTIONAL     :: fmt    !! Optional format. If given it must be a valid Fortran format.
2500    CHARACTER(len=:), ALLOCATABLE              :: output !! Output fortran instrinsic string
2501    INTEGER, DIMENSION(:), ALLOCATABLE :: codes
2502    CHARACTER(len=:), ALLOCATABLE      :: tmp
2503    codes = get_attrs_indexes(flags)
2504    IF (PRESENT(fmt)) THEN ; tmp = to_string(value,fmt) ; ELSE ; tmp = to_string(value) ; ENDIF
2505    IF (SIZE(codes) /= 0) THEN ; output = add_csi(tmp,codes) ; ELSE ; output = tmp ; ENDIF
2506  END FUNCTION fancy_real
2507
2508  FUNCTION fancy_double(value,flags,fmt) RESULT(output)
2509    !! Compute a fancy string from the given real value (double precision).
2510    REAL(kind=8), INTENT(in)                   :: value  !! String object reference
2511    CHARACTER(len=2), DIMENSION(:), INTENT(in) :: flags  !! CSI attributes flags
2512    CHARACTER(len=*), INTENT(in), OPTIONAL     :: fmt    !! Optional format. If given it must be a valid Fortran format.
2513    CHARACTER(len=:), ALLOCATABLE              :: output !! Output fortran instrinsic string
2514    INTEGER, DIMENSION(:), ALLOCATABLE :: codes
2515    CHARACTER(len=:), ALLOCATABLE      :: tmp
2516    codes = get_attrs_indexes(flags)
2517    IF (PRESENT(fmt)) THEN ; tmp = to_string(value,fmt) ; ELSE ; tmp = to_string(value) ; ENDIF
2518    IF (SIZE(codes) /= 0) THEN ; output = add_csi(tmp,codes) ; ELSE ; output = tmp ; ENDIF
2519  END FUNCTION fancy_double
2520
2521  FUNCTION fancy_cplx(value,flags,fmt) RESULT(output)
2522    !! Compute a fancy string from the given complex value (simple precision).
2523    COMPLEX(kind=4), INTENT(in)                :: value  !! String object reference
2524    CHARACTER(len=2), DIMENSION(:), INTENT(in) :: flags  !! CSI attributes flags
2525    CHARACTER(len=*), INTENT(in), OPTIONAL     :: fmt    !! Optional format. If given it must be a valid Fortran format.
2526    CHARACTER(len=:), ALLOCATABLE              :: output !! Output fortran instrinsic string
2527    INTEGER, DIMENSION(:), ALLOCATABLE :: codes
2528    CHARACTER(len=:), ALLOCATABLE      :: tmp
2529    codes = get_attrs_indexes(flags)
2530    IF (PRESENT(fmt)) THEN ; tmp = to_string(value,fmt) ; ELSE ; tmp = to_string(value) ; ENDIF
2531    IF (SIZE(codes) /= 0) THEN ; output = add_csi(tmp,codes) ; ELSE ; output = tmp ; ENDIF
2532  END FUNCTION fancy_cplx
2533 
2534  FUNCTION fancy_dcplx(value,flags,fmt) RESULT(output)
2535    !! Compute a fancy string from the given complex value (double precision).
2536    COMPLEX(kind=8), INTENT(in)                :: value  !! String object reference
2537    CHARACTER(len=2), DIMENSION(:), INTENT(in) :: flags  !! CSI attributes flags
2538    CHARACTER(len=*), INTENT(in), OPTIONAL     :: fmt    !! Optional format. If given it must be a valid Fortran format.
2539    CHARACTER(len=:), ALLOCATABLE              :: output !! Output fortran instrinsic string
2540    INTEGER, DIMENSION(:), ALLOCATABLE :: codes
2541    CHARACTER(len=:), ALLOCATABLE      :: tmp
2542    codes = get_attrs_indexes(flags)
2543    IF (PRESENT(fmt)) THEN ; tmp = to_string(value,fmt) ; ELSE ; tmp = to_string(value) ; ENDIF
2544    IF (SIZE(codes) /= 0) THEN ; output = add_csi(tmp,codes) ; ELSE ; output = tmp ; ENDIF
2545  END FUNCTION fancy_dcplx
2546
2547END MODULE STRING_OP
Note: See TracBrowser for help on using the repository browser.