Skip to content

Commit a8519b6

Browse files
authored
feat: get_cwd and set_cwd (#1014)
2 parents 60f5308 + 607da8d commit a8519b6

File tree

6 files changed

+317
-22
lines changed

6 files changed

+317
-22
lines changed

doc/specs/stdlib_system.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,80 @@ Subroutine
646646

647647
---
648648

649+
## `get_cwd` - Gets the current working directory
650+
651+
### Status
652+
653+
Experimental
654+
655+
### Description
656+
657+
This subroutine retrieves the current working directory the running process is executing from.
658+
It is designed to work across multiple platforms. On Windows, paths with both forward `/` and backward `\` slashes are accepted.
659+
660+
### Syntax
661+
662+
`call [[stdlib_system(module):get_cwd(subroutine)]] (cwd [, err])`
663+
664+
### Class
665+
666+
Subroutine
667+
668+
### Arguments
669+
670+
`cwd`: Shall be a character string for receiving the path of the current working directory (cwd). It is an `intent(out)` argument.
671+
672+
`err`(optional): Shall be of type `state_type`, and is used for error handling. It is an `intent(out)` argument.
673+
674+
### Return values
675+
676+
`err` is an optional state return flag. On error if not requested, an `FS_ERROR` will trigger an error stop.
677+
678+
### Example
679+
680+
```fortran
681+
{!example/system/example_cwd.f90!}
682+
```
683+
684+
---
685+
686+
## `set_cwd` - Sets the current working directory
687+
688+
### Status
689+
690+
Experimental
691+
692+
### Description
693+
694+
This subrotine sets the current working directory the process is executing from.
695+
It is designed to work across multiple platforms. On Windows, paths with both forward `/` and backward `\` slashes are accepted.
696+
697+
### Syntax
698+
699+
`call [[stdlib_system(module):set_cwd(subroutine)]] (path [, err])`
700+
701+
### Class
702+
703+
Subroutine
704+
705+
### Arguments
706+
707+
`path`: Shall be a character string containing the path of the directory. It is an `intent(in)` argument.
708+
709+
`err`(optional): Shall be of type `state_type`, and is used for error handling. It is an `intent(out)` argument.
710+
711+
### Return values
712+
713+
`err` is an optional state return flag. On error if not requested, an `FS_ERROR` will trigger an error stop.
714+
715+
### Example
716+
717+
```fortran
718+
{!example/system/example_cwd.f90!}
719+
```
720+
721+
---
722+
649723
## `null_device` - Return the null device file path
650724

651725
### Status
@@ -682,6 +756,8 @@ None.
682756
{!example/system/example_null_device.f90!}
683757
```
684758

759+
---
760+
685761
## `delete_file` - Delete a file
686762

687763
### Status
@@ -723,6 +799,8 @@ The file is removed from the filesystem if the operation is successful. If the o
723799
{!example/system/example_delete_file.f90!}
724800
```
725801

802+
---
803+
726804
## `join_path` - Joins the provided paths according to the OS
727805

728806
### Status
@@ -785,6 +863,8 @@ The result is an `allocatable` character string or `type(string_type)`
785863
{!example/system/example_path_join.f90!}
786864
```
787865

866+
---
867+
788868
## `split_path` - splits a path immediately following the last separator
789869

790870
### Status
@@ -825,6 +905,8 @@ The splitted path. `head` and `tail`.
825905
{!example/system/example_path_split_path.f90!}
826906
```
827907

908+
---
909+
828910
## `base_name` - The last part of a path
829911

830912
### Status
@@ -860,6 +942,8 @@ A character string or `type(string_type)`.
860942
{!example/system/example_path_base_name.f90!}
861943
```
862944

945+
---
946+
863947
## `dir_name` - Everything except the last part of the path
864948

865949
### Status

example/system/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ ADD_EXAMPLE(path_base_name)
1818
ADD_EXAMPLE(path_dir_name)
1919
ADD_EXAMPLE(make_directory)
2020
ADD_EXAMPLE(remove_directory)
21+
ADD_EXAMPLE(cwd)

example/system/example_cwd.f90

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
! Illustrate the usage of `get_cwd`, `set_cwd`
2+
program example_cwd
3+
use stdlib_system, only: get_cwd, set_cwd
4+
use stdlib_error, only: state_type
5+
implicit none
6+
7+
character(len=:), allocatable :: path
8+
type(state_type) :: err
9+
10+
call get_cwd(path, err)
11+
12+
if (err%error()) then
13+
print *, "Error getting current working directory: "//err%print()
14+
end if
15+
16+
print *, "CWD: "//path
17+
18+
call set_cwd("./src", err)
19+
20+
if (err%error()) then
21+
print *, "Error setting current working directory: "//err%print()
22+
end if
23+
24+
call get_cwd(path, err)
25+
26+
if (err%error()) then
27+
print *, "Error getting current working directory after using set_cwd: "//err%print()
28+
end if
29+
30+
print *, "CWD: "//path
31+
end program example_cwd
32+

src/stdlib_system.F90

Lines changed: 100 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ module stdlib_system
22
use, intrinsic :: iso_c_binding, only : c_int, c_long, c_ptr, c_null_ptr, c_int64_t, c_size_t, &
33
c_f_pointer
44
use stdlib_kinds, only: int64, dp, c_bool, c_char
5-
use stdlib_strings, only: to_c_char, find
5+
use stdlib_strings, only: to_c_char, find, to_string
66
use stdlib_string_type, only: string_type
77
use stdlib_optval, only: optval
88
use stdlib_error, only: state_type, STDLIB_SUCCESS, STDLIB_FS_ERROR
@@ -156,6 +156,32 @@ module stdlib_system
156156
!!
157157
public :: remove_directory
158158

159+
!! version: experimental
160+
!!
161+
!! Gets the current working directory of the process
162+
!! ([Specification](../page/specs/stdlib_system.html#get_cwd))
163+
!!
164+
!! ### Summary
165+
!! Gets the current working directory.
166+
!!
167+
!! ### Description
168+
!! This subroutine gets the current working directory the process is executing from.
169+
!!
170+
public :: get_cwd
171+
172+
!! version: experimental
173+
!!
174+
!! Sets the current working directory of the process
175+
!! ([Specification](../page/specs/stdlib_system.html#set_cwd))
176+
!!
177+
!! ### Summary
178+
!! Changes the current working directory to the one specified.
179+
!!
180+
!! ### Description
181+
!! This subroutine sets the current working directory the process is executing from.
182+
!!
183+
public :: set_cwd
184+
159185
!! version: experimental
160186
!!
161187
!! Deletes a specified file from the filesystem.
@@ -896,6 +922,25 @@ end function stdlib_is_directory
896922

897923
end function is_directory
898924

925+
! A Helper function to convert C character arrays to Fortran character strings
926+
function to_f_char(c_str_ptr, len) result(f_str)
927+
type(c_ptr), intent(in) :: c_str_ptr
928+
! length of the string excluding the null character
929+
integer(kind=c_size_t), intent(in) :: len
930+
character(:), allocatable :: f_str
931+
932+
integer :: i
933+
character(kind=c_char), pointer :: c_str(:)
934+
935+
call c_f_pointer(c_str_ptr, c_str, [len])
936+
937+
allocate(character(len=len) :: f_str)
938+
939+
do concurrent (i=1:len)
940+
f_str(i:i) = c_str(i)
941+
end do
942+
end function to_f_char
943+
899944
! A helper function to get the result of the C function `strerror`.
900945
! `strerror` is a function provided by `<string.h>`.
901946
! It returns a string describing the meaning of `errno` in the C header `<errno.h>`
@@ -911,18 +956,11 @@ end function strerror
911956
end interface
912957

913958
type(c_ptr) :: c_str_ptr
914-
integer(c_size_t) :: len, i
915-
character(kind=c_char), pointer :: c_str(:)
959+
integer(c_size_t) :: len
916960

917961
c_str_ptr = strerror(len)
918962

919-
call c_f_pointer(c_str_ptr, c_str, [len])
920-
921-
allocate(character(len=len) :: str)
922-
923-
do concurrent (i=1:len)
924-
str(i:i) = c_str(i)
925-
end do
963+
str = to_f_char(c_str_ptr, len)
926964
end function c_get_strerror
927965

928966
!! makes an empty directory
@@ -1024,6 +1062,56 @@ end function stdlib_remove_directory
10241062

10251063
end subroutine remove_directory
10261064

1065+
subroutine get_cwd(cwd, err)
1066+
character(:), allocatable, intent(out) :: cwd
1067+
type(state_type), optional, intent(out) :: err
1068+
type(state_type) :: err0
1069+
1070+
interface
1071+
type(c_ptr) function stdlib_get_cwd(len, stat) bind(C, name='stdlib_get_cwd')
1072+
import c_ptr, c_size_t
1073+
integer(c_size_t), intent(out) :: len
1074+
integer :: stat
1075+
end function stdlib_get_cwd
1076+
end interface
1077+
1078+
type(c_ptr) :: c_str_ptr
1079+
integer(c_size_t) :: len
1080+
integer :: stat
1081+
1082+
c_str_ptr = stdlib_get_cwd(len, stat)
1083+
1084+
if (stat /= 0) then
1085+
err0 = FS_ERROR_CODE(stat, c_get_strerror())
1086+
call err0%handle(err)
1087+
end if
1088+
1089+
cwd = to_f_char(c_str_ptr, len)
1090+
1091+
end subroutine get_cwd
1092+
1093+
subroutine set_cwd(path, err)
1094+
character(len=*), intent(in) :: path
1095+
type(state_type), optional, intent(out) :: err
1096+
type(state_type) :: err0
1097+
1098+
interface
1099+
integer function stdlib_set_cwd(path) bind(C, name='stdlib_set_cwd')
1100+
import c_char
1101+
character(kind=c_char), intent(in) :: path(*)
1102+
end function stdlib_set_cwd
1103+
end interface
1104+
1105+
integer :: code
1106+
1107+
code = stdlib_set_cwd(to_c_char(trim(path)))
1108+
1109+
if (code /= 0) then
1110+
err0 = FS_ERROR_CODE(code, c_get_strerror())
1111+
call err0%handle(err)
1112+
end if
1113+
end subroutine set_cwd
1114+
10271115
!> Returns the file path of the null device for the current operating system.
10281116
!>
10291117
!> Version: Helper function.
@@ -1042,21 +1130,13 @@ end function process_null_device
10421130

10431131
end interface
10441132

1045-
integer(c_size_t) :: i, len
1133+
integer(c_size_t) :: len
10461134
type(c_ptr) :: c_path_ptr
1047-
character(kind=c_char), pointer :: c_path(:)
10481135

10491136
! Call the C function to get the null device path and its length
10501137
c_path_ptr = process_null_device(len)
1051-
call c_f_pointer(c_path_ptr,c_path,[len])
10521138

1053-
! Allocate the Fortran string with the length returned from C
1054-
allocate(character(len=len) :: path)
1055-
1056-
do concurrent (i=1:len)
1057-
path(i:i) = c_path(i)
1058-
end do
1059-
1139+
path = to_f_char(c_path_ptr, len)
10601140
end function null_device
10611141

10621142
!> Delete a file at the given path.

src/stdlib_system.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
#include <limits.h>
12
#include <stddef.h>
3+
#include <stdlib.h>
24
#include <sys/stat.h>
35
#include <sys/types.h>
46
#include <string.h>
@@ -44,3 +46,48 @@ int stdlib_remove_directory(const char* path){
4446

4547
return (!code) ? 0 : errno;
4648
}
49+
50+
// Wrapper to the platform's `getcwd`(get current working directory) call.
51+
// Uses `getcwd` on unix, `_getcwd` on windows.
52+
// Returns the cwd, sets the length of cwd and the `stat` of the operation.
53+
char* stdlib_get_cwd(size_t* len, int* stat){
54+
*stat = 0;
55+
#ifdef _WIN32
56+
char* buffer;
57+
buffer = _getcwd(NULL, 0);
58+
59+
if (buffer == NULL) {
60+
*stat = errno;
61+
return NULL;
62+
}
63+
64+
*len = strlen(buffer);
65+
return buffer;
66+
#else
67+
char buffer[PATH_MAX + 1];
68+
if (!getcwd(buffer, sizeof(buffer))) {
69+
*stat = errno;
70+
}
71+
72+
*len = strlen(buffer);
73+
74+
char* res = malloc(*len);
75+
strncpy(res, buffer, *len);
76+
77+
return res;
78+
#endif /* ifdef _WIN32 */
79+
}
80+
81+
// Wrapper to the platform's `chdir`(change directory) call.
82+
// Uses `chdir` on unix, `_chdir` on windows.
83+
// Returns 0 if successful, otherwise returns the `errno`.
84+
int stdlib_set_cwd(char* path) {
85+
int code;
86+
#ifdef _WIN32
87+
code = _chdir(path);
88+
#else
89+
code = chdir(path);
90+
#endif /* ifdef _WIN32 */
91+
92+
return (code == -1) ? errno : 0;
93+
}

0 commit comments

Comments
 (0)