My Project
mod_sng.f90
Go to the documentation of this file.
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 !/===========================================================================/
13 ! CVS VERSION INFORMATION
14 ! $Id$
15 ! $Name$
16 ! $Revision$
17 !/===========================================================================/
18 ! Purpose: Library of Fortran string manipulation routines
19 !
20 ! Copyright (C) 1997--2005 Charlie Zender
21 ! License Summary: X11-style free, non-restrictive, non-copyleft.
22 ! Permission is hereby granted, free of charge, to any person obtaining a
23 ! copy of this software and associated documentation files (the
24 ! "Software"), to deal in the Software without restriction, including
25 ! without limitation the rights to use, copy, modify, merge, publish,
26 ! distribute, and/or sell copies of the Software, and to permit persons
27 ! to whom the Software is furnished to do so, provided that the above
28 ! copyright notice(s) and this permission notice appear in all copies of
29 ! the Software and that both the above copyright notice(s) and this
30 ! permission notice appear in supporting documentation.
31 ! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
32 ! OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33 ! MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
34 ! OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
35 ! HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
36 ! INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
37 ! FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
38 ! NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
39 ! WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
40 ! Except as contained in this notice, the name of a copyright holder
41 ! shall not be used in advertising or otherwise to promote the sale, use
42 ! or other dealings in this Software without prior written authorization
43 ! of the copyright holder.
44 
45 ! The original author of this software, Charlie Zender, wants to receive
46 ! your suggestions, thanks, bug-reports, and patches to improve it.
47 ! Charlie Zender <zender at uci dot edu>
48 ! Department of Earth System Science
49 ! University of California, Irvine
50 ! Irvine, CA 92697-3100
51 
52 ! These routines contain the functionality required to parse command-line arguments
53 ! http://www.winteracter.com/f2kcli/index.htm provides f2kcli module
54 ! required by some (non-UNIX) F9X compilers. F2K provides functions natively.
55 ! A standalone test program, sng.F90, is used to demonstrate sng_mdl.F90
56 
57 ! Recursive I/O:
58 ! Do not embed print statments in a function whose result is being printed
59 
60 ! NyL97 p. 318: POT, TIP, APT
61 ! For formatted I/O, characters are truncated or blanks are added,
62 ! depending on whether the field width is too small or too large.
63 ! For input, truncation occurs on the left, and blank padding on the right;
64 ! for output, truncation occurs on the right, and blak padding on the left
65 
66 ! Notes on PRC_DBL, auto-promotion, overloading:
67 ! Assume PRC_DBL is defined when compiler is passed autopromotion flags ("-r8")
68 ! This means all floats are promoted to double precision
69 ! As a result, ftn_arg_get_flt() is identical to ftn_arg_get_dbl() in precision
70 ! Thus compiler cannot disambiguate overloading these two functions
71 ! Result is OK on all known compilers except Lahey lf95 which complains
72 ! Solution is to define ftn_arg_get_flt() only when auto-promotion is not enabled
73 ! Alternate solution is never use auto-promotion with lf95
74 
75 ! Usage:
76 !use sng_mdl ! [mdl] String manipulation
77 
78 ! Set F2K token on compilers known to support Fortran 2000/2003 intrinsics
79 ! gfortran 4.1.2 does not recognize __GFORTRAN__ so use 4 instead
80 
81 module mod_sng ! [mdl] String manipulation
82  implicit none
83  public ! [stt] Symbols are public unless individually qualified as private
84  private::ftn_arg_get_dbl,ftn_arg_get_int,ftn_arg_get_lgc,ftn_arg_get_sng
85  private::ftn_arg_get_flt
86 
87  ! Overloaded command-line argument retrieval functions
88  interface ftn_arg_get
89  module procedure ftn_arg_get_dbl,ftn_arg_get_int,ftn_arg_get_lgc,ftn_arg_get_sng
90 
91  module procedure ftn_arg_get_flt
92  end interface ! ftn_arg_get
93 
94 contains
95 
96 
97  integer function ftn_strlen(sng) ! [nbr] Length of string
98  ! Purpose: Return length of string preceding first null character
99  ! Fortran intrinsic len(sng) is returned if string is not NUL-terminated
100  ! Prototype:
101  ! integer ftn_strlen ! [nbr] Length of string
102  implicit none
103  ! Input
104  character(len=*),intent(in)::sng
105  ! Local
106  ! Main Code
107  ! Normally, length of string is position of first insignificant character minus one
108  ftn_strlen=ftn_strfic(sng)-1 ! [idx] First NUL or 8-bit character position
109  ! String length may not exceed buffer size
110  if (ftn_strlen > len(sng)) ftn_strlen=len(sng)
111  ! String length may not be less than 0
112  ! Strings initialized to '' should have length 0 not -1
113  if (ftn_strlen < 0 .or. sng == '') ftn_strlen=0
114  return
115  end function ftn_strlen ! end ftn_strlen()
116 
117  integer function ftn_opt_lng_get(sng)
118  ! Purpose: Return length of option string preceding space or equals character
119  ! Any preceding dashes are NOT counted towards option length
120  ! Fortran intrinsic len(sng) is returned if string is not NUL-terminated
121  ! Prototype:
122  ! integer ftn_opt_lng_get ! [nbr] Length of option
123  ! Usage:
124  ! Call ftn_opt_lng_get with the full option string, e.g.,
125  ! ftn_opt_lng_get('--dbg_lvl=5') and ftn_opt_lng_get will return the
126  ! length of the option string
127  ! opt_sng=arg_val(3:2+ftn_opt_lng_get(arg_val)) ! [sng] Option string
128  ! if (opt_sng == 'dbg') then ...
129  implicit none
130  ! Input
131  character(len=*),intent(in)::sng
132  ! Local
133  integer idx
134  integer len_sng
135  integer::dsh_nbr=0 ! [nbr] Number of dashes
136  ! Main Code
137  ! Length of option string is one less than position of first space or equal sign
138  len_sng=len(sng)
139  if (len_sng >= 1) then
140  if (sng(1:1) == '-') dsh_nbr=1 ! [nbr] Number of dashes
141  endif
142  if (len_sng >= 2) then
143  if (sng(1:2) == '--') dsh_nbr=2 ! [nbr] Number of dashes
144  endif
145  do idx=1,len_sng
146  ! Look for first abnormal character
147  if (sng(idx:idx) == ' ' .or. sng(idx:idx) == '=') goto 100
148  end do ! end loop over characters
149 100 continue
150  ftn_opt_lng_get=idx-1-dsh_nbr
151  ! Strings initialized to '' should have length 0
152  if (ftn_opt_lng_get < 0 .or. sng == '') ftn_opt_lng_get=0
153  return
154  end function ftn_opt_lng_get ! end ftn_opt_lng_get()
155 
156  subroutine ftn_getarg_wrp( & ! [sbr] Call getarg() and increment arg_idx
157  arg_idx, & ! I/O [idx] Argument counter
158  arg_val) ! I/O [sng] String to copy into opt_val
159  ! Purpose: Wrapper for getarg() intrinsic that increments argument counter
160  ! Argument index is incremented after getarg() is called
161  ! Usage:
162  ! call ftn_getarg_wrp(arg_idx,arg_val) ! [sbr] Call getarg() and increment arg_idx
163  use mod_utils ! [mdl] Debugging constants, prg_nm, dbg_lvl
164  implicit none
165  ! Commons
166  ! Input
167  ! Input/Output
168  integer,intent(inout)::arg_idx ! I/O [idx] Argument counter
169  character(len=*),intent(inout)::arg_val ! I/O [sng] String to copy into opt_val
170  ! Output
171  ! Local
172  ! Main Code
173  call get_command_argument(arg_idx,arg_val)
174  if (dbg_lvl >= dbg_io) write (6,'(2a,i2,2a)') prg_nm(1:ftn_strlsc(prg_nm)), &
175  ': DEBUG ftn_getarg_wrp() reports arg_idx = ',arg_idx,', arg_val = ',arg_val(1:len_trim(arg_val))
176  ! Increment counter for next read
177  arg_idx=arg_idx+1
178  return
179  end subroutine ftn_getarg_wrp
180 
181  subroutine ftn_getarg_err( & ! [sbr] Error handler for getarg()
182  arg_idx, & ! I/O [idx] Argument counter
183  arg_val) ! I/O [sng] String to copy into opt_val
184  ! Purpose: Handle option-not-found errors for getopt()
185  ! Routine assumes arg_idx points to arg_val
186  ! Typically arg_idx is advanced before option recognition is attempted,
187  ! thus arg_idx may need to be decremented before ftn_getarg_err() is called
188  ! Usage:
189  ! call ftn_getarg_err(arg_idx,arg_val) ! [sbr] Error handler for getarg()
190  use mod_utils ! [mdl] Debugging constants, prg_nm, dbg_lvl
191  implicit none
192  ! Commons
193  ! Input
194  ! Input/Output
195  integer,intent(inout)::arg_idx ! I/O [idx] Argument counter
196  character(len=*),intent(inout)::arg_val ! I/O [sng] String to copy into opt_val
197  ! Output
198  ! Local
199  integer arg_nbr ! [nbr] Number of command-line arguments
200  ! integer exit_status ! [enm] Program exit status (non-standard Fortran)
201  ! Main Code
202  arg_nbr=command_argument_count() ! [nbr] Number of command-line arguments
203  write (6,'(2a)') prg_nm(1:ftn_strlsc(prg_nm)),': ERROR Option not recognized'
204  write (6,'(2a)') prg_nm(1:ftn_strlsc(prg_nm)),': HINT Option syntax is nearly POSIX/GNU compliant:'
205  write (6,'(2a)') prg_nm(1:ftn_strlsc(prg_nm)), &
206  ': Short option syntax is dash-key-space-value, e.g., -D 2'
207  write (6,'(3a)') prg_nm(1:ftn_strlsc(prg_nm)), &
208  ': Long option syntax is dash-dash-key-space-value, e.g., --dbg 2', &
209  ', or dash-dash-key-equal-value, e.g., --dbg=2 (preferred)'
210  ! Subtract 1 from arg_idx because rule is that arg_idx is incremented before option recognition is attempted
211  write (6,'(2a,i2,2a)') prg_nm(1:ftn_strlsc(prg_nm)), &
212  ': DEBUG ftn_getarg_err() reports arg_idx = ',arg_idx,', arg_val = ',arg_val(1:len_trim(arg_val))
213  if (arg_idx+1 <= arg_nbr) then
214  arg_idx=arg_idx+1 ! [idx] Counting index
215  call get_command_argument(arg_idx,arg_val)
216  write (6,'(2a,i2,2a)') prg_nm(1:ftn_strlsc(prg_nm)), &
217  ': DEBUG Next argument value is arg_val(',arg_idx,') = ',arg_val
218  else
219  write (6,'(2a)') prg_nm(1:ftn_strlsc(prg_nm)), &
220  ': DEBUG No arguments follow this one'
221  endif ! endif not last argument
222  ! exit_status=-1 ! [enm] Program exit status (non-standard Fortran)
223  ! call exit(exit_status) ! [enm] Exit with current exit status (non-standard Fortran)
224  stop 'Exit on error from ftn_getarg_err()'
225  return
226  end subroutine ftn_getarg_err
227 
228  ! Notes common to all arg_get() routines:
229  ! arg_val is assumed to be present as a command-line line option
230  ! On input, first two characters of arg_val are assumed to be dashes,
231  ! although this would not be too difficult to change.
232  ! Routine accepts options specified in POSIX format: "--opt_sng val" or "--opt_sng=val" (preferred)
233  ! Case 1: Option string and value are separated by space " " conjunction
234  ! On input, arg_val is assumed to hold option string with dashes, e.g., "--opt_sng"
235  ! On output, arg_val holds corresponding argument, e.g., "73"
236  ! Routine uses getarg() to obtain arg_val, then increments arg_idx
237  ! Case 2: Option string and value are separated by equals "=" conjunction
238  ! On input and output, arg_val holds entire option and value, e.g., "--opt_sng=73"
239  ! Routines do not call getarg() to change arg_val and do not increment arg_idx
240  ! Routines set optional logical argument opt_flg to true if called
241 
242  subroutine ftn_arg_get_dbl( & ! [sbr] Process double-valued command-line argument
243  arg_idx, & ! I/O [idx] Argument counter
244  arg_val, & ! I/O [sng] Double to copy into opt_val
245  opt_val, & ! O [frc] Variable to receive copy of arg_val
246  opt_flg) ! O [frc] Variable set by command-line
247  ! Purpose: Copy command-line double argument arg_val into variable opt_val
248  ! Usage:
249  ! call ftn_arg_get_dbl(arg_idx,arg_val,opt_val) ! [sbr] Process double-valued command-line argument
250  use mod_utils ! [mdl] Debugging constants, prg_nm, dbg_lvl
251  implicit none
252  ! Parameters
253  character(len=*),parameter::sbr_nm='ftn_arg_get_dbl' ! [sng] Subroutine name
254  ! Commons
255  ! Input
256  ! Input/Output
257  integer,intent(inout)::arg_idx ! I/O [idx] Argument counter
258  character(len=*),intent(inout)::arg_val ! I/O [sng] Double to copy into opt_val
259  ! Output
260  real(selected_real_kind(p=12)),intent(out)::opt_val ! O [frc] Variable to receive copy of arg_val
261  logical,optional,intent(out)::opt_flg ! O [flg] Variable set by command-line
262  ! Local
263  integer arg_lng ! [nbr] Length of argument
264  integer opt_lng ! [nbr] Length of option
265  integer arg_val_srt_idx ! [idx] Starting position of argument value
266  logical opt_cnj_is_spc ! [flg] Option conjunction is space character
267  ! Main Code
268  ! Print any diagnostics before current value of arg_val is overwritten
269  arg_lng=len_trim(arg_val) ! [nbr] Length of argument
270  opt_lng=ftn_opt_lng_get(arg_val) ! [nbr] Length of option
271  if (dbg_lvl >= dbg_io) then
272  write (6,'(2a,i2,3a,i2)') prg_nm(1:ftn_strlsc(prg_nm)), &
273  ': DEBUG '//sbr_nm//'() reports arg_idx = ',arg_idx, &
274  ', Full option = ''',arg_val(1:arg_lng),''', Length = ',arg_lng
275  endif ! endif dbg
276  ! Determine whether format is --opt_sng=val or --opt_sng val
277  ! Add two to cover preliminary dashes
278  if (opt_lng+2 < arg_lng) then
279  opt_cnj_is_spc=.false. ! [flg] Option conjunction is space character
280  arg_val_srt_idx=3+opt_lng+1 ! [idx] Starting position of argument value
281  if (dbg_lvl >= dbg_io) then
282  write (6,'(8a)') prg_nm(1:ftn_strlsc(prg_nm)), &
283  ': DEBUG '//sbr_nm//'() diassembles argument into Option = ''', &
284  arg_val(3:2+opt_lng),''', conjunction is ''', &
285  arg_val(3+opt_lng:3+opt_lng),''', argument = ''', &
286  arg_val(arg_val_srt_idx:arg_lng),'''' ! Quote syntax is mystical
287  endif ! endif dbg
288  else
289  opt_cnj_is_spc=.true. ! [flg] Option conjunction is space character
290  arg_val_srt_idx=1 ! [idx] Starting position of argument value
291  endif ! endif
292  if (opt_cnj_is_spc) then
293  ! Obtain value of option by getting next command-line argument
294  ! New arguments will alter input values of arg_idx,arg_val
295  call get_command_argument(arg_idx,arg_val)
296  ! Increment counter for next read
297  arg_idx=arg_idx+1
298  ! Sanity checks
299  arg_lng=len_trim(arg_val) ! [nbr] Length of argument
300  if (arg_lng <= 0) stop 'ftn_???_arg_get() reports option lacks argument'
301  endif ! endif opt_cnj_is_spc
302  ! Read argument into double and return
303  read (arg_val(arg_val_srt_idx:arg_lng),*) opt_val ! [frc] Variable to receive copy of arg_val
304  if (dbg_lvl >= dbg_io) then
305  write (6,'(2a,g13.6)') prg_nm(1:ftn_strlsc(prg_nm)), &
306  ': DEBUG '//sbr_nm//'() assigned argument value = ',opt_val
307  endif ! endif
308  if(present(opt_flg)) opt_flg=.true. ! [flg] Variable set by command-line
309  return
310  end subroutine ftn_arg_get_dbl
311 
312  subroutine ftn_arg_get_flt( & ! [sbr] Process float-valued command-line argument
313  arg_idx, & ! I/O [idx] Argument counter
314  arg_val, & ! I/O [sng] Float to copy into opt_val
315  opt_val, & ! O [frc] Variable to receive copy of arg_val
316  opt_flg) ! O [frc] Variable set by command-line
317  ! Purpose: Copy command-line float argument arg_val into variable opt_val
318  ! Usage:
319  ! call ftn_arg_get_flt(arg_idx,arg_val,opt_val) ! [sbr] Process float-valued command-line argument
320  use mod_utils ! [mdl] Debugging constants, prg_nm, dbg_lvl
321  implicit none
322  ! Parameters
323  character(len=*),parameter::sbr_nm='ftn_arg_get_flt' ! [sng] Subroutine name
324  ! Commons
325  ! Input
326  ! Input/Output
327  integer,intent(inout)::arg_idx ! I/O [idx] Argument counter
328  character(len=*),intent(inout)::arg_val ! I/O [sng] Float to copy into opt_val
329  ! Output
330  real(selected_real_kind(p=6)),intent(out)::opt_val ! O [frc] Variable to receive copy of arg_val
331  logical,optional,intent(out)::opt_flg ! O [flg] Variable set by command-line
332  ! Local
333  integer arg_lng ! [nbr] Length of argument
334  integer opt_lng ! [nbr] Length of option
335  integer arg_val_srt_idx ! [idx] Starting position of argument value
336  logical opt_cnj_is_spc ! [flg] Option conjunction is space character
337  ! Main Code
338  ! Print any diagnostics before current value of arg_val is overwritten
339  arg_lng=len_trim(arg_val) ! [nbr] Length of argument
340  opt_lng=ftn_opt_lng_get(arg_val) ! [nbr] Length of option
341  if (dbg_lvl >= dbg_io) then
342  write (6,'(2a,i2,3a,i2)') prg_nm(1:ftn_strlsc(prg_nm)), &
343  ': DEBUG '//sbr_nm//'() reports arg_idx = ',arg_idx, &
344  ', Full option = ''',arg_val(1:arg_lng),''', Length = ',arg_lng
345  endif ! endif dbg
346  ! Determine whether format is --opt_sng=val or --opt_sng val
347  ! Add two to cover preliminary dashes
348  if (opt_lng+2 < arg_lng) then
349  opt_cnj_is_spc=.false. ! [flg] Option conjunction is space character
350  arg_val_srt_idx=3+opt_lng+1 ! [idx] Starting position of argument value
351  if (dbg_lvl >= dbg_io) then
352  write (6,'(8a)') prg_nm(1:ftn_strlsc(prg_nm)), &
353  ': DEBUG '//sbr_nm//'() diassembles argument into Option = ''', &
354  arg_val(3:2+opt_lng),''', conjunction is ''', &
355  arg_val(3+opt_lng:3+opt_lng),''', argument = ''', &
356  arg_val(arg_val_srt_idx:arg_lng),'''' ! Quote syntax is mystical
357  endif ! endif dbg
358  else
359  opt_cnj_is_spc=.true. ! [flg] Option conjunction is space character
360  arg_val_srt_idx=1 ! [idx] Starting position of argument value
361  endif ! endif
362  if (opt_cnj_is_spc) then
363  ! Obtain value of option by getting next command-line argument
364  ! New arguments will alter input values of arg_idx,arg_val
365  call get_command_argument(arg_idx,arg_val)
366  ! Increment counter for next read
367  arg_idx=arg_idx+1
368  ! Sanity checks
369  arg_lng=len_trim(arg_val) ! [nbr] Length of argument
370  if (arg_lng <= 0) stop 'ftn_???_arg_get() reports option lacks argument'
371  endif ! endif opt_cnj_is_spc
372  ! Read argument into float and return
373  read (arg_val(arg_val_srt_idx:arg_lng),*) opt_val ! [frc] Variable to receive copy of arg_val
374  if (dbg_lvl >= dbg_io) then
375  write (6,'(2a,g13.6)') prg_nm(1:ftn_strlsc(prg_nm)), &
376  ': DEBUG '//sbr_nm//'() assigned argument value = ',opt_val
377  endif ! endif
378  if(present(opt_flg)) opt_flg=.true. ! [flg] Variable set by command-line
379  return
380  end subroutine ftn_arg_get_flt
381 
382  subroutine ftn_arg_get_int( & ! [sbr] Process integer-valued command-line argument
383  arg_idx, & ! I/O [idx] Argument counter
384  arg_val, & ! I/O [sng] Integer to copy into opt_val
385  opt_val, & ! O [nbr] Variable to receive copy of arg_val
386  opt_flg) ! O [frc] Variable set by command-line
387  ! Purpose: Copy command-line integer argument arg_val into variable opt_val
388  ! Usage:
389  ! call ftn_arg_get_int(arg_idx,arg_val,opt_val) ! [sbr] Process integer-valued command-line argument
390  use mod_utils ! [mdl] Debugging constants, prg_nm, dbg_lvl
391  implicit none
392  ! Parameters
393  character(len=*),parameter::sbr_nm='ftn_arg_get_int' ! [sng] Subroutine name
394  ! Commons
395  ! Input
396  ! Input/Output
397  integer,intent(inout)::arg_idx ! I/O [idx] Argument counter
398  character(len=*),intent(inout)::arg_val ! I/O [sng] Integer to copy into opt_val
399  ! 20030429: Change opt_val to inout since dbg_lvl itself may be an opt_val but is referenced on LHS
400  integer,intent(inout)::opt_val ! O [nbr] Variable to receive copy of arg_val
401  logical,optional,intent(out)::opt_flg ! O [flg] Variable set by command-line
402  ! Output
403  ! Local
404  integer arg_lng ! [nbr] Length of argument
405  integer opt_lng ! [nbr] Length of option
406  integer arg_val_srt_idx ! [idx] Starting position of argument value
407  logical opt_cnj_is_spc ! [flg] Option conjunction is space character
408  ! Main Code
409  ! Print any diagnostics before current value of arg_val is overwritten
410  arg_lng=len_trim(arg_val) ! [nbr] Length of argument
411  opt_lng=ftn_opt_lng_get(arg_val) ! [nbr] Length of option
412  if (dbg_lvl >= dbg_io) then
413  write (6,'(2a,i2,3a,i2)') prg_nm(1:ftn_strlsc(prg_nm)), &
414  ': DEBUG '//sbr_nm//'() reports arg_idx = ',arg_idx, &
415  ', Full option = ''',arg_val(1:arg_lng),''', Length = ',arg_lng
416  endif ! endif dbg
417  ! Determine whether format is --opt_sng=val or --opt_sng val
418  ! Add two to cover preliminary dashes
419  if (opt_lng+2 < arg_lng) then
420  opt_cnj_is_spc=.false. ! [flg] Option conjunction is space character
421  arg_val_srt_idx=3+opt_lng+1 ! [idx] Starting position of argument value
422  if (dbg_lvl >= dbg_io) then
423  write (6,'(8a)') prg_nm(1:ftn_strlsc(prg_nm)), &
424  ': DEBUG '//sbr_nm//'() diassembles argument into Option = ''', &
425  arg_val(3:2+opt_lng),''', conjunction is ''', &
426  arg_val(3+opt_lng:3+opt_lng),''', argument = ''', &
427  arg_val(arg_val_srt_idx:arg_lng),'''' ! Quote syntax is mystical
428  endif ! endif dbg
429  else
430  opt_cnj_is_spc=.true. ! [flg] Option conjunction is space character
431  arg_val_srt_idx=1 ! [idx] Starting position of argument value
432  endif ! endif
433  if (opt_cnj_is_spc) then
434  ! Obtain value of option by getting next command-line argument
435  ! New arguments will alter input values of arg_idx,arg_val
436  call get_command_argument(arg_idx,arg_val)
437  ! Increment counter for next read
438  arg_idx=arg_idx+1
439  ! Sanity checks
440  arg_lng=len_trim(arg_val) ! [nbr] Length of argument
441  if (arg_lng <= 0) stop 'ftn_arg_get_int() reports option lacks argument'
442  endif ! endif opt_cnj_is_spc
443  ! Read argument into integer and return
444  read (arg_val(arg_val_srt_idx:arg_lng),*) opt_val ! [nbr] Variable to receive copy of arg_val
445  if (dbg_lvl >= dbg_io) then
446  write (6,'(2a,g13.6)') prg_nm(1:ftn_strlsc(prg_nm)), &
447  ': DEBUG '//sbr_nm//'() assigned argument value = ',opt_val
448  endif ! endif
449  if(present(opt_flg)) opt_flg=.true. ! [flg] Variable set by command-line
450  return
451  end subroutine ftn_arg_get_int
452 
453  subroutine ftn_arg_get_lgc( & ! [sbr] Process logical-valued command-line argument
454  arg_idx, & ! I/O [idx] Argument counter
455  arg_val, & ! I/O [sng] Logical to copy into opt_val
456  opt_val, & ! O [flg] Variable to receive copy of arg_val
457  opt_flg) ! O [frc] Variable set by command-line
458  ! Purpose: Copy command-line logical argument arg_val into variable opt_val
459  ! Usage:
460  ! call ftn_arg_get_lgc(arg_idx,arg_val,opt_val) ! [sbr] Process logical-valued command-line argument
461  use mod_utils ! [mdl] Debugging constants, prg_nm, dbg_lvl
462  implicit none
463  ! Parameters
464  character(len=*),parameter::sbr_nm='ftn_arg_get_lgc' ! [sng] Subroutine name
465  ! Commons
466  ! Input
467  ! Input/Output
468  integer,intent(inout)::arg_idx ! I/O [idx] Argument counter
469  character(len=*),intent(inout)::arg_val ! I/O [sng] Logical to copy into opt_val
470  ! Output
471  logical,intent(out)::opt_val ! O [flg] Variable to receive copy of arg_val
472  logical,optional,intent(out)::opt_flg ! O [flg] Variable set by command-line
473  ! Local
474  integer arg_lng ! [nbr] Length of argument
475  integer opt_lng ! [nbr] Length of option
476  integer arg_val_srt_idx ! [idx] Starting position of argument value
477  logical opt_cnj_is_spc ! [flg] Option conjunction is space character
478  ! Main Code
479  ! Print any diagnostics before current value of arg_val is overwritten
480  arg_lng=len_trim(arg_val) ! [nbr] Length of argument
481  opt_lng=ftn_opt_lng_get(arg_val) ! [nbr] Length of option
482  if (dbg_lvl >= dbg_io) then
483  write (6,'(2a,i2,3a,i2)') prg_nm(1:ftn_strlsc(prg_nm)), &
484  ': DEBUG '//sbr_nm//'() reports arg_idx = ',arg_idx, &
485  ', Full option = ''',arg_val(1:arg_lng),''', Length = ',arg_lng
486  endif ! endif dbg
487  ! Determine whether format is --opt_sng=val or --opt_sng val
488  ! Add two to cover preliminary dashes
489  if (opt_lng+2 < arg_lng) then
490  opt_cnj_is_spc=.false. ! [flg] Option conjunction is space character
491  arg_val_srt_idx=3+opt_lng+1 ! [idx] Starting position of argument value
492  if (dbg_lvl >= dbg_io) then
493  write (6,'(8a)') prg_nm(1:ftn_strlsc(prg_nm)), &
494  ': DEBUG '//sbr_nm//'() diassembles argument into Option = ''', &
495  arg_val(3:2+opt_lng),''', conjunction is ''', &
496  arg_val(3+opt_lng:3+opt_lng),''', argument = ''', &
497  arg_val(arg_val_srt_idx:arg_lng),'''' ! Quote syntax is mystical
498  endif ! endif dbg
499  else
500  opt_cnj_is_spc=.true. ! [flg] Option conjunction is space character
501  arg_val_srt_idx=1 ! [idx] Starting position of argument value
502  endif ! endif
503  if (opt_cnj_is_spc) then
504  ! Obtain value of option by getting next command-line argument
505  ! New arguments will alter input values of arg_idx,arg_val
506  call get_command_argument(arg_idx,arg_val)
507  ! Increment counter for next read
508  arg_idx=arg_idx+1
509  ! Sanity checks
510  arg_lng=len_trim(arg_val) ! [nbr] Length of argument
511  if (arg_lng <= 0) stop 'ftn_arg_get_lgc() reports option lacks argument'
512  endif ! endif opt_cnj_is_spc
513  ! Read argument into integer and return
514  read (arg_val(arg_val_srt_idx:arg_lng),*) opt_val ! [flg] Variable to receive copy of arg_val
515  if (dbg_lvl >= dbg_io) then
516  write (6,'(2a,g13.6)') prg_nm(1:ftn_strlsc(prg_nm)), &
517  ': DEBUG '//sbr_nm//'() assigned argument value = ',opt_val
518  endif ! endif
519  if(present(opt_flg)) opt_flg=.true. ! [flg] Variable set by command-line
520  return
521  end subroutine ftn_arg_get_lgc
522 
523  subroutine ftn_arg_get_sng( & ! [sbr] Process string-valued command-line argument
524  arg_idx, & ! I/O [idx] Argument counter
525  arg_val, & ! I/O [sng] String to copy into opt_val
526  opt_val, & ! O [sng] Variable to receive copy of arg_val
527  opt_flg) ! O [frc] Variable set by command-line
528  ! Purpose: Copy command-line string argument arg_val into variable opt_val
529  ! opt_val is unchanged if arg_val cannot be copied
530  ! Remainder of opt_val is NUL-initialized
531  ! Usage:
532  ! call ftn_arg_get_sng(arg_idx,arg_val,opt_val) ! [sbr] Process string-valued command-line argument
533  use mod_utils ! [mdl] Debugging constants, prg_nm, dbg_lvl
534  implicit none
535  ! Parameters
536  character(len=*),parameter::sbr_nm='ftn_arg_get_sng' ! [sng] Subroutine name
537  ! Commons
538  ! Input
539  ! Input/Output
540  integer,intent(inout)::arg_idx ! I/O [idx] Argument counter
541  character(len=*),intent(inout)::arg_val ! I/O [sng] String to copy into opt_val
542  ! Output
543  character(len=*),intent(out)::opt_val ! O [sng] Variable to receive copy of arg_val
544  logical,optional,intent(out)::opt_flg ! O [flg] Variable set by command-line
545  ! Local
546  integer idx
547  integer len_var ! [nbr] Length of string variable
548  integer arg_lng ! [nbr] Length of argument
549  integer opt_lng ! [nbr] Length of option
550  integer arg_val_srt_idx ! [idx] Starting position of argument value
551  logical opt_cnj_is_spc ! [flg] Option conjunction is space character
552  integer nll_srt_idx ! [idx] First NUL position in opt_val
553  ! Main Code
554  ! Print any diagnostics before current value of arg_val is overwritten
555  len_var=len(opt_val) ! [nbr] Length of string variable
556  arg_lng=len_trim(arg_val) ! [nbr] Length of argument
557  opt_lng=ftn_opt_lng_get(arg_val) ! [nbr] Length of option
558  if (dbg_lvl >= dbg_io) then
559  write (6,'(2a,i2,3a,i2)') prg_nm(1:ftn_strlsc(prg_nm)), &
560  ': DEBUG '//sbr_nm//'() reports arg_idx = ',arg_idx, &
561  ', Option = ''',arg_val(1:arg_lng),''', Length = ',arg_lng
562  endif ! endif
563  ! Determine whether format is --opt_sng=val or --opt_sng val
564  ! Add two to cover preliminary dashes
565  if (opt_lng+2 < arg_lng) then
566  opt_cnj_is_spc=.false. ! [flg] Option conjunction is space character
567  arg_val_srt_idx=3+opt_lng+1 ! [idx] Starting position of argument value
568  if (dbg_lvl >= dbg_io) then
569  write (6,'(8a)') prg_nm(1:ftn_strlsc(prg_nm)), &
570  ': DEBUG '//sbr_nm//'() diassembles argument into Option = ''', &
571  arg_val(3:2+opt_lng),''', conjunction is ''', &
572  arg_val(3+opt_lng:3+opt_lng),''', argument = ''', &
573  arg_val(arg_val_srt_idx:arg_lng),'''' ! Quote syntax is mystical
574  endif ! endif dbg
575  else
576  opt_cnj_is_spc=.true. ! [flg] Option conjunction is space character
577  arg_val_srt_idx=1 ! [idx] Starting position of argument value
578  endif ! endif
579  if (opt_cnj_is_spc) then
580  ! Obtain value of option by getting next command-line argument
581  ! New arguments will alter input values of arg_idx,arg_val
582  call get_command_argument(arg_idx,arg_val)
583  ! Increment counter for next read
584  arg_idx=arg_idx+1
585  ! Sanity checks
586  arg_lng=len_trim(arg_val) ! [nbr] Length of argument
587  if (arg_lng <= 0) stop 'ftn_???_arg_get() reports option lacks argument'
588  if (arg_lng > len_var) then
589  write (6,'(2a,i5,a,i5)') prg_nm(1:ftn_strlsc(prg_nm)), &
590  ': ERROR '//sbr_nm//'() reports argument length = ',arg_lng, &
591  ' too long to fit into variable length ',len_var
592  stop
593  endif ! endif arg_lng > len_var
594  endif ! endif opt_cnj_is_spc
595  ! Read argument into string and return
596  read (arg_val(arg_val_srt_idx:arg_lng),'(a)') opt_val ! [sng] Variable to receive copy of arg_val
597  ! NUL-initialize remainder of opt_val
598  nll_srt_idx=arg_lng-arg_val_srt_idx+2 ! [idx] First NUL position in opt_val
599  do idx=nll_srt_idx,len_var
600  opt_val(idx:idx)=char(0) ! [sng] Variable to receive copy of arg_val
601  end do ! end loop over characters
602  if (dbg_lvl >= dbg_io) then
603  write (6,'(4a)') prg_nm(1:ftn_strlsc(prg_nm)), &
604  ': DEBUG '//sbr_nm//'() assigned argument value = ',opt_val(1:len_trim(opt_val)),'<--End of opt_val string'
605  call ftn_strprn(opt_val) ! [fnc] Print character values of string
606  endif ! endif
607  if(present(opt_flg)) opt_flg=.true. ! [flg] Variable set by command-line
608  return
609  end subroutine ftn_arg_get_sng
610 
611  integer function ftn_strstr(sng1,sng2) ! [idx] Location of sng2 in sng1
612  ! Purpose: Return the location of second string in the first
613  ! Fortran intrinsic len(sng) is returned if string is not NUL-terminated
614  ! Return -1 if sng1 does not contain sng1
615  ! Prototype:
616  ! integer ftn_strstr ! [idx] Location of sng1 in sng2
617  implicit none
618  ! Input
619  character(len=*),intent(in)::sng1
620  character(len=*),intent(in)::sng2
621  ! Local
622  integer lng1
623  integer lng2
624  integer idx1
625  integer idx2
626  ! Initialize
627  idx2=0 ! CEWI for lf95
628  ! Main Code
629  lng1=ftn_strlen(sng1) ! [nbr] Length of string
630  lng2=ftn_strlen(sng2) ! [nbr] Length of string
631  do idx1=1,lng1
632  if (sng1(idx1:idx1) == sng2(1:1)) then
633  do idx2=2,lng2
634  if (sng1(idx1+idx2-1:idx1+idx2-1) /= sng2(idx2:idx2)) goto 200
635  end do ! end loop over characters
636  goto 100
637  endif ! endif
638 200 continue
639  end do ! end loop over characters
640 100 continue
641  if (idx2 == lng2+1) then
642  ftn_strstr=idx1
643  else
644  ftn_strstr=-1
645  endif ! endif
646  return
647  end function ftn_strstr
648 
649  subroutine ftn_strini(sng) ! [sng] sng(1:len)=NUL
650  ! Purpose: Initialize all elements of a character array to char(0)
651  ! Usage:
652  ! call ftn_strini(sng) ! [sng] sng(1:len)=NUL
653  implicit none
654  ! Input/Output
655  character(len=*),intent(out)::sng ! I/O [sng] String to initialize
656  ! Local
657  integer idx
658  integer lng
659  ! Main Code
660  lng=len(sng)
661  do idx=1,lng
662  sng(idx:idx)=char(0)
663  end do ! end loop over characters
664  ! write (6,*) 'ftn_strini(): Initialized string of length',lng
665  return
666  end subroutine ftn_strini
667 
668  subroutine ftn_strnul(sng) ! [sbr] NUL-initialize all characters after LSC
669  ! Purpose: Change space characters to NUL characters in a string
670  ! Spaces after last significant character (LSC) in string are changed to NUL
671  ! Spaces before LSC are not affected
672  ! Usage:
673  ! call ftn_strnul(sng) ! [sbr] NUL-initialize all characters after LSC
674  implicit none
675  ! Input/Output
676  character(len=*),intent(inout)::sng ! I/O [sng] String to NUL-initialize
677  ! Local
678  integer idx
679  integer lng
680  integer lst_sgn_chr_idx
681  ! Main Code
682  lng=ftn_strlen(sng) ! [nbr] Length of string
683  lst_sgn_chr_idx=ftn_strlsc(sng)
684  do idx=lst_sgn_chr_idx+1,lng
685  if(sng(idx:idx) == ' ') sng(idx:idx)=char(0)
686  end do ! end loop over characters
687  return
688  end subroutine ftn_strnul
689 
690  integer function ftn_strlsc(sng)
691  ! Purpose: Return position of last significant character (LSC) in string
692  ! LSC is last character that is not a space or a NUL character
693  ! Routine is useful in determining effective lengths of strings which
694  ! may have been padded by compiler or shell.
695  ! Automatic padding is always done with spaces or NULs, so this routine
696  ! tells us how long the non-padded portion of the string is.
697  ! Routine will mis-handle user-defined strings that end in spaces
698  ! LSC is always <= len(sng)
699  ! Usage:
700  ! psn=ftn_strlsc(sng) ! [idx] Last significant character position
701  ! Prototype:
702  ! integer ftn_strlsc ! [idx] Last significant character position
703  implicit none
704  ! Input
705  character(len=*),intent(in)::sng
706  ! Local
707  integer idx
708  integer lng
709  ! Main Code
710  lng=len(sng)
711  do idx=lng,1,-1
712  ! Look for last normal character
713  if (sng(idx:idx) /= ' '.and.sng(idx:idx) /= char(0)) goto 100
714  end do ! end loop over characters
715 100 continue
716  ftn_strlsc=idx
717  return
718  end function ftn_strlsc
719 
720  integer function ftn_strfic(sng) ! [idx] First NUL or 8-bit character position
721  ! Purpose: Return position of first insignificant character (FIC) in string
722  ! FIC is first NUL character in string, or first 8-bit character, i.e., iachar > 127
723  ! FIC is len(sng)+1 if string has no NUL character and no iachar > 127
724  ! Usage:
725  ! psn=ftn_strfic(sng) ! [idx] First NUL or 8-bit character position
726  ! Prototype:
727  ! integer ftn_strfic ! [idx] First NUL or 8-bit character position
728  implicit none
729  ! Input
730  character(len=*),intent(in)::sng ! I [sng] String to examine
731  ! Local
732  integer idx
733  integer lng
734  ! Main Code
735  lng=len(sng)
736  do idx=1,lng
737  ! Look for first abnormal character
738  if (sng(idx:idx) == char(0) .or. iachar(sng(idx:idx)) > 127) goto 100
739  end do ! end loop over characters
740 100 continue
741  ftn_strfic=idx ! [idx] First NUL or 8-bit character position
742  return
743  end function ftn_strfic
744 
745  subroutine ftn_strprn(sng) ! [fnc] Print character values of string
746  ! Purpose: Print character values of string
747  ! Usage:
748  ! call ftn_strprn(sng) ! [fnc] Print character values of string
749  implicit none
750  ! Input
751  character(len=*),intent(in)::sng
752  ! Local
753  character(1) chr_crr ! [sng] Current character
754  integer idx
755  integer lng
756  integer iachr_crr ! [enm] Integer representing current character
757  ! Main Code
758  lng=len(sng)
759  do idx=1,lng
760  ! Print each normal character
761  chr_crr=sng(idx:idx) ! [sng] Current character
762  iachr_crr=iachar(chr_crr) ! [enm] Integer representing current character
763  if (idx < lng) then
764  if (iachr_crr == 0) then
765  write (6,'(a3,a3,i3,a2)',advance="no") 'NUL',' = ',iachr_crr,', '
766  else if (iachr_crr == 32) then
767  write (6,'(a5,a3,i3,a2)',advance="no") 'SPACE',' = ',iachr_crr,', '
768  else
769  write (6,'(a1,a3,i3,a2)',advance="no") chr_crr,' = ',iachr_crr,', '
770  endif ! endif
771  else
772  if (iachr_crr == 0) then
773  write (6,'(a3,a3,i3)') 'NUL',' = ',iachr_crr,', '
774  else if (iachr_crr == 32) then
775  write (6,'(a5,a3,i3)') 'SPACE',' = ',iachr_crr,', '
776  else
777  write (6,'(a1,a3,i3)') chr_crr,' = ',iachr_crr,', '
778  endif ! endif
779  endif ! endif
780  end do ! end loop over characters
781  return
782  end subroutine ftn_strprn
783 
784  subroutine ftn_strcpy(sng1,sng2)
785  ! Purpose: Copy sng2 into sng1
786  ! Space remaining at end of sng1 is NUL-initialized
787  ! This function works well at copying fixed strings into variables, e.g.,
788  ! Usage:
789  ! call ftn_strcpy(sng,'Hello World') ! [fnc] Copy sng2 into sng1, NUL-terminate unused space
790  use mod_utils ! [mdl] Debugging constants, prg_nm, dbg_lvl
791  implicit none
792  ! Commons
793  ! Input
794  character(len=*),intent(in)::sng2
795  ! Input/Output
796  character(len=*),intent(inout)::sng1
797  ! Local
798  integer len1
799  integer len2
800  integer lng1
801  integer lng2
802  ! Main Code
803  len1=len(sng1)
804  len2=len(sng2)
805  ! Initialize sng1 before diagnostic ftn_strlen(sng1) is evaluated
806  ! This handles situation when sng1 is large enough, but not-initialized in calling routine
807  ! Otherwise some compilers, e.g., lf95 in debug mode, may complain in ftn_strlen(sng1)->ftn_strfic()
808  ! when they are unable to determine string length
809  ! Initialize sng1 before copying sng2 into sng1 WLOG since ftn_strcpy() behavior
810  ! is intended to mimic C library strcpy() which NUL-terminates the copy, and thus
811  ! all material after the NUL may be assumed to be undefined, i.e., set to NUL.
812  ! This is not equivalent to calling ftn_strnul(sng1) after the copy since when sng1 is undefined
813  ! There could be corner cases where contents of undefined sng1 character immediately
814  ! following copy of sng2 is not NUL or 8-bit. Results are unpredicatable in that case.
815  ! Hence initialize sng1 to NUL before using ftn_strlen(sng1) and before copying sng2
816  call ftn_strini(sng1) ! [sng] sng(1:len)=NUL
817  lng1=ftn_strlen(sng1) ! [nbr] Length of string
818  lng2=ftn_strlen(sng2) ! [nbr] Length of string
819  if (len1 < lng2) then
820  write (6,'(a,a)') prg_nm(1:ftn_strlen(prg_nm)),': ERROR len1 < lng2 in ftn_strcpy()'
821  write (6,'(a,i3,a,i3,2a)') 'len1 = ',len1,', lng1 = ',lng1,', sng1 = ',sng1
822  write (6,'(a,i3,a,i3,2a)') 'len2 = ',len2,', lng2 = ',lng2,', sng2 = ',sng2
823  stop 'EXIT_FAILURE from ftn_strcpy()'
824  endif ! endif
825  sng1(1:lng2)=sng2(1:lng2)
826  return
827  end subroutine ftn_strcpy
828 
829  integer function ftn_strcmp(sng1,sng2) ! [fnc] Compare sng1 to sng2: -1,0,1 iff sng1 <,=,> sng2
830  ! Purpose: Compare sng2 to sng1
831  ! Returns an integer less than, equal to, or greater than
832  ! zero if sng1 is found, respectively, to be less than, to
833  ! match, or be greater than sng2.
834  ! Usage:
835  ! if(ftn_strcmp(sng1,"Hello World")==0) then ! [fnc] Compare sng1 to sng2: -1,0,1 iff sng1 <,=,> sng2
836  use mod_utils ! [mdl] Debugging constants, prg_nm, dbg_lvl
837  implicit none
838  ! Commons
839  ! Input
840  character(len=*),intent(in)::sng1
841  character(len=*),intent(in)::sng2
842  ! Input/Output
843  ! Local
844  integer idx ! [idx] Counting idx
845  integer len1
846  integer len2
847  integer lng1
848  integer lng2
849  integer lng_min
850  ! Main Code
851  len1=len(sng1)
852  len2=len(sng2)
853  lng1=ftn_strlen(sng1) ! [nbr] Length of string
854  lng2=ftn_strlen(sng2) ! [nbr] Length of string
855  if (.false.) then
856  ! If this debugging block is turned on then attempting to print,ftn_strcmp()
857  ! will cause a recursive I/O error at runtime
858  write (6,'(2a)') prg_nm(1:ftn_strlen(prg_nm)),': DEBUG Diagnostics from ftn_strcmp()'
859  write (6,'(2(a,i3),2a)') 'len1 = ',len1,', lng1 = ',lng1,', sng1 = ',sng1
860  write (6,'(2(a,i3),2a)') 'len2 = ',len2,', lng2 = ',lng2,', sng2 = ',sng2
861  call ftn_strprn(sng1) ! [fnc] Print character values of string
862  call ftn_strprn(sng2) ! [fnc] Print character values of string
863  endif ! endif
864  lng_min=min(lng1,lng2)
865  do idx=1,lng_min
866  ! Prevent memory overruns
867  ! Characters are equal, proceed to next character
868  if (iachar(sng1(idx:idx)) == iachar(sng2(idx:idx))) cycle ! Goto next iteration
869  ! Characters are unequal, determine relationship
870  if (iachar(sng1(idx:idx)) < iachar(sng2(idx:idx))) then
871  ftn_strcmp=-1 ! [fnc] Compare sng1 to sng2: -1,0,1 iff sng1 <,=,> sng2
872  else ! (iachar(sng1(idx:idx)) > iachar(sng2(idx:idx)))
873  ftn_strcmp=1 ! [fnc] Compare sng1 to sng2: -1,0,1 iff sng1 <,=,> sng2
874  endif ! endif
875 
876  ! Characters were unequal, exit with appropriate value
877  goto 100 ! Exit Branch
878  end do
879 
880  if (lng1 == lng2) then
881  ! All significant values of both strings were equal in all positions
882  ! Thus the strings are equal by our criterion
883  ftn_strcmp=0 ! [fnc] Compare sng1 to sng2: -1,0,1 iff sng1 <,=,> sng2
884  else
885  ! All significant values of both strings were equal in all mutually legal positions
886  ! However, one string has more significant characters than the other
887  ! Thus return value depends on which string is longer
888  if (lng1 > lng2) then
889  ftn_strcmp=1 ! [fnc] Compare sng1 to sng2: -1,0,1 iff sng1 <,=,> sng2
890  else
891  ftn_strcmp=-1 ! [fnc] Compare sng1 to sng2: -1,0,1 iff sng1 <,=,> sng2
892  endif ! endif
893  endif ! endif lng1 == lng2
894 
895 100 continue ! Exit branch for unequal values inside loop
896  return
897  end function ftn_strcmp
898 
899  subroutine ftn_strcpylsc(sng1,sng2)
900  ! Purpose: Copy sng2 into sng1
901  ! Space remaining at end of sng1 is NUL-initialized
902  ! This function works well at copying fixed strings into variables, e.g.,
903  ! Usage:
904  ! call ftn_strcpylsc(sng,'Hello World') ! [fnc] Copy up to LSC of sng2 into sng1, NUL-initialize unused space
905  use mod_utils ! [mdl] Debugging constants, prg_nm, dbg_lvl
906  implicit none
907  ! Commons
908  ! Input
909  character(len=*),intent(in)::sng2
910  ! Input/Output
911  character(len=*),intent(inout)::sng1
912  ! Local
913  integer idx ! [idx] Counting idx
914  integer len1
915  integer len2
916  integer lsc1
917  integer lsc2
918  ! Main Code
919  len1=len(sng1)
920  len2=len(sng2)
921  lsc1=ftn_strlsc(sng1)
922  lsc2=ftn_strlsc(sng2)
923  if (len1 < lsc2) then
924  write (6,'(a,a)') prg_nm(1:ftn_strlsc(prg_nm)),': ERROR len1 < lsc2 in ftn_strcpylsc()'
925  write (6,'(a,i3,a,i3,2a)') 'len1 = ',len1,', lsc1 = ',lsc1,', sng1 = ',sng1
926  write (6,'(a,i3,a,i3,2a)') 'len2 = ',len2,', lsc2 = ',lsc2,', sng2 = ',sng2
927  stop 'EXIT_FAILURE from ftn_strcpylsc()'
928  endif ! endif
929  sng1(1:lsc2)=sng2(1:lsc2)
930  ! NUL-initialize rest of string
931  do idx=lsc2+1,len1
932  sng1(idx:idx)=char(0)
933  end do ! end loop over characters
934  return
935  end subroutine ftn_strcpylsc
936 
937  subroutine ftn_strcat( & ! [fnc] sng1 := sng1 // sng2
938  sng1, & ! I/O [sng] String to affix second string to
939  sng2) ! I [sng] String to affix to first string
940  ! Purpose: sng1 := sng1 // sng2
941  ! The case where sng1=sng2 is handled correctly
942  ! Usage:
943  ! call ftn_strcat(sng1,sng2) ! [fnc] sng1 := sng1 // sng2
944  use mod_utils ! [mdl] Debugging constants, prg_nm, dbg_lvl
945  implicit none
946  ! Commons
947  ! Input
948  character(len=*),intent(in)::sng2 ! I [sng] String to affix to first string
949  ! Input/Output
950  character(len=*),intent(inout)::sng1 ! I/O [sng] String to affix second string to
951  ! Local
952  integer len1
953  integer len2
954  integer lng1
955  integer lng2
956  ! Main Code
957  len1=len(sng1)
958  len2=len(sng2)
959  lng1=ftn_strlen(sng1) ! [nbr] Length of string
960  lng2=ftn_strlen(sng2) ! [nbr] Length of string
961  if (lng1+lng2 >= len1) then
962  write (6,'(a,a)') prg_nm(1:ftn_strlen(prg_nm)),': ERROR lng1+lng2 >= len1 in ftn_strcat()'
963  write (6,'(a,i3,a,i3,2a)') 'len1 = ',len1,', lng1 = ',lng1,', sng1 = ',sng1
964  write (6,'(a,i3,a,i3,2a)') 'len2 = ',len2,', lng2 = ',lng2,', sng2 = ',sng2
965  stop 'EXIT_FAILURE from ftn_strcat()'
966  endif ! endif
967  sng1(lng1+1:lng1+lng2)=sng2(1:lng2) ! I/O [sng] String to affix second string to
968  return
969  end subroutine ftn_strcat
970 
971  subroutine ftn_strpfx(sng1,sng2) ! [sbr] sng2 := sng1 // sng2
972  ! Purpose: sng2 := sng1 // sng2
973  ! Differences with ftn_strcat():
974  ! 1. Result is stored in second string not first string
975  ! 2. Strings are trimmed before concatenation
976  ! The case where sng1=sng2 is handled correctly
977  ! Usage:
978  ! call ftn_strpfx(sng1,sng2) ! [sbr] sng2 := sng1 // sng2
979  use mod_utils ! [mdl] Debugging constants, prg_nm, dbg_lvl
980  implicit none
981  ! Commons
982  ! Input
983  character(len=*),intent(in)::sng1
984  ! Output
985  character(len=*),intent(inout)::sng2
986  ! Local
987  integer len1
988  integer len2
989  integer len_trim1
990  integer len_trim2
991  integer lng1
992  integer lng2
993  integer lsc1
994  integer lsc2
995  ! Main Code
996  len1=len(sng1)
997  len2=len(sng2)
998  lng1=ftn_strlen(sng1) ! [nbr] Length of string
999  lng2=ftn_strlen(sng2) ! [nbr] Length of string
1000  lsc1=ftn_strlsc(sng1)
1001  lsc2=ftn_strlsc(sng2)
1002  len_trim1=len_trim(sng1)
1003  len_trim2=len_trim(sng2)
1004  if (lsc1+lsc2 >= len2) then
1005  write (6,'(a,a)') prg_nm(1:ftn_strlen(prg_nm)),': ERROR lsc1+lsc2 >= len2 in ftn_strpfx()'
1006  write (6,'(4(a,i3),2a)') 'len1 = ',len1,', len_trim1 = ',len_trim1,', lng1 = ',lng1,', sng1 = ',sng1
1007  write (6,'(4(a,i3),2a)') 'len2 = ',len2,', len_trim2 = ',len_trim2,', lng2 = ',lng2,', sng2 = ',sng2
1008  call ftn_strprn(sng1)
1009  call ftn_strprn(sng2)
1010  stop 'EXIT_FAILURE from ftn_strpfx()'
1011  endif ! endif
1012  ! Order is important
1013  sng2(lsc1+1:lsc1+lsc2)=sng2(1:lsc2)
1014  sng2(1:lsc1)=sng1(1:lsc1)
1015  return
1016  end subroutine ftn_strpfx
1017 
1018  subroutine ftn_drcpfx( & ! [sbr] fl_nm := drc/fl_nm
1019  drc, & ! I [sng] Directory to prepend
1020  fl_nm) ! I/O [sng] File name
1021  ! Purpose: fl_nm := drc/fl_nm, more or less
1022  ! Differences with ftn_strpfx():
1023  ! 1. Result is stored in second string not first string
1024  ! 2. Strings are trimmed before concatenation
1025  ! 3. Filenames containing slashes are unaltered
1026  ! Usage:
1027  ! call ftn_drcpfx(drc,fl_nm) ! [sbr] fl_nm := drc/fl_nm
1028  use mod_utils ! [mdl] Debugging constants, prg_nm, dbg_lvl
1029  implicit none
1030  ! Commons
1031  ! Input
1032  character(len=*),intent(in)::drc ! I [sng] Directory to prepend
1033  ! Input/Output
1034  character(len=*),intent(inout)::fl_nm ! I/O [sbr] File name
1035  ! Local
1036  integer len_drc
1037  integer len_fl
1038  integer lng_drc
1039  integer lng_fl
1040  ! Main Code
1041  len_drc=len(drc)
1042  len_fl=len(fl_nm)
1043  lng_drc=ftn_strlen(drc) ! [nbr] Length of string
1044  lng_fl=ftn_strlen(fl_nm) ! [nbr] Length of string
1045  if (lng_drc+lng_fl >= len_fl) then
1046  write (6,'(a,a)') prg_nm(1:ftn_strlen(prg_nm)),': ERROR lng_drc+lng_fl >= len_fl in ftn_drcpfx()'
1047  write (6,'(2(a,i3),2a)') 'len_drc = ',len_drc,', lng_drc = ',lng_drc,', drc = ',drc
1048  write (6,'(2(a,i3),2a)') 'len_fl = ',len_fl,', lng_fl = ',lng_fl,', fl_nm = ',fl_nm
1049  call ftn_strprn(drc)
1050  call ftn_strprn(fl_nm)
1051  stop 'EXIT_FAILURE from ftn_drcpfx()'
1052  endif ! endif
1053 
1054  if ( & ! Conditions for prepending fl_nm with drc:
1055  lng_drc > 0 .and. & ! Directory must be non-NUL
1056  ftn_strstr(fl_nm,'/') == -1 & ! fl_nm does not look like directory itself
1057  ) then
1058  ! If directory does not have trailing slash...
1059  if (drc(lng_drc:lng_drc) /= '/') then ! Directory does not have trailing slash
1060  ! ...and if room exists for one...
1061  if (lng_drc+lng_fl+1 < len_fl) then
1062  ! ...then add one
1063  ! Order is important
1064  fl_nm(lng_drc+2:lng_drc+2+lng_fl)=fl_nm(1:lng_fl)
1065  fl_nm(1:lng_drc)=drc(1:lng_drc)
1066  fl_nm(lng_drc+1:lng_drc+1)='/'
1067  endif ! endif adding trailing slash
1068  else ! Directory does have trailing slash
1069  fl_nm(lng_drc+1:lng_drc+1+lng_fl)=fl_nm(1:lng_fl)
1070  fl_nm(1:lng_drc)=drc(1:lng_drc)
1071  endif ! Directory does have trailing slash
1072  endif ! endif prepending drc
1073  call ftn_strnul(fl_nm) ! [sbr] NUL-initialize all characters after LSC
1074 
1075  return
1076  end subroutine ftn_drcpfx
1077 
1078  subroutine ftn_prg_id_mk(CVS_Id,CVS_Revision,CVS_Date,prg_ID)
1079  ! Purpose: Create identity string from CVS input
1080  use mod_utils ! [mdl] Debugging constants, prg_nm, dbg_lvl
1081  implicit none
1082  ! Commons
1083  ! Parameter
1084  integer,parameter::CVS_kk=0
1085  integer,parameter::CVS_kv=1
1086  integer,parameter::CVS_kkv=2
1087  ! Input
1088  character(len=*),intent(in)::CVS_Id
1089  character(len=*),intent(in)::CVS_Revision
1090  character(len=*),intent(in)::CVS_Date
1091  ! Output
1092  character(len=*),intent(out)::prg_ID
1093  ! Local
1094  integer CVS_typ
1095  integer Date_ptr
1096  integer slash_ptr
1097  integer prg_ptr
1098  integer vrs_ptr
1099  ! Main Code
1100  ! Decide how CVS keywords were expanded
1101  date_ptr=ftn_strstr(cvs_date,'Date')
1102  if (date_ptr < 0) then
1103  ! String 'Date' was not found---CVS expansion must be -kv
1104  cvs_typ=cvs_kv
1105  else
1106  ! String 'Date' was found---CVS expansion is -kk or -kkv
1107  slash_ptr=ftn_strstr(cvs_date,'/')
1108  if (slash_ptr > 0) then
1109  cvs_typ=cvs_kkv
1110  else
1111  cvs_typ=cvs_kk
1112  endif ! endif
1113  endif ! endif
1114 
1115  if (.false.) then
1116  write (6,'(a,a)') prg_nm(1:ftn_strlen(prg_nm)),': DEBUG ftn_prg_ID_mk() reports CVS strings:'
1117  write (6,'(a,a)') 'CVS_Id: ',cvs_id
1118  write (6,'(a,a)') 'CVS_Revision: ',cvs_revision
1119  write (6,'(a,a)') 'CVS_Date: ',cvs_date
1120  write (6,'(a,i1)') 'CVS_typ: ',cvs_typ
1121  write (6,'(a,i2)') 'Date_ptr: ',date_ptr
1122  write (6,'(a,i2)') 'slash_ptr: ',slash_ptr
1123  endif
1124 
1125  ! CVS_Revision and CVS_Date are parameters---do not attempt to write them
1126  ! call ftn_strnul(CVS_Revision) ! [sbr] NUL-initialize all characters after LSC
1127  ! call ftn_strnul(CVS_Date) ! [sbr] NUL-initialize all characters after LSC
1128  call ftn_strini(prg_id) ! [sng] sng(1:len)=NUL
1129  if (cvs_typ == cvs_kk) then
1130  prg_id='Source file unknown Version unknown Date unknown' // char(0)
1131  else
1132  prg_ptr=ftn_strstr(cvs_id,',v') ! ',v' is right after program name
1133  if (prg_ptr < 0) stop 'ERROR: ftn_prg_ID_mk() unable to find source code name'
1134  if (cvs_typ == cvs_kv) then
1135  prg_id=cvs_id(1:prg_ptr-1) // &
1136  ' version ' // cvs_revision(1:ftn_strlen(cvs_revision)) // &
1137  ' dated ' // cvs_date(1:ftn_strlen(cvs_date)) // ' GMT' // char(0)
1138  else if (cvs_typ == cvs_kkv) then
1139  vrs_ptr=ftn_strstr(cvs_revision,'ion:') ! 'ion:' should appear two characters before version
1140  if (vrs_ptr < 0) stop 'ERROR: ftn_prg_ID_mk() unable to find revision number'
1141  prg_id=cvs_id(6:prg_ptr-1) // &
1142  ' version ' // cvs_revision(vrs_ptr+5:ftn_strlen(cvs_revision)-2) // &
1143  ' dated ' // cvs_date(8:26) // ' GMT' // char(0)
1144  else
1145  write (6,'(a,a)') prg_nm(1:ftn_strlen(prg_nm)),': ERROR ftn_prg_ID_mk() reports unknown CVS_typ'
1146  endif ! endif
1147  endif ! endif
1148  return
1149  end subroutine ftn_prg_id_mk
1150 
1151  subroutine ftn_cmd_ln_sng(cmd_ln) ! [sng] Re-construct command-line into single string
1152  ! Purpose: Return a copy of command-line and initialize program name
1153  ! See http://www.winteracter.com/f2kcli/index.htm for alternatives
1154  ! Usage:
1155  ! call ftn_cmd_ln_sng(cmd_ln) ! [sng] command-line
1156  use mod_utils ! [mdl] Debugging constants, prg_nm, dbg_lvl
1157  implicit none
1158  ! Commons
1159  ! Output
1160  character(len=*),intent(out)::cmd_ln ! O [sng] command-line
1161  ! Local
1162  character(len=80) arg_val ! [sng] command-line argument value
1163  integer arg_idx ! [idx] Counting index
1164  integer arg_nbr ! [nbr] Number of command-line arguments
1165  integer cmd_ln_len ! [nbr] Length of command-line
1166  integer cmd_ln_lng ! [nbr] Length of command-line
1167  ! Main Code
1168  ! Initialize defaults
1169  call ftn_strini(cmd_ln) ! [sng] sng(1:len)=NUL
1170  call ftn_strini(prg_nm) ! [sng] sng(1:len)=NUL
1171  cmd_ln_len=len(cmd_ln) ! [nbr] Length of command-line
1172  cmd_ln_lng=0 ! [nbr] Length of command-line CEWI
1173 
1174  arg_nbr=command_argument_count() ! [nbr] Number of command-line arguments
1175  if (dbg_lvl >= dbg_vec) then
1176  write (6,'(a,i2)') 'ftn_cmd_ln_sng(): arg_nbr = ',arg_nbr
1177  endif ! endif dbg
1178 
1179  ! Loop over arguments
1180  do arg_idx=0,arg_nbr ! NB: Loop starts with 0
1181  ! Argument 0 is program name
1182  ! if (arg_idx > 0) call ftn_strcat(cmd_ln,' ')
1183  ! Insert space between arguments
1184  if (arg_idx > 0) cmd_ln(cmd_ln_lng+1:cmd_ln_lng+1)=' '
1185  call ftn_strini(arg_val) ! [sng] sng(1:len)=NUL
1186  call get_command_argument(arg_idx,arg_val)
1187  call ftn_strnul(arg_val) ! [sbr] NUL-initialize all characters after LSC
1188  call ftn_strcat(cmd_ln,arg_val)
1189  if (arg_idx == 0) call ftn_strcpy(prg_nm,arg_val) ! [sng] Program name
1190  cmd_ln_lng=ftn_strlen(cmd_ln) ! [nbr] Length of string
1191  if (cmd_ln_lng > cmd_ln_len) stop 'cmd_ln_lng > cmd_ln_len in ftn_cmd_ln_sng()'
1192  if (dbg_lvl >= dbg_vrb) then
1193  write (6,'(a,i3)') 'arg_idx = ',arg_idx
1194  write (6,'(a,a)') 'arg_val = ',arg_val
1195  write (6,'(a,i3)') 'len(arg_val) = ',len(arg_val)
1196  write (6,'(a,i3)') 'ftn_strlen(arg_val) = ',ftn_strlen(arg_val)
1197  write (6,'(a,i3)') 'ftn_strlen(cmd_ln) = ',ftn_strlen(cmd_ln)
1198  write (6,'(a,a)') 'cmd_ln = ',cmd_ln
1199  endif ! endif dbg
1200  end do ! end loop over arg
1201 
1202  if (dbg_lvl >= dbg_io) write (6,'(a,a)') 'cmd_ln = ',cmd_ln(1:ftn_strlen(cmd_ln))
1203  return
1204  end subroutine ftn_cmd_ln_sng
1205 
1206  character(len=10) function ftn_date2sng(idate)
1207  ! Purpose: Convert integer date in YYYYMMDD to a character array in the form
1208  ! 'YYYY-MM-DD'
1209  ! Author: Brian Eaton charutl.F:idate2char()
1210  ! Recoded: 19990901 Charlie Zender
1211  ! Usage:
1212  ! character(len=10) ftn_date2sng ! [sng] Convert YYYYMMDD integer to YYYY-MM-DD string
1213  implicit none
1214  ! Input
1215  integer,intent(in)::idate
1216  ! Local
1217  integer yy
1218  integer mm
1219  integer dd
1220  character dash
1221  ! Main code
1222  dash='-'
1223  ! Extract year, month, and day from date
1224  yy=idate/10000
1225  mm=mod(abs(idate),10000)/100
1226  dd=mod(abs(idate),100)
1227  write(ftn_date2sng,'(i4.4,a1,i2.2,a1,i2.2)') yy,dash,mm,dash,dd
1228  return
1229  end function ftn_date2sng
1230 
1231  character(len=8) function ftn_sec2sng(isec)
1232  ! Purpose: Convert integer seconds to a character array in the form 'HH:MM:SS'
1233  ! Author: Brian Eaton charutl.F:isec2char()
1234  ! Recoded: 19990901 Charlie Zender
1235  ! character(len=8) ftn_sec2sng ! [sng] Convert integer to 'HH:MM:SS' string
1236  implicit none
1237  ! Input
1238  integer,intent(in)::isec
1239  ! Local
1240  integer hh
1241  integer mm
1242  integer ss
1243  character colon
1244  ! Main Code
1245  colon=':'
1246  ! Extract hour, minutes, seconds
1247  hh=isec/3600
1248  mm=mod(isec,3600)/60
1249  ss=mod(isec,60)
1250  write(ftn_sec2sng,'(i2.2,a1,i2.2,a1,i2.2)') hh,colon,mm,colon,ss
1251  return
1252  end function ftn_sec2sng
1253 
1254 end module mod_sng ! [mdl] String manipulation
integer function ftn_strfic(sng)
Definition: mod_sng.f90:721
subroutine ftn_getarg_wrp(arg_idx, arg_val)
Definition: mod_sng.f90:159
subroutine ftn_strcpy(sng1, sng2)
Definition: mod_sng.f90:785
subroutine ftn_getarg_err(arg_idx, arg_val)
Definition: mod_sng.f90:184
subroutine ftn_cmd_ln_sng(cmd_ln)
Definition: mod_sng.f90:1152
integer function ftn_strcmp(sng1, sng2)
Definition: mod_sng.f90:830
integer dbg_lvl
Definition: mod_utils.f90:75
integer, parameter dbg_vrb
Definition: mod_utils.f90:72
subroutine ftn_prg_id_mk(CVS_Id, CVS_Revision, CVS_Date, prg_ID)
Definition: mod_sng.f90:1079
subroutine ftn_strpfx(sng1, sng2)
Definition: mod_sng.f90:972
integer function ftn_strlen(sng)
Definition: mod_sng.f90:98
integer function ftn_opt_lng_get(sng)
Definition: mod_sng.f90:118
subroutine ftn_strcat(sng1, sng2)
Definition: mod_sng.f90:940
character(len=80) prg_nm
Definition: mod_utils.f90:74
subroutine ftn_strini(sng)
Definition: mod_sng.f90:650
subroutine ftn_strnul(sng)
Definition: mod_sng.f90:669
subroutine ftn_drcpfx(drc, fl_nm)
Definition: mod_sng.f90:1021
subroutine ftn_strprn(sng)
Definition: mod_sng.f90:746
character(len=8) function ftn_sec2sng(isec)
Definition: mod_sng.f90:1232
subroutine ftn_strcpylsc(sng1, sng2)
Definition: mod_sng.f90:900
integer, parameter dbg_io
Definition: mod_utils.f90:66
integer function ftn_strstr(sng1, sng2)
Definition: mod_sng.f90:612
integer function ftn_strlsc(sng)
Definition: mod_sng.f90:691
character(len=10) function ftn_date2sng(idate)
Definition: mod_sng.f90:1207
integer, parameter dbg_vec
Definition: mod_utils.f90:71