Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ if(CMAKE_Fortran_COMPILER_ID STREQUAL GNU AND CMAKE_Fortran_COMPILER_VERSION VER
message(FATAL_ERROR "GCC Version 9 or newer required")
endif()

# Convert CMAKE_SYSTEM_NAME to uppercase
string(TOUPPER "${CMAKE_SYSTEM_NAME}" SYSTEM_NAME_UPPER)

# Pass the uppercase system name as a macro
add_compile_options(-D${SYSTEM_NAME_UPPER})

# --- compiler feature checks
include(CheckFortranSourceCompiles)
include(CheckFortranSourceRuns)
Expand Down
2 changes: 2 additions & 0 deletions config/fypp_deployment.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import platform
import fypp
import argparse
from joblib import Parallel, delayed
Expand Down Expand Up @@ -115,6 +116,7 @@ def fpm_build(args,unknown):
for idx, arg in enumerate(unknown):
if arg.startswith("--flag"):
flags= flags + unknown[idx+1]
flags = flags + "-D{}".format(platform.system().upper())
#==========================================
# build with fpm
subprocess.run("fpm build"+
Expand Down
177 changes: 177 additions & 0 deletions doc/specs/stdlib_system.md
Original file line number Diff line number Diff line change
Expand Up @@ -532,3 +532,180 @@ The file is removed from the filesystem if the operation is successful. If the o
```fortran
{!example/system/example_delete_file.f90!}
```

## `joinpath` - Joins the provided paths according to the OS

### Status

Experimental

### Description

This interface joins the paths provided to it according to the platform specific path-separator.
i.e `\` for windows and `/` for others

### Syntax

`res = [[stdlib_system(module):joinpath(interface)]] (p1, p2)`
`res = [[stdlib_system(module):joinpath(interface)]] (p)`

### Class
Pure function

### Arguments

`p1, p2`: Shall be a character string. It is an `intent(in)` argument.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because stdlib offers a string type, it would be nice if these string manipulation functions were interfaces working with either character strings (as currently proposed), or the internal string_type type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made these functions interfaces keeping this possibility in mind... But what is considered a good way of doing this? Writing procedures for string_type and then, when calling them with character variables just assign these to the new string_type variables and call the string_type version of the interface itself or code duplication, keeping the code separate for these two although they are in essence the same?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I got it!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes: the easiest option is to keep the character implementation and then return the result to a string via move:

!> Moves the allocated character scalar from 'from' to 'to'
!> [Specifications](../page/specs/stdlib_string_type.html#move)
interface move
module procedure :: move_string_string
module procedure :: move_string_char
module procedure :: move_char_string
module procedure :: move_char_char
end interface move

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added the relevant changes... But it doesn't use move, it uses assignment(=) and char, Let me know what you think of it.

Copy link
Member

@perazz perazz Jul 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works @wassup05, however, there are two copies: string->char for the input arguments, and then char -> string on the assignment. Using move would at least avoid incurring the last copy. Just do:

character(:), allocatable :: join_char
join_char = join_path(char(a),char(b))
call move(from=join_char,to=...result variable...)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the internal string_type representation is just an allocatable character variable, I would also support (to be discussed with the community as a separate PR) the implementation of a tiny zero-copy "view" function for the string type, see here:

    !> Zero-copy view of the string as a character array
    pure function view(string) result(char_array)
        type(string_type), intent(in), target :: string
        character(:), pointer :: char_array
        if (allocated(string%raw)) then
            char_array => string%raw
        else
            nullify(char_array)
        end if
    end function view

or
`p`: Shall be a list of character strings. `intent(in)` argument.

### Return values

The resultant path.

### Example

```fortran
{!example/system/example_path_1.f90!}
```

## `operator(/)`

Join two paths according to the platform specific path-separator,
Behavior exactly similar to `joinpath`

### Status

Experimental

### Syntax

`p = lval + rval`

### Class

Pure function.

### Arguments

`lval`: A character string, `intent(in)`.
`rval`: A character string, `intent(in)`.

### Result value

The result is an `allocatable` character string

#### Example

```fortran
{!example/system/example_path_1.f90!}
```

## `splitpath` - splits a path immediately following the last separator

### Status

Experimental

### Description

This subroutine splits a path immediately following the last separator after removing the trailing separators
splitting it into most of the times a directory and a file name.

### Syntax

`call [[stdlib_system(module):splitpath(interface)]] (p, head, tail)`

### Class
Subroutine

### Arguments

`p`: A character string containing the path to be split. `intent(in)`
`head`: The first part of the path. `allocatable, intent(out)`
`tail`: The rest part of the path. `allocatable, intent(out)`

### Behavior

- If `p` is empty, `head` is set to `.` and `tail` is empty
- If `p` consists entirely of path-separators. `head` is set to the path-separator and `tail` is empty
- `head` ends in a path-separator iff and only if `p` appears to be a root directory or child of one

### Return values

The splitted path. `head` and `tail`.

### Example

```fortran
{!example/system/example_path_1.f90!}
```

## `basename` - The last part of a path

### Status

Experimental

### Description

This function returns the last part of a path after removing trailing path separators.

### Syntax

`res = [[stdlib_system(module):basename(interface)]] (p)`

### Class
Function

### Arguments

`p`: the path, a character string, `intent(in)`

### Behavior

- The `tail` of `stdlib_system(module):splitpath(interface)` is exactly what is returned. Same Behavior.

### Return values

A character string.

### Example

```fortran
{!example/system/example_path_1.f90!}
```

## `dirname` - Everything except the last part of the path

### Status

Experimental

### Description

This function returns everything except the last part of a path.

### Syntax

`res = [[stdlib_system(module):dirname(interface)]] (p)`

### Class
Function

### Arguments

`p`: the path, a character string, `intent(in)`

### Behavior

- The `head` of `stdlib_system(module):splitpath(interface)` is exactly what is returned. Same Behavior.

### Return values

A character string.

### Example

```fortran
{!example/system/example_path_1.f90!}
```
1 change: 1 addition & 0 deletions example/system/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ ADD_EXAMPLE(process_5)
ADD_EXAMPLE(process_6)
ADD_EXAMPLE(process_7)
ADD_EXAMPLE(sleep)
ADD_EXAMPLE(path_1)
41 changes: 41 additions & 0 deletions example/system/example_path_1.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
program example_path
use stdlib_system, only: joinpath, operator(/), splitpath, ISWIN, dirname, basename
character(len=:), allocatable :: p1, p2, head, tail
character(len=20) :: parr(4)

if (ISWIN) then
p1 = 'C:'/'Users'/'User1'/'Desktop'
parr = [character(len=20) :: 'C:', 'Users', 'User1', 'Desktop']
p2 = joinpath(parr)

! p1 == p2 = 'C:\Users\User1\Desktop'
print *, p1
print *, "p1 == p2: ", p1 == p2

call splitpath(p1, head, tail)
print *, p1 // " -> " // head // " + " // tail

call splitpath(head, p1, tail)
print *, head // " -> " // p1 // " + " // tail

print *, 'dirname of '// p1 // ' -> ' // dirname(p1)
print *, 'basename of '// p1 // ' -> ' // basename(p1)
else
p1 = ''/'home'/'User1'/'Desktop'
parr = [character(len=20) :: '', 'home', 'User1', 'Desktop']
p2 = joinpath(parr)

! p1 == p2 = '/home/User1/Desktop'
print *, p1
print *, "p1 == p2: ", p1 == p2

call splitpath(p1, head, tail)
print *, p1 // " -> " // head // " + " // tail

call splitpath(head, p1, tail)
print *, head // " -> " // p1 // " + " // tail

print *, 'dirname of '// p1 // ' -> ' // dirname(p1)
print *, 'basename of '// p1 // ' -> ' // basename(p1)
end if
end program example_path
7 changes: 4 additions & 3 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@ set(fppFiles
stdlib_linalg_kronecker.fypp
stdlib_linalg_cross_product.fypp
stdlib_linalg_eigenvalues.fypp
stdlib_linalg_solve.fypp
stdlib_linalg_solve.fypp
stdlib_linalg_determinant.fypp
stdlib_linalg_qr.fypp
stdlib_linalg_inverse.fypp
stdlib_linalg_pinv.fypp
stdlib_linalg_norms.fypp
stdlib_linalg_state.fypp
stdlib_linalg_svd.fypp
stdlib_linalg_svd.fypp
stdlib_linalg_cholesky.fypp
stdlib_linalg_schur.fypp
stdlib_optval.fypp
Expand Down Expand Up @@ -94,6 +94,7 @@ set(cppFiles

stdlib_linalg_blas.fypp
stdlib_linalg_lapack.fypp
stdlib_system.F90
)

add_subdirectory(blas)
Expand All @@ -116,7 +117,7 @@ set(SRC
stdlib_sorting_radix_sort.f90
stdlib_system_subprocess.c
stdlib_system_subprocess.F90
stdlib_system.F90
stdlib_system_path.f90
stdlib_sparse.f90
stdlib_specialfunctions_legendre.f90
stdlib_quadrature_gauss.f90
Expand Down
Loading
Loading