Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

filesystem: error handling, delete_file, is_directory and other #904

Open
wants to merge 29 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
dbe2d49
Generalize `state_type` -> to `module stdlib_error`
perazz Dec 8, 2024
0fecec4
add `io_filesystem` module
perazz Dec 8, 2024
668bd1c
add filesystem test program
perazz Dec 8, 2024
354c75c
`state_type`: add assignment operator
perazz Dec 8, 2024
4b01b33
filesystem: `delete_file`
perazz Dec 8, 2024
f1aa61a
add tests
perazz Dec 8, 2024
710d322
runtime OS type evaluation
perazz Dec 8, 2024
b4a8900
add `is_directory`
perazz Dec 9, 2024
66301ef
implement `getfile`
perazz Dec 9, 2024
5ca7096
system: implement `run`, `null_device`
perazz Dec 9, 2024
3006ca5
filesystem: implement `is_directory`, `delete_file`
perazz Dec 9, 2024
09cf636
reorganize `delete_file`
perazz Dec 9, 2024
0b26aee
document `run`
perazz Dec 9, 2024
6cc51bb
document `null_device`
perazz Dec 9, 2024
551d238
document `OS_TYPE`, `runtime_os`
perazz Dec 9, 2024
087955a
document OS type flags
perazz Dec 9, 2024
f65fcd1
document `getfile`
perazz Dec 9, 2024
a3df034
document `delete_file`, `is_directory`
perazz Dec 9, 2024
27d6b15
remove `pure`
perazz Dec 9, 2024
283ae75
use Windows API
perazz Dec 9, 2024
3990315
Use Windows API if possible
perazz Dec 9, 2024
6b61ab0
test is_directory
perazz Dec 9, 2024
3c2f866
test is_directory with file
perazz Dec 9, 2024
ebf7aca
Merge branch 'delete_file' of https://github.com/perazz/stdlib into d…
perazz Dec 9, 2024
8788cc6
fix docs
perazz Dec 9, 2024
5b2dbb0
`getfile`: read all at once
perazz Dec 11, 2024
79d6f46
`getline` add tests
perazz Dec 11, 2024
5b2283d
better error message on read
perazz Dec 11, 2024
14f58b6
Merge branch 'fortran-lang:master' into delete_file
perazz Dec 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/specs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ This is an index/directory of the specifications (specs) for each new module/fea
- [string\_type](./stdlib_string_type.html) - Basic string support
- [stringlist_type](./stdlib_stringlist_type.html) - 1-Dimensional list of strings
- [strings](./stdlib_strings.html) - String handling and manipulation routines
- [system](./stdlib_system.html) - OS and sub-processing routines
- [version](./stdlib_version.html) - Version information

## Released/Stable Features & Modules
Expand Down
158 changes: 158 additions & 0 deletions doc/specs/stdlib_io.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,161 @@ Provides formats for all kinds as defined in the `stdlib_kinds` module.
```fortran
{!example/io/example_fmt_constants.f90!}
```

## `getfile` - Read a whole ASCII file into a string variable

### Status

Experimental

### Description

This function reads the entirety of a specified ASCII file and returns its content as a string. The function provides an optional error-handling mechanism via the `state_type` class. If the `err` argument is not provided, exceptions will trigger an `error stop`. The function also supports an optional flag to delete the file after reading.

### Syntax

`call [[stdlib_io(module):getfile(function)]] (fileName [, err] [, delete=.false.])`

### Class
Function

### Arguments

`fileName`: Shall be a character input containing the path to the ASCII file to read. It is an `intent(in)` argument.

`err` (optional): Shall be a `type(state_type)` variable. It is an `intent(out)` argument used for error handling.

`delete` (optional): Shall be a `logical` flag. If `.true.`, the file is deleted after reading. Default is `.false.`. It is an `intent(in)` argument.

### Return values

The function returns a `string_type` variable containing the full content of the specified file.

Raises `STDLIB_IO_ERROR` if the file is not found, cannot be opened, read, or deleted.
Exceptions trigger an `error stop` unless the optional `err` argument is provided.

