File: options.f90

package info (click to toggle)
wsjtx 2.0.0%2Brepack-2
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 192,624 kB
  • sloc: cpp: 1,071,838; ansic: 60,751; f90: 25,266; python: 20,318; sh: 10,636; xml: 8,148; cs: 2,121; fortran: 2,051; yacc: 472; asm: 353; makefile: 316; perl: 19
file content (337 lines) | stat: -rw-r--r-- 10,320 bytes parent folder | download | duplicates (13)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
module options
  !
  ! Source code copied from:
  ! http://fortranwiki.org/fortran/show/Command-line+arguments
  !
  implicit none

  type option
     !> Long name.
     character(len=100) :: name
     !> Does the option require an argument?
     logical :: has_arg
     !> Corresponding short name.
     character :: chr
     !> Description.
     character(len=500) :: descr
     !> Argument name, if required.
     character(len=20) :: argname
   contains
     procedure :: print => print_opt
  end type option

contains

  !> Parse command line options. Options and their arguments must come before
  !> all non-option arguments. Short options have the form "-X", long options
  !> have the form "--XXXX..." where "X" is any character. Parsing can be
  !> stopped with the option '--'.
  !> The following code snippet illustrates the intended use:
  !> \code
  !> do
  !>   call getopt (..., optchar=c, ...)
  !>   if (stat /= 0) then
  !>     ! optional error handling
  !>     exit
  !>   end if
  !>   select case (c)
  !>     ! process options
  !>   end select
  !> end do
  !> \endcode
  subroutine getopt (options, longopts, optchar, optarg, arglen, stat, &
       offset, remain, err)
    use iso_fortran_env, only: error_unit

    !> String containing the characters that are valid short options. If
    !> present, command line arguments are scanned for those options.
    !> If a character is followed by a colon (:) its corresponding option
    !> requires an argument. E.g. "vn:" defines two options -v and -n with -n
    !> requiring an argument.
    character(len=*), intent(in), optional :: options

    !> Array of long options. If present, options of the form '--XXXX...' are
    !> recognised. Each option has an associated option character. This can be
    !> any character of default kind, it is just an identifier. It can, but
    !> doesn't have to, match any character in the options argument. In fact it
    !> is possible to only pass long options and no short options at all.
    !> Only name, has_arg and chr need to be set.
    type(option), intent(in), optional :: longopts(:)

    !> If stat is not 1, optchar contains the option character that was parsed.
    !> Otherwise its value is undefined.
    character, intent(out), optional :: optchar

    !> If stat is 0 and the parsed option requires an argument, optarg contains
    !> the first len(optarg) (but at most 500) characters of that argument.
    !> Otherwise its value is undefined. If the arguments length exceeds 500
    !> characters and err is .true., a warning is issued.
    character(len=*), intent(out), optional :: optarg

    !> If stat is 0 and the parsed option requires an argument, arglen contains
    !> the actual length of that argument. Otherwise its value is undefined.
    !> This can be used to make sure the argument was not truncated by the
    !> limited length of optarg.
    integer, intent(out), optional :: arglen

    !> Status indicator. Can have the following values:
    !>   -  0: An option was successfully parsed.
    !>   -  1: Parsing stopped successfully because a non-option or '--' was
    !>         encountered.
    !>   - -1: An unrecognised option was encountered.
    !>   - -2: A required argument was missing.
    !>   .
    !> Its value is never undefined.
    integer, intent(out), optional :: stat

    !> If stat is 1, offset contains the number of the argument before the
    !> first non-option argument, i.e. offset+n is the nth non-option argument.
    !> If stat is not 1, offset contains the number of the argument that would
    !> be parsed in the next call to getopt. This number can be greater than
    !> the actual number of arguments.
    integer, intent(out), optional :: offset

    !> If stat is 1, remain contains the number of remaining non-option
    !> arguments, i.e. the non-option arguments are in the range 
    !> (offset+1:offset+remain). If stat is not 1, remain is undefined.
    integer, intent(out), optional :: remain

    !> If err is present and .true., getopt prints messages to the standard
    !> error unit if an error is encountered (i.e. whenever stat would be set
    !> to a negative value).
    logical, intent(in), optional :: err

    integer, save :: pos = 1, cnt = 0
    character(len=500), save :: arg

    integer :: chrpos, length, st, id = 0
    character :: chr
    logical :: long

    if (cnt == 0) cnt = command_argument_count()
    long = .false.

    ! no more arguments left
    if (pos > cnt) then
       pos = pos - 1
       st = 1
       goto 10
    end if

    call get_command_argument (pos, arg, length)

    ! is argument an option?
    if (arg(1:1) == '-') then

       chr = arg(2:2)

       ! too long ('-xxxx...') for one dash?
       if (chr /= '-' .and. len_trim(arg) > 2) then
          st = -1
          goto 10
       end if

       ! forced stop ('--')
       if (chr == '-' .and. arg(3:3) == ' ') then
          st = 1
          goto 10
       end if

       ! long option ('--xxx...')
       if (chr == '-') then

          long = .true.

          ! check if valid
          id = lookup(arg(3:))

          ! option is invalid, stop
          if (id == 0) then
             st = -1
             goto 10
          end if

          chr = longopts(id)%chr

          ! check if option requires an argument
          if (.not. longopts(id)%has_arg) then
             st = 0
             goto 10
          end if

          ! check if there are still arguments left
          if (pos == cnt) then
             st = -2
             goto 10
          end if

          ! go to next position
          pos = pos + 1

          ! get argument
          call get_command_argument (pos, arg, length)

          ! make sure it is not an option
          if (arg(1:1) == '-') then
             st = -2
             pos = pos - 1
             goto 10
          end if

       end if

       ! short option
       ! check if valid
       if (present(options)) then
          chrpos = scan(options, chr)
       else
          chrpos = 0
       end if

       ! option is invalid, stop
       if (chrpos == 0) then
          st = -1
          goto 10
       end if

       ! look for argument requirement
       if (chrpos < len_trim(options)) then
          if (options(chrpos+1:chrpos+1) == ':') then

             ! check if there are still arguments left
             if (pos == cnt) then
                st = -2
                goto 10
             end if

             ! go to next position
             pos = pos + 1

             ! get argument
             call get_command_argument (pos, arg, length)

             ! make sure it is not an option
             if (arg(1:1) == '-') then
                st = -2
                pos = pos - 1
                goto 10
             end if

          end if
       end if

       ! if we get to this point, no error happened
       ! return option and the argument (if there is one)
       st = 0
       goto 10
    end if

    ! not an option, parsing stops
    st = 1
    ! we are already at the first non-option argument
    ! go one step back to the last option or option argument
    pos = pos - 1


    ! error handling and setting of return values
