Skip to content

Commit 954a84b

Browse files
authored
system: null_device (fortran-lang#945)
2 parents d724658 + 1676233 commit 954a84b

File tree

7 files changed

+136
-35
lines changed

7 files changed

+136
-35
lines changed

doc/specs/stdlib_system.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,3 +417,39 @@ Returns one of the `integer` `OS_*` parameters representing the OS type, from th
417417
```fortran
418418
{!example/system/example_os_type.f90!}
419419
```
420+
421+
## `null_device` - Return the null device file path
422+
423+
### Status
424+
425+
Experimental
426+
427+
### Description
428+
429+
This function returns the file path of the null device, which is a special file used to discard any data written to it.
430+
It reads as an empty file. The null device's path varies by operating system:
431+
- On Windows, the null device is represented as `NUL`.
432+
- On UNIX-like systems (Linux, macOS), the null device is represented as `/dev/null`.
433+
434+
### Syntax
435+
436+
`path = [[stdlib_system(module):null_device(function)]]()`
437+
438+
### Class
439+
440+
Function
441+
442+
### Arguments
443+
444+
None.
445+
446+
### Return Value
447+
448+
- **Type:** `character(:), allocatable`
449+
- Returns the null device file path as a character string, appropriate for the operating system.
450+
451+
### Example
452+
453+
```fortran
454+
{!example/system/example_null_device.f90!}
455+
```

example/system/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
ADD_EXAMPLE(get_runtime_os)
2+
ADD_EXAMPLE(null_device)
23
ADD_EXAMPLE(os_type)
34
ADD_EXAMPLE(process_1)
45
ADD_EXAMPLE(process_2)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
! Showcase usage of the null device
2+
program example_null_device
3+
use stdlib_system, only: null_device
4+
use iso_fortran_env, only: output_unit
5+
implicit none
6+
integer :: unit
7+
logical :: screen_output = .false.
8+
9+
if (screen_output) then
10+
unit = output_unit
11+
else
12+
! Write to the null device if no screen output is wanted
13+
open(newunit=unit,file=null_device())
14+
endif
15+
16+
write(unit,*) "Hello, world!"
17+
18+
if (.not.screen_output) close(unit)
19+
20+
end program example_null_device

src/stdlib_system.F90

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module stdlib_system
2-
use, intrinsic :: iso_c_binding, only : c_int, c_long, c_null_ptr, c_int64_t
3-
use stdlib_kinds, only: int64, dp
2+
use, intrinsic :: iso_c_binding, only : c_int, c_long, c_ptr, c_null_ptr, c_int64_t, c_size_t, &
3+
c_f_pointer
4+
use stdlib_kinds, only: int64, dp, c_char
45
implicit none
56
private
67
public :: sleep
@@ -81,6 +82,23 @@ module stdlib_system
8182
public :: elapsed
8283
public :: is_windows
8384

85+
!! version: experimental
86+
!!
87+
!! Returns the file path of the null device, which discards all data written to it.
88+
!! ([Specification](../page/specs/stdlib_system.html#null_device-return-the-null-device-file-path))
89+
!!
90+
!! ### Summary
91+
!! Function that provides the file path of the null device appropriate for the current operating system.
92+
!!
93+
!! ### Description
94+
!!
95+
!! The null device is a special file that discards all data written to it and always reads as
96+
!! an empty file. This function returns the null device path, adapted for the operating system in use.
97+
!!
98+
!! On Windows, this is `NUL`. On UNIX-like systems, this is `/dev/null`.
99+
!!
100+
public :: null_device
101+
84102
! CPU clock ticks storage
85103
integer, parameter, private :: TICKS = int64
86104
integer, parameter, private :: RTICKS = dp
@@ -618,4 +636,39 @@ pure function OS_NAME(os)
618636
end select
619637
end function OS_NAME
620638

639+
!> Returns the file path of the null device for the current operating system.
640+
!>
641+
!> Version: Helper function.
642+
function null_device() result(path)
643+
!> File path of the null device
644+
character(:), allocatable :: path
645+
646+
interface
647+
648+
! No-overhead return path to the null device
649+
type(c_ptr) function process_null_device(len) bind(C,name='process_null_device')
650+
import c_ptr, c_size_t
651+
implicit none
652+
integer(c_size_t), intent(out) :: len
653+
end function process_null_device
654+
655+
end interface
656+
657+
integer(c_size_t) :: i, len
658+
type(c_ptr) :: c_path_ptr
659+
character(kind=c_char), pointer :: c_path(:)
660+
661+
! Call the C function to get the null device path and its length
662+
c_path_ptr = process_null_device(len)
663+
call c_f_pointer(c_path_ptr,c_path,[len])
664+
665+
! Allocate the Fortran string with the length returned from C
666+
allocate(character(len=len) :: path)
667+
668+
do concurrent (i=1:len)
669+
path(i:i) = c_path(i)
670+
end do
671+
672+
end function null_device
673+
621674
end module stdlib_system

src/stdlib_system_subprocess.F90

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,6 @@ subroutine process_wait(seconds) bind(C,name='process_wait')
5151
real(c_float), intent(in), value :: seconds
5252
end subroutine process_wait
5353

54-
! Return path to the null device
55-
type(c_ptr) function process_null_device(len) bind(C,name='process_null_device')
56-
import c_ptr, c_int
57-
implicit none
58-
integer(c_int), intent(out) :: len
59-
end function process_null_device
60-
6154
! Utility: check if _WIN32 is defined in the C compiler
6255
logical(c_bool) function process_is_windows() bind(C,name='process_is_windows')
6356
import c_bool
@@ -604,29 +597,6 @@ function assemble_cmd(args, stdin, stdout, stderr) result(cmd)
604597

605598
end function assemble_cmd
606599

607-
!> Returns the file path of the null device for the current operating system.
608-
!>
609-
!> Version: Helper function.
610-
function null_device()
611-
character(:), allocatable :: null_device
612-
613-
integer(c_int) :: i, len
614-
type(c_ptr) :: c_path_ptr
615-
character(kind=c_char), pointer :: c_path(:)
616-
617-
! Call the C function to get the null device path and its length
618-
c_path_ptr = process_null_device(len)
619-
call c_f_pointer(c_path_ptr,c_path,[len])
620-
621-
! Allocate the Fortran string with the length returned from C
622-
allocate(character(len=len) :: null_device)
623-
624-
do concurrent (i=1:len)
625-
null_device(i:i) = c_path(i)
626-
end do
627-
628-
end function null_device
629-
630600
!> Returns the file path of the null device for the current operating system.
631601
!>
632602
!> Version: Helper function.

src/stdlib_system_subprocess.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ void process_wait(float seconds)
376376
}
377377

378378
// Returns the cross-platform file path of the null device for the current operating system.
379-
const char* process_null_device(int* len)
379+
const char* process_null_device(size_t* len)
380380
{
381381
#ifdef _WIN32
382382
(*len) = strlen("NUL");

test/system/test_os.f90

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module test_os
22
use testdrive, only : new_unittest, unittest_type, error_type, check, skip_test
3-
use stdlib_system, only: get_runtime_os, OS_WINDOWS, OS_UNKNOWN, OS_TYPE, is_windows
3+
use stdlib_system, only: get_runtime_os, OS_WINDOWS, OS_UNKNOWN, OS_TYPE, is_windows, null_device
44

55
implicit none
66

@@ -13,7 +13,8 @@ subroutine collect_suite(testsuite)
1313

1414
testsuite = [ &
1515
new_unittest('test_get_runtime_os', test_get_runtime_os), &
16-
new_unittest('test_is_windows', test_is_windows) &
16+
new_unittest('test_is_windows', test_is_windows), &
17+
new_unittest('test_null_device', test_null_device) &
1718
]
1819
end subroutine collect_suite
1920

@@ -38,6 +39,26 @@ subroutine test_is_windows(error)
3839

3940
end subroutine test_is_windows
4041

42+
!> Test that the null_device is valid by writing something to it
43+
subroutine test_null_device(error)
44+
type(error_type), allocatable, intent(out) :: error
45+
integer :: unit, ios
46+
character(len=512) :: iomsg
47+
48+
! Try opening the null device for writing
49+
open(newunit=unit, file=null_device(), status='old', action='write', iostat=ios, iomsg=iomsg)
50+
call check(error, ios==0, 'Cannot open null_device unit: '//trim(iomsg))
51+
if (allocated(error)) return
52+
53+
write(unit, *, iostat=ios, iomsg=iomsg) 'Hello, World!'
54+
call check(error, ios==0, 'Cannot write to null_device unit: '//trim(iomsg))
55+
if (allocated(error)) return
56+
57+
close(unit, iostat=ios, iomsg=iomsg)
58+
call check(error, ios==0, 'Cannot close null_device unit: '//trim(iomsg))
59+
if (allocated(error)) return
60+
61+
end subroutine test_null_device
4162

4263
end module test_os
4364

0 commit comments

Comments
 (0)