### Example

```fortran
program example_getfile
use stdlib_io
implicit none

type(string_type) :: fileContent
type(state_type) :: err

! Read a file into a string
fileContent = getfile("example.txt", err=err)

if (err%error()) then
print *, "Error reading file:", err%print()
else
print *, "File content:", fileContent
end if
end program example_getfile
```

## `is_directory` - Test if a path is a directory

### Status

Experimental

### Description

This function checks if a specified file system path is a directory. It is designed to work across multiple platforms without relying on external C libraries, using system commands native to the detected operating system.

Supported operating systems include Linux, macOS, Windows, and UNIX-like environments (e.g., FreeBSD, OpenBSD). If the operating system is unknown or unsupported, the function will return `.false.`.

### Syntax

`result = [[stdlib_io(module):is_directory(function)]] (path)`

### Class
Function

### Arguments

`path`: Shall be a character string containing the file system path to evaluate. It is an `intent(in)` argument.

### Return values

The function returns a `logical` value:

- `.true.` if the path matches an existing directory.
- `.false.` otherwise, or if the operating system is unsupported.

### Example

```fortran
program example_is_directory
use stdlib_io
implicit none

logical :: isDir

! Test a directory path
isDir = is_directory("/path/to/check")

if (isDir) then
print *, "The specified path is a directory."
else
print *, "The specified path is not a directory."
end if
end program example_is_directory
```

## `delete_file` - Delete a file

### Status

Experimental

### Description

This subroutine deletes a specified file from the filesystem. It ensures that the file exists and is not a directory before attempting deletion.
If the file cannot be deleted due to permissions, being a directory, or other issues, an error is raised.
Errors are handled using the library's `state_type`. If the optional `err` argument is not provided, exceptions trigger an `error stop`.

### Syntax

`call [[stdlib_fs(module):delete_file(subroutine)]] (path [, err])`

### Class
Subroutine

### Arguments

`path`: Shall be a character string containing the path to the file to be deleted. It is an `intent(in)` argument.

`err` (optional): Shall be a `type(state_type)` variable for error handling. If provided, errors are returned as a state object. If not provided, the program stops execution on error.

### Behavior

- Checks if the file exists. If not, an error is raised.
- Ensures the path is not a directory before deletion.
- Attempts to delete the file, raising an error if unsuccessful.

### Return values

The file is removed from the filesystem if the operation is successful. If the operation fails, an error is raised.

### Example

```fortran
program example_delete_file
use stdlib_fs
implicit none

type(state_type) :: err

! Delete a file with error handling
call delete_file("example.txt", err)

if (err%error()) then
print *, "Failed to delete file:", err%print()
else
print *, "File deleted successfully."
end if
end program example_delete_file
```
219 changes: 219 additions & 0 deletions doc/specs/stdlib_system.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
---
title: system
---

# System and sub-processing module

[TOC]

## `run` - Execute a synchronous command

### Status

Experimental

### Description

This subroutine executes a command in the system shell synchronously, waiting for its completion before returning. It provides the option to capture the command's standard output (`stdout`) and standard error (`stderr`), along with its exit and command states.

The implementation relies on Fortran's `execute_command_line`.

### Syntax

`call [[stdlib_system(module):run(subroutine)]](cmd [, exit_state] [, command_state] [, stdout] [, stderr])`

### Class

Subroutine

### Arguments

`cmd`: Shall be a scalar `character(len=*)` input argument containing the shell command to execute.

`exit_state` (optional): Shall be an integer `intent(out)` argument, returning the command's exit state (usually `0` on success).

`command_state` (optional): Shall be an integer `intent(out)` argument, indicating issues with command invocation.

`stdout` (optional): Shall be an `intent(out)` `type(string_type)` variable, capturing the command's standard output.

`stderr` (optional): Shall be an `intent(out)` `type(string_type)` variable, capturing the command's standard error messages.

### Return Values

