lfi2cdf: options are now stored in a structure, command line is read in Fortran
[MNH-git_open_source-lfs.git] / tools / lfi2cdf / src / mode_options.f90
1 module mode_options
2   implicit none
3
4   integer,parameter :: nbavailoptions = 10
5   integer,parameter :: TYPEUNDEF = -1, TYPEINT = 1, TYPELOG = 2, TYPEREAL = 3, TYPECHAR = 4
6   integer,parameter :: MODEUNDEF = -11, MODECDF2CDF = 11, MODELFI2CDF = 12, MODECDF2LFI = 13
7
8   integer,parameter :: OPTCDF3   = 1, OPTCDF4   = 2, OPTCOMPRESS = 3
9   integer,parameter :: OPTHELP   = 4, OPTLIST   = 5, OPTMERGE    = 6
10   integer,parameter :: OPTOUTPUT = 7, OPTREDUCE = 8, OPTSPLIT    = 9
11   integer,parameter :: OPTVAR    = 10
12
13   type option
14     logical :: set = .false.
15     character(len=:),allocatable :: long_name
16     character :: short_name
17     logical :: has_argument
18     integer :: type = TYPEUNDEF
19     integer :: ivalue
20     logical :: lvalue
21     real    :: rvalue
22     character(len=:),allocatable :: cvalue
23   end type option
24
25 contains
26 subroutine read_commandline(options,hinfile,houtfile,runmode)
27   implicit none
28
29   type(option),dimension(:),allocatable,intent(out) :: options
30   character(len=:),allocatable,intent(out)          :: hinfile
31   character(len=:),allocatable,intent(out)          :: houtfile
32   integer,intent(out)                               :: runmode
33
34   integer :: idx, ji, nbargs, status, sz
35   logical :: finished
36   character(len=:),allocatable :: command, fullcommand
37
38
39   call GET_COMMAND_ARGUMENT(NUMBER=0,LENGTH=sz)
40   allocate(character(len=sz)::fullcommand)
41   call GET_COMMAND_ARGUMENT(NUMBER=0,VALUE=fullcommand)
42
43   idx = index(fullcommand,'/',back=.true.)
44   allocate(character(len=sz-idx)::command)
45   command=fullcommand(idx+1:)
46
47   select case (command)
48     case ('cdf2cdf')
49       runmode = MODECDF2CDF
50     case ('cdf2lfi')
51       runmode = MODECDF2LFI
52     case ('lfi2cdf')
53       runmode = MODELFI2CDF
54     case default
55       runmode = MODEUNDEF
56       print *,'Error: program started with unknown command: ',command
57       call help()
58   end select
59   deallocate(command,fullcommand)
60
61   call init_options(options)
62
63   nbargs = COMMAND_ARGUMENT_COUNT()
64
65   if (nbargs==0) then
66     print *,'Error: no input file given'
67     call help()
68   end if
69
70   if (nbargs>1) then
71     finished = .false.
72     do while(.not.finished)
73       call get_option(options,finished)
74     end do
75   end if
76
77   call GET_COMMAND_ARGUMENT(NUMBER=nbargs,LENGTH=sz)
78   allocate(character(len=sz)::hinfile)
79   call GET_COMMAND_ARGUMENT(NUMBER=COMMAND_ARGUMENT_COUNT(),VALUE=hinfile)
80
81   call check_options(options,hinfile,runmode)
82
83   houtfile = options(OPTOUTPUT)%cvalue
84
85   !Remove level in the filename if merging LFI splitted files
86   if (.NOT.options(OPTOUTPUT)%set) then
87     if (options(OPTMERGE)%set .AND. .NOT.options(OPTSPLIT)%set) then
88        houtfile=houtfile(1:len(houtfile)-9)//houtfile(len(houtfile)-3:)
89     end if
90     if (.NOT.options(OPTMERGE)%set .AND. options(OPTSPLIT)%set) then
91        if (options(OPTCDF4)%set) then
92          ji=4
93        else
94          ji=3
95        end if
96        houtfile=houtfile(1:len(houtfile)-ji)
97     end if
98     if (options(OPTMERGE)%set .AND. options(OPTSPLIT)%set) then
99        if (options(OPTCDF4)%set) then
100          ji=9
101        else
102          ji=8
103        end if
104        houtfile=houtfile(1:len(houtfile)-ji)
105     end if
106   end if
107
108 end subroutine read_commandline
109
110 subroutine init_options(options)
111   implicit none
112
113   type(option),dimension(:),allocatable,intent(out) :: options
114
115   allocate(options(nbavailoptions))
116
117   options(OPTCDF3)%long_name    = "cdf3"
118   options(OPTCDF3)%short_name   = '3'
119   options(OPTCDF3)%has_argument = .false.
120
121   options(OPTCDF4)%long_name    = "cdf4"
122   options(OPTCDF4)%short_name   = '4'
123   options(OPTCDF4)%has_argument = .false.
124
125   options(OPTCOMPRESS)%long_name    = "compress"
126   options(OPTCOMPRESS)%short_name   = 'c'
127   options(OPTCOMPRESS)%has_argument = .true.
128   options(OPTCOMPRESS)%type         = TYPEINT
129
130   options(OPTHELP)%long_name    = "help"
131   options(OPTHELP)%short_name   = 'h'
132   options(OPTHELP)%has_argument = .false.
133
134   options(OPTLIST)%long_name    = "list"
135   options(OPTLIST)%short_name   = 'l'
136   options(OPTLIST)%has_argument = .false.
137
138   options(OPTMERGE)%long_name    = "merge"
139   options(OPTMERGE)%short_name   = 'm'
140   options(OPTMERGE)%has_argument = .true.
141   options(OPTMERGE)%type         = TYPEINT
142
143   options(OPTOUTPUT)%long_name    = "output"
144   options(OPTOUTPUT)%short_name   = 'o'
145   options(OPTOUTPUT)%has_argument = .true.
146   options(OPTOUTPUT)%type         = TYPECHAR
147
148   options(OPTREDUCE)%long_name    = "reduce-precision"
149   options(OPTREDUCE)%short_name   = 'r'
150   options(OPTREDUCE)%has_argument = .false.
151
152   options(OPTSPLIT)%long_name    = "split"
153   options(OPTSPLIT)%short_name   = 's'
154   options(OPTSPLIT)%has_argument = .false.
155
156   options(OPTVAR)%long_name    = "var"
157   options(OPTVAR)%short_name   = 'v'
158   options(OPTVAR)%has_argument = .true.
159   options(OPTVAR)%type         = TYPECHAR
160
161 end subroutine init_options
162
163 subroutine get_option(options,finished)
164   implicit none
165
166   integer,parameter :: MAXARGSIZE=512
167
168   logical,intent(out) :: finished
169   type(option),dimension(:),intent(inout) :: options
170
171   integer,save              :: argnum = 1
172   integer                   :: i, sz
173   logical                   :: found
174   character(len=MAXARGSIZE) :: arg
175
176   found = .false.
177   call GET_COMMAND_ARGUMENT(NUMBER=argnum,VALUE=arg,LENGTH=sz)
178   if(sz>MAXARGSIZE) print *,'Error: argument bigger than ',MAXARGSIZE
179   if ( INDEX(arg,'--')==1 .AND. sz>2) then
180     do i=1,nbavailoptions
181       if (options(i)%long_name == trim(arg(3:))) then
182         found = .true.
183         exit
184       end if
185     end do
186   else if ( INDEX(arg,'-')==1 ) then
187     do i=1,nbavailoptions
188       if (options(i)%short_name == trim(arg(2:))) then
189         found = .true.
190         exit
191       end if
192     end do
193   else
194     print *,'Error: ',trim(arg),' is not an option'
195     call help()
196   end if
197
198   if ( .not.found ) then
199     print *,'Error: unknown option: ',trim(arg)
200     call help()
201   end if
202
203   if (options(i)%set) then
204     print *,'Error: at least 1 option is set several times!'
205     call help()
206   end if
207
208   options(i)%set = .true.
209   if (options(i)%has_argument) then
210     argnum = argnum + 1
211     if (argnum >= COMMAND_ARGUMENT_COUNT()) then
212       print *,'Error: argument for option ',trim(arg),' not found'
213       call help()
214     end if
215     call GET_COMMAND_ARGUMENT(NUMBER=argnum,VALUE=arg,LENGTH=sz)
216     if(sz>MAXARGSIZE) print *,'Error: argument bigger than ',MAXARGSIZE
217     select case (options(i)%type)
218       case (TYPEINT)
219         read (arg,*) options(i)%ivalue
220       case (TYPELOG)
221         read (arg,*) options(i)%lvalue
222       case (TYPEREAL)
223         read (arg,*) options(i)%rvalue
224       case (TYPECHAR)
225         options(i)%cvalue = arg
226       case default
227         print *,'Error: unknown option type'
228         call help()
229     end select
230   end if
231
232   argnum = argnum + 1
233
234   if (argnum >= COMMAND_ARGUMENT_COUNT()) finished = .true.
235
236 end subroutine get_option
237
238 subroutine check_options(options,infile,runmode)
239   implicit none
240
241   type(option),dimension(:),intent(inout) :: options
242   character(len=:),allocatable,intent(in) :: infile
243   integer,intent(in)                      :: runmode
244
245   integer :: idx1, idx2
246
247
248   !Check if help has been asked
249   if (options(OPTHELP)%set) then
250     call help()
251   end if
252
253   !Use NetCF-4 by default
254   if (.NOT.options(OPTCDF3)%set) then
255     options(OPTCDF4)%set = .true.
256   else
257     if (options(OPTCDF4)%set) then
258       print *,'Warning: NetCDF-3 and NetCDF-4 options are not compatible'
259       print *,'NetCDF-4 is forced'
260       options(OPTCDF3)%set = .false.
261     end if
262   end if
263
264   !Check compression level
265   if (options(OPTCOMPRESS)%set) then
266     if (options(OPTCOMPRESS)%ivalue < 1 .OR. options(OPTCOMPRESS)%ivalue > 9 ) then
267       print *,'Error: compression level should in the 1 to 9 interval'
268       call help()
269     end if
270   end if
271
272   !Check list option
273   if (options(OPTLIST)%set .AND. runmode/=MODELFI2CDF) then
274     print *,'Error: list option is only valid for lfi2cdf'
275     call help()
276   end if
277
278   !Merge flag only supported if -v is set
279   if (options(OPTMERGE)%set .AND. .NOT.options(OPTVAR)%set) then
280     print *,'Error: merge option must be used with var option'
281     call help()
282   end if
283
284   !Split flag only supported if -v is set
285   if (options(OPTSPLIT)%set .AND. .NOT.options(OPTVAR)%set) then
286       options(OPTSPLIT)%set = .false.
287       print *,"Warning: split option is forced to disable"
288   end if
289
290   !Determine outfile name if not given
291   if (.NOT.options(OPTOUTPUT)%set) then
292     idx1 = index(infile,'/',back=.true.)
293     idx2 = index(infile,'.',back=.true.)
294     options(OPTOUTPUT)%cvalue = infile(idx1+1:idx2-1)
295   end if
296
297 end subroutine check_options
298
299 subroutine help()
300   implicit none
301
302 !TODO: -l option for cdf2cdf and cdf2lfi
303   print *,"Usage : lfi2cdf [-h --help] [--cdf4 -4] [-l] [-v --var var1[,...]] [-r --reduce-precision]"
304   print *,"                [-m --merge number_of_z_levels] [-s --split] [-o --output output-file.nc]"
305   print *,"                [-c --compress compression_level] input-file.lfi"
306   print *,"        cdf2cdf [-h --help] [--cdf4 -4] [-v --var var1[,...]] [-r --reduce-precision]"
307   print *,"                [-m --merge number_of_z_levels] [-s --split] [-o --output output-file.nc]"
308   print *,"                [-c --compress compression_level] input-file.nc"
309   print *,"        cdf2lfi [-o --output output-file.lfi] input-file.nc"
310   print *,""
311   print *,"Options:"
312   print *,"  --cdf3, -3"
313   print *,"     Write netCDF file in netCDF-3 format (cdf2cdf and lfi2cdf only)"
314   print *,"  --cdf4, -4 (by default)"
315   print *,"     Write netCDF file in netCDF-4 format (HDF5 compatible) (cdf2cdf and lfi2cdf only)"
316   print *,"  --compress, -c compression_level"
317   print *,"     Compress data. The compression level should be in the 1 to 9 interval."
318   print *,"     Only supported with the netCDF-4 format (cdf2cdf and lfi2cdf only)"
319   print *,"  --help, -h"
320   print *,"     Print this text"
321   print *,"  --list, -l"
322   print *,"     List all the fields of the LFI file and returns (lfi2cdf only)"
323   print *,"  --merge, -m number_of_z_levels"
324   print *,"     Merge LFI files which are split by vertical level (cdf2cdf and lfi2cdf only)"
325   print *,"  --output, -o"
326   print *,"     Name of file for the output"
327   print *,"  --reduce-precision, -r"
328   print *,"     Reduce the precision of the floating point variables to single precision (cdf2cdf and lfi2cdf only)"
329   print *,"  --split, -s"
330   print *,"     Split variables specified with the -v option (one per file) (cdf2cdf and lfi2cdf only)"
331   print *,"  --var, -v var1[,...]"
332   print *,"     List of the variable to write in the output file. Variables names have to be separated by commas (,)."
333   print *,"     A variable can be computed from the sum of existing variables (format: new_var=var1+var2[+...])"
334   print *,"     (cdf2cdf and lfi2cdf only)"
335   print *,""
336   stop
337
338 end subroutine help
339
340 end module mode_options