1
! @@LICENSE@@ see Copyright notice in the top-directory
3
! This extension currently uses these pre-processor flags:
4
! - BUD_MOD_NAME name of the module
5
! - BUD_TYPE_NAME name of the public type
6
! - BUD_TYPE_NAME_ name of the internal data pointer
7
! - BUD_TYPE_NAME_STR the routine name in "string" format (for IO)
9
#include "bud_utils.inc"
11
! This bud does not allow writing
14
! Define default variable for the file
15
#define BUD_MOD_NAME BUD_CC3(BUD_MOD,_,File)
16
#define BUD_TYPE_NAME BUD_CC2(BUD_TYPE,File)
18
!> @defgroup file File
19
!! @ingroup bud-intrinsic
21
!! Perform file operations such as open/close/delete etc.
23
!! This enables a consistent data-type which contains the
24
!! unit of a file while implementing specific queries
27
!! I.e. one may create a file object and determine whether
28
!! it exists or not. One may also use the object to
31
!! Exposes functionality regarding files.
32
!! It allows interaction with the files in a standard way
33
!! by opening, closing, deleting files in a simple and clean
38
! This *MUST* be the first statement
39
! Common parameters and variables used
40
# include "bud_common_declarations.inc"
42
!> Maximum path length for the filenames
43
integer, parameter :: FILE_NAME_LEn = 256
45
!> Initial unit to open files with
46
integer, parameter :: FILE_UNIT_STARt = 1000
51
!! This generic file type enables opening/closing/deleting
52
!! etc. using a single file handle.
54
!! It may also be used to retrieve a new file-unit.
57
!> Stored pointer which contains the reference counting etc.
58
type(BUD_TYPE_NAME_), pointer :: D => null()
60
# include "bud_common_type.inc"
61
#if BUD_FORTRAN >= 2003
64
procedure, public :: new => new_
67
procedure, public :: open => open_
70
procedure, public :: close => close_
73
procedure, public :: rewind => rewind_
76
procedure, public :: backspace => backspace_
79
procedure, public :: file => filename_
81
procedure, public :: name => filename_
83
procedure, public :: filename => filename_
86
procedure, public :: unit => get_unit_
88
!> @iSee unopened_unit
89
procedure, public, nopass :: unopened_unit => unopened_unit_
92
procedure, public :: is_open => is_open_
94
procedure, public :: is_direct => is_direct_
95
!> @iSee is_sequential
96
procedure, public :: is_sequential => is_sequential_
98
procedure, public :: is_formatted => is_formatted_
99
!> @iSee is_unformatted
100
procedure, public :: is_unformatted => is_unformatted_
103
procedure, public :: exists => exists_
106
procedure, public :: delete_file => delete_file_
109
procedure, public :: stat => get_stat_
112
end type BUD_TYPE_NAME
115
!> @cond BUD_DEVELOPER
117
!> @bud container for BUD_TYPE_NAME
119
!! Contains information regarding a file.
123
!> File name/path for this object
124
character(len=FILE_NAME_LEN) :: file = ' '
126
!> Unit for the file (negative if non-opened)
129
! Consistent data in the reference counted object
130
# include "bud_common_type_.inc"
132
end type BUD_TYPE_NAME_
134
!> @endcond BUD_DEVELOPER
138
!> Create a new file @bud
140
!! Initializes the file with the associated
144
!! This will _not_ open the file.
146
module procedure new_
151
!> Open file via object
153
!! Equivalent to `open` statement.
155
!! If the file is already opened, the file will be
156
!! closed and subsequently re-opened with the passed
159
!! Once a file is opened several procedures may
160
!! be used to query information, in case one does not
161
!! know the options used for opening the file.
163
module procedure open_
167
!> Close file via object
169
!! This transforms the file into an un-opened
170
!! state and several of the procedures will return
171
!! default values in this case.
173
module procedure close_
177
!> Rewind file to the beginning of the file
179
!! This is equivalent to the `rewind` statement.
181
module procedure rewind_
185
!> Backspace the file
187
!! Move back in the file records.
189
module procedure backspace_
194
!> Retrieve new unused unit (global function)
196
!! Will always return an integer > 1000 with a unit
197
!! that is currently not in use.
198
interface unopened_unit
199
module procedure unopened_unit_
201
public :: unopened_unit
203
!> Query filename of the file @bud
205
!! This *MUST* only be called on an initialized
207
!! The function will return a trimmed filename
208
!! which is only possible by using `len_trim`.
210
module procedure filename_
214
!> Unit of opened file @bud
216
!! Retrieve the unit of the file in case it is
218
!! If the file is not opened, `-1` will be returned.
220
module procedure get_unit_
226
module procedure get_unit_
230
!> Query whether file is open
232
!! Checks whether the file is opened by `open`.
234
module procedure is_open_
238
!> Query whether file exists
240
!! Checks whether the file exists on disk.
241
!! This is independent of `is_open` and may
242
!! be called on unopened files.
244
module procedure exists_
248
!> Query file opened in DIRECT mode
250
module procedure is_direct_
254
!> Query file opened in SEQUENTIAL mode
255
interface is_sequential
256
module procedure is_sequential_
258
public :: is_sequential
260
!> Query file opened in FORMATTED mode
261
interface is_formatted
262
module procedure is_formatted_
264
public :: is_formatted
266
!> Query file opened in UNFORMATTED mode
267
interface is_unformatted
268
module procedure is_unformatted_
270
public :: is_unformatted
272
!> Status of the latest action with iostat
274
!! This may be to check the `iostat` of the last internal
277
!! Calling this immediately twice in a row will always return
278
!! 0 on the second call.
280
module procedure get_stat_
285
!> Delete file on disk
287
!! Perform an actual delete of the file.
288
!! If the file is opened it will be closed afterwards.
290
!! The file object will not be deleted as one may then query
291
!! the status of the operation.
292
interface delete_file
293
module procedure delete_file_
295
public :: delete_file
298
! Include common data routines
299
! Note that 'CONTAINS' is present in this include file.
300
! the common_delete_ ensures a closed file
301
! Hence we *must* not use elemental
302
# include "bud_common.inc"
305
!> @cond BUD_DEVELOPER
307
!> Internal routine for cleaning up the data container.
310
!! This routine is only used internally to clean-up
311
!! any data in the type.
312
!! Should never be made public.
314
!! @param[inout] this contained data to be deleted
315
subroutine delete_(this)
316
type(BUD_TYPE_NAME), intent(inout) :: this
321
inquire( this%D%unit , opened = is_open )
323
! Only close if the file is open
325
close( this%D%unit, iostat = stat )
332
call set_error(this, stat)
334
end subroutine delete_
336
!> @param[inout] this force the status to be 0
337
subroutine stat_reset_(this)
338
BUD_CLASS(BUD_TYPE_NAME), intent(inout) :: this
339
call set_error(this, 0)
340
end subroutine stat_reset_
342
!> @endcond BUD_DEVELOPER
345
!> @param[in] from origin of data
346
!> @param[inout] to copy data to this object
347
subroutine copy_(from, to)
348
BUD_CLASS(BUD_TYPE_NAME), intent(in) :: from
349
BUD_CLASS(BUD_TYPE_NAME), intent(inout) :: to
352
if ( .not. is_initd(from) ) return
356
call common_copy_(from, to)
359
to%D%file = from%D%file
360
to%D%unit = from%D%unit
366
!> @param[inout] this file @bud
367
!! @param[in] filename the path `this` will contain
368
subroutine new_(this, filename)
369
BUD_CLASS(BUD_TYPE_NAME), intent(inout) :: this
370
character(len=*), intent(in) :: filename
373
call initialize(this)
375
if ( len_trim(filename) == 0 ) then
376
! show an error if there is no filename
377
call set_error(this, -100)
379
this%D%file = trim(filename)
385
!> @return unit currently un-used
386
function unopened_unit_() result(unit)
390
unit = FILE_UNIT_STARt - 1
395
inquire( unit , opened = is_open )
398
end function unopened_unit_
400
!> @param[in] this query filename from this file @bud
401
!! @return filename of the file @bud
402
pure function filename_(this) result(filename)
403
BUD_CLASS(BUD_TYPE_NAME), intent(in) :: this
405
character(len=len_trim(this%D%file)) :: filename
407
filename = trim(this%D%file)
409
end function filename_
411
!> @param[in] this file @bud
412
!! @return unit of the opened file (-1 if unopened)
413
function get_unit_(this) result(unit)
414
BUD_CLASS(BUD_TYPE_NAME), intent(inout) :: this
418
if ( .not. is_initd(this) ) then
423
call stat_reset_(this)
426
end function get_unit_
428
!> @param[in] this file @bud
429
!! @return `.true.` if the file is opened
430
function is_open_(this) result(is)
431
BUD_CLASS(BUD_TYPE_NAME), intent(inout) :: this
436
if ( .not. is_initd(this) ) then
441
if ( this%D%unit < 0 ) then
442
call stat_reset_(this)
447
inquire( this%D%unit, opened = is, &
449
call set_error(this, stat)
451
! restore unit in case it really is not opened...
452
! this should only happen if the user closes the file
453
! without using the `close` function.
454
if ( .not. is ) this%D%unit = -1
456
end function is_open_
459
!> @param[in] this file @bud
460
!! @return `.true.` if the file exists on disk
461
function exists_(this) result(exist)
462
BUD_CLASS(BUD_TYPE_NAME), intent(inout) :: this
467
if ( is_initd(this) ) then
468
inquire( file = this%D%file, exist = exist, &
470
call set_error(this, stat)
478
!> @param[in] this file @bud
479
!! @return `.true.` if the file is opened in access=DIRECT mode
480
function is_direct_(this) result(direct)
481
BUD_CLASS(BUD_TYPE_NAME), intent(inout) :: this
483
character(len=10) :: dir
487
if ( is_open(this) ) then
488
inquire( this%D%unit, direct = dir, &
490
call set_error(this, stat)
492
direct = (dir == 'YES') .or. &
497
call stat_reset_(this)
502
end function is_direct_
505
!> @param[in] this file @bud
506
!! @return `.true.` if the file is opened in access=SEQUENTIAL mode
507
function is_sequential_(this) result(sequential)
508
BUD_CLASS(BUD_TYPE_NAME), intent(inout) :: this
510
character(len=10) :: seq
512
logical :: sequential
514
if ( is_open(this) ) then
515
inquire( this%D%unit, sequential = seq, &
517
call set_error(this, stat)
518
sequential = (seq == 'YES') .or. &
523
call stat_reset_(this)
528
end function is_sequential_
531
!> @param[in] this file @bud
532
!! @return `.true.` if the file is opened in form=FORMATTED mode
533
function is_formatted_(this) result(formatted)
534
BUD_CLASS(BUD_TYPE_NAME), intent(inout) :: this
536
character(len=10) :: form
540
if ( is_open(this) ) then
541
inquire( this%D%unit, formatted = form, &
543
call set_error(this, stat)
545
formatted = (form == 'YES') .or. &
550
call stat_reset_(this)
555
end function is_formatted_
558
!> @param[in] this file @bud
559
!! @return `.true.` if the file is opened in form=UNFORMATTED mode
560
function is_unformatted_(this) result(unformatted)
561
BUD_CLASS(BUD_TYPE_NAME), intent(inout) :: this
563
character(len=10) :: unform
565
logical :: unformatted
567
if ( is_open(this) ) then
568
inquire( this%D%unit, unformatted = unform, &
570
call set_error(this, stat)
572
unformatted = (unform == 'YES') .or. &
577
call stat_reset_(this)
578
unformatted = .false.
582
end function is_unformatted_
585
!> @param[in] this file @bud
586
!! @return status from the last operation
587
function get_stat_(this) result(stat)
588
BUD_CLASS(BUD_TYPE_NAME), intent(inout) :: this
592
if ( is_open(this) ) then
594
call stat_reset_(this)
599
end function get_stat_
602
!> @param[inout] this file @bud
603
!! @param[in] D (dummy argument which should _NEVER_ be used, forces explicit interface usage)
604
!! @param[in] form @opt='FORMATTED' format of opened file
605
!! @param[in] access @opt='SEQUENTIAL' file access pattern
606
!! @param[in] action @opt='READWRITE' file R/W access
607
!! @param[in] status @opt='OLD' file-existance
608
subroutine open_(this, D, form, access, action, status)
609
BUD_CLASS(BUD_TYPE_NAME), intent(inout) :: this
610
character(len=*), intent(in), optional :: D, form, access, action, status
612
character(len=32) :: lform, laccess, laction, lstatus
615
if ( .not. is_initd(this) ) return
617
! ensure that the file is closed
622
if ( present(form) ) lform = trim(form)
623
laccess = 'SEQUENTIAL'
624
if ( present(access) ) laccess = trim(access)
625
laction = 'READWRITE'
626
if ( present(action) ) laction = trim(action)
628
if ( present(status) ) lstatus = trim(status)
631
this%D%unit = unopened_unit()
633
open( this%D%unit, file=trim(this%D%file), &
634
form = lform, access = laccess, action = laction, &
637
call set_error(this, stat)
641
!> @param[inout] this file @bud
642
subroutine close_(this)
643
BUD_CLASS(BUD_TYPE_NAME), intent(inout) :: this
646
if ( .not. is_open(this) ) then
647
call stat_reset_(this)
652
close( this%D%unit, iostat = stat )
653
call set_error(this, stat)
657
end subroutine close_
659
!> @param[inout] this file @bud
660
subroutine rewind_(this)
661
BUD_CLASS(BUD_TYPE_NAME), intent(inout) :: this
664
if ( .not. is_open(this) ) return
666
rewind( this%D%unit, iostat = stat )
667
call set_error(this, stat)
669
end subroutine rewind_
671
!> @param[inout] this file @bud
672
!! @param[in] n @opt=1 number of times to backspace
673
subroutine backspace_(this, n)
674
BUD_CLASS(BUD_TYPE_NAME), intent(inout) :: this
675
integer, intent(in), optional :: n
679
if ( .not. is_open(this) ) then
680
call stat_reset_(this)
684
if ( present(n) ) then
688
backspace( this%D%unit, &
690
call set_error(this, stat)
692
if ( error(this) /= 0 ) return
697
backspace( this%D%unit, &
699
call set_error(this, stat)
703
end subroutine backspace_
705
!> @param[inout] this file @bud
706
subroutine delete_file_(this)
707
BUD_CLASS(BUD_TYPE_NAME), intent(inout) :: this
711
! immediately return if the object
712
! has not been created.
713
if ( .not. is_initd(this) ) return
715
if ( is_open(this) ) then
717
close( this%D%unit, STATUS = 'DELETE', &
719
call set_error(this, stat)
721
! reset status as not opened
724
else if ( exists(this) ) then
726
unit = unopened_unit()
727
! it does not matter how it is opened
728
open( unit, file = this%D%file )
729
close( unit, STATUS = 'DELETE', &
731
call set_error(this, stat)
735
end subroutine delete_file_
738
!> @param[in] this data type
739
!! @param[in] info @opt=BUD_TYPE_NAME_STR additional information printed
740
!! @param[in] indent @opt=1 possible indentation of printed statement
741
subroutine print_(this, info, indent)
742
BUD_CLASS(BUD_TYPE_NAME), intent(inout) :: this
743
character(len=*), intent(in), optional :: info
744
integer, intent(in), optional :: indent
749
character(len=32) :: fmt
750
character(len=256) :: name
752
name = BUD_TYPE_NAME_STR
753
if ( present(info) ) name = info
755
if ( present(indent) ) lindent = indent
757
write(fmt, '(a,i0,a)') '(t',lindent,',3a)'
759
if ( .not. is_initd(this) ) then
760
write(*,fmt) "<", trim(name), " not initialized>"
765
write(fmt, '(a,i0,a)') '(t',lindent,',4a,4(a,l1),a,i0,a)'
767
write(*,fmt) "<", trim(name), &
768
" file=", filename(this), &
769
", open=", is_open(this), &
770
", formatted=", is_formatted(this), &
771
", direct=", is_direct(this), &
772
", exists=", exists(this), &
773
", refs: ", references(this), ">"
775
call stat_reset_(this)
777
end subroutine print_
783
! project-buds -- local file settings
784
! Anything below this line may be overwritten by scripts
785
! Below are non-editable settings
791
! f90-associate-indent: 2
792
! f90-continuation-indent: 2
793
! f90-structure-indent: 2
794
! f90-critical-indent: 2
795
! f90-program-indent: 2