- Captures the exit state and command state of the executed command.
- Retrieves `stdout` and/or `stderr` if the respective optional arguments are provided.
- Raises an error via `error stop` if no `exit_state` or `command_state` arguments are provided and an issue occurs.

### Example

```fortran
program example_run
use stdlib_system, only: run
implicit none
type(string_type) :: output, error_output
integer :: exit_status, cmd_status

call run("ls -l", exit_state=exit_status, command_state=cmd_status, stdout=output, stderr=error_output)

if (exit_status == 0) then
print *, "Command executed successfully!"
print *, "Output:", trim(output)
else
print *, "Error occurred:", trim(error_output)
end if
end program example_run
```

## `null_device` - Return the null device file path

### Status

Experimental

### Description

This function returns the file path of the null device, which is a special file used to discard any data written to it.
It reads as an empty file. The null device's path varies by operating system:
- On Windows, the null device is represented as `NUL`.
- On UNIX-like systems (Linux, macOS), the null device is represented as `/dev/null`.

### Syntax

`path = [[stdlib_system(module):null_device(function)]]()`

### Class

Function

### Arguments

None.

### Return Value

- **Type:** `character(:), allocatable`
- Returns the null device file path as a character string, appropriate for the operating system.

### Example

```fortran
program example_null_device
use stdlib_system, only: null_device
implicit none
character(:), allocatable :: null_path

! Retrieve the null device path
null_path = null_device()

print *, "The null device path is: ", null_path
end program example_null_device
```

## `runtime_os` - Determine the OS type at runtime

### Status

Experimental

### Description

`runtime_os` inspects the runtime environment to identify the current OS type. It evaluates environment variables (`OSTYPE`, `OS`) and checks for specific files associated with known operating systems.
The supported OS types are:

- **Linux** (`OS_LINUX`)
- **macOS** (`OS_MACOS`)
- **Windows** (`OS_WINDOWS`)
- **Cygwin** (`OS_CYGWIN`)
- **Solaris** (`OS_SOLARIS`)
- **FreeBSD** (`OS_FREEBSD`)
- **OpenBSD** (`OS_OPENBSD`)

If the OS cannot be identified, the function returns `OS_UNKNOWN`.

### Syntax

`os = [[stdlib_system(module):runtime_os(function)]]()`

### Class

Function

### Arguments

None.

### Return Value

- **Type:** `integer`
- Returns a constant representing the OS type, or `OS_UNKNOWN` if undetermined.

### Example

```fortran
program example_os_detection
use stdlib_system, only: OS_TYPE, runtime_os
implicit none
integer :: os_type_cached, os_type_runtime

! Cached OS detection
os_type_cached = OS_TYPE()
print *, "Cached OS Type: ", os_type_cached

! Runtime OS detection (full inspection)
os_type_runtime = runtime_os()
print *, "Runtime OS Type: ", os_type_runtime
end program example_os_detection
```

---

## `OS_TYPE` - Cached OS type retrieval

### Status

Experimental

### Description

`OS_TYPE` provides a cached result of the `runtime_os` function. The OS type is determined during the first invocation and stored in a static variable.
Subsequent calls reuse the cached value, making this function highly efficient.

This caching mechanism ensures negligible overhead for repeated calls, unlike `runtime_os`, which performs a full runtime inspection.

### Syntax

`os = [[stdlib_system(module):OS_TYPE(function)]]()`

### Class

Function

### Arguments

None.

### Return Value

- **Type:** `integer`
- Returns a cached constant representing the OS type, as determined by `runtime_os`.

---

### Example

```fortran
program example_os_detection
use stdlib_system, only: OS_TYPE, runtime_os
implicit none
integer :: os_type_cached, os_type_runtime

! Cached OS detection
os_type_cached = OS_TYPE()
print *, "Cached OS Type: ", os_type_cached

! Runtime OS detection (full inspection)
os_type_runtime = runtime_os()
print *, "Runtime OS Type: ", os_type_runtime
end program example_os_detection
```

Loading
Loading