10  continue

    if (present(err)) then
       if (err) then

          select case (st)
          case (-1)
             write (error_unit, *) "error: unrecognised option: " // trim(arg) 
          case (-2)
             if (.not. long) then
                write (error_unit, *) "error: option -" // chr &
                     // " requires an argument"
             else
                write (error_unit, *) "error: option --" &
                     // trim(longopts(id)%name) // " requires an argument"
             end if
          end select

       end if
    end if

    if (present(optchar)) optchar = chr
    if (present(optarg))  optarg  = arg
    if (present(arglen))  arglen  = length
    if (present(stat))    stat    = st
    if (present(offset))  offset  = pos
    if (present(remain))  remain  = cnt-pos

    ! setup pos for next call to getopt
    pos = pos + 1

  contains

    integer function lookup (name)
      character(len=*), intent(in) :: name
      integer :: i

      ! if there are no long options, skip the loop
      if (.not. present(longopts)) goto 10

      do i = 1, size(longopts)
         if (name == longopts(i)%name) then
            lookup = i
            return
         end if
      end do
      ! if we get to this point, the option was not found

10    lookup = 0
    end function lookup

  end subroutine getopt

  !============================================================================

  !> Print an option in the style of a man page. I.e.
  !> \code
  !> -o arg
  !> --option arg
  !>    description.................................................................
  !>    ............................................................................
  !> \endcode
  subroutine print_opt (opt, unit)
    !> the option
    class(option), intent(in) :: opt
    !> logical unit number
    integer, intent(in) :: unit

    integer :: l, c1, c2

    if (opt%has_arg) then
       write (unit, '(1x,"-",a,1x,a)') opt%chr, trim(opt%argname)
       write (unit, '(1x,"--",a,1x,a)') trim(opt%name), trim(opt%argname)
    else
       write (unit, '(1x,"-",a)') opt%chr
       write (unit, '(1x,"--",a)') trim(opt%name)
    end if
    l = len_trim(opt%descr)

    ! c1 is the first character of the line
    ! c2 is one past the last character of the line
    c1 = 1
    do
       if (c1 > l) exit
       ! print at maximum 4+76 = 80 characters
       c2 = min(c1 + 76, 500)
       ! if not at the end of the whole string
       if (c2 /= 500) then
          ! find the end of a word
          do
             if (opt%descr(c2:c2) == ' ') exit
             c2 = c2-1
          end do
       end if
       write (unit, '(4x,a)') opt%descr(c1:c2-1)
       c1 = c2+1
    end do

  end subroutine print_opt

end module options