diff --git a/.github/collab.sh b/.github/collab.sh old mode 100644 new mode 100755 diff --git a/doc/specs/stdlib_ascii.md b/doc/specs/stdlib_ascii.md index 0bab6e8c3..9c088fbb3 100644 --- a/doc/specs/stdlib_ascii.md +++ b/doc/specs/stdlib_ascii.md @@ -14,13 +14,540 @@ intrinsic character variables and constants. ## Constants provided by `stdlib_ascii` -@note Specification of constants is currently incomplete. +### `NUL` +Null character + +### `SOH` + +Start Of Heading Character + +### `STX` + +Start Of Text character + +### `ETX` + +End Of Text character + +### `EOT` + +End Of Transmission character + +### `ENQ` + +Enquiry character + +### `ACK` + +Acknowledge character + +### `BEL` + +Bell character + +### `BS` + +Backspace character + +### `TAB` + +Horizontal Tab character + +### `LF` + +Line Feed character + +### `VT` + +Vertical Tab character + +### `FF` + +Form Feed character + +### `CR` + +Carriage Return character + +### `SO` + +Shift Out character + +### `SI` + +Shift In character + +### `DLE` + +Data Link Escape character + +### `DC1` + +Device Control 1 character + +### `DC2` + +Device Control 2 character + +### `DC3` + +Device Control 3 character + +### `DC4` + +Device Control 4 character + +### `NAK` + +Negative Acknowledge character + +### `SYN` + +Synchronous Idle character + +### `ETB` + +End of Transmission Block character + +### `CAN` + +Cancel character + +### `EM` + +End of Medium character + +### `SUB` + +Substitute character + +### `ESC` + +Escape character + +### `FS` + +File separator character + +### `GS` + +Group Separator character + +### `RS` + +Record Separator character + +### `US` + +Unit separator character + +### `DEL` + +Delete character + +### `fullhex_digits` + +All the hexadecimal digits (0-9, A-F, a-f) + +### `hex_digits` + +All the numerical and uppercase hexadecimal digits (0-9, A-F) + +### `lowerhex_digits` + +All the numerical and lowercase hexadecimal digits (0-9, a-f) + +### `digits` + +base 10 digits (0-9) + +### `octal_digits` + +base 8 digits (0-7) + +### `letters` + +Uppercase and lowercase letters of the english alphabet (A-Z, a-z) + +### `uppercase` + +Uppercase english albhabets (A-Z) + +### `lowercase` + +Lowercase english albhabets (a-z) + +### `whitespace` + +All the ascii whitespace characters (space, horizontal tab, vertical tab, carriage return, line feed, form feed) ## Specification of the `stdlib_ascii` procedures -@note Specification of procedures is currently incomplete. +### `is_alpha` + +#### Status + +Experimental + +#### Description + +Checks whether input character is an ASCII letter (A-Z, a-z). + +#### Syntax + +`res =` [[stdlib_ascii(module):is_alpha(function)]] `(c)` + +#### Class + +Elemental function. + +#### Argument + +`c`: shall be an intrinsic `character(len=1)` type. It is an `intent(in)` argument. + +#### Result value + +The result is a `logical`. + +### `is_alphanum` + +#### Status + +Experimental + +#### Description + +Checks whether input character is an ASCII letter or a number (A-Z, a-z, 0-9). + +#### Syntax + +`res =` [[stdlib_ascii(module):is_alphanum(function)]] `(c)` + +#### Class + +Elemental function. + +#### Argument + +`c`: shall be an intrinsic `character(len=1)` type. It is an `intent(in)` argument. + +#### Result value + +The result is a `logical`. + +### `is_ascii` + +#### Status + +Experimental + +#### Description + +Checks whether input character is in the ASCII character set i.e in the range 0-128. + +#### Syntax + +`res =` [[stdlib_ascii(module):is_ascii(function)]] `(c)` + +#### Class + +Elemental function. + +#### Argument + +`c`: shall be an intrinsic `character(len=1)` type. It is an `intent(in)` argument. +#### Result value + +The result is a `logical`. + +### `is_control` + +#### Status + +Experimental + +#### Description + +Checks whether input character is a control character. + +#### Syntax + +`res =` [[stdlib_ascii(module):is_control(function)]] `(c)` + +#### Class + +Elemental function. + +#### Argument + +`c`: shall be an intrinsic `character(len=1)` type. It is an `intent(in)` argument. + +#### Result value + +The result is a `logical`. + +### `is_digit` + +#### Status + +Experimental + +#### Description + +Checks whether input character is a digit (0-9). + +#### Syntax + +`res =` [[stdlib_ascii(module):is_digit(function)]] `(c)` + +#### Class + +Elemental function. + +#### Argument + +`c`: shall be an intrinsic `character(len=1)` type. It is an `intent(in)` argument. + +#### Result value + +The result is a `logical`. + +### `is_octal_digit` + +#### Status + +Experimental + +#### Description + +Checks whether input character is an octal digit (0-7) + +#### Syntax + +`res =` [[stdlib_ascii(module):is_octal_digit(function)]] `(c)` + +#### Class + +Elemental function. + +#### Argument + +`c`: shall be an intrinsic `character(len=1)` type. It is an `intent(in)` argument. + +#### Result value + +The result is a `logical`. + +### `is_hex_digit` + +#### Status + +Experimental + +#### Description + +Checks whether input character is a hexadecimal digit (0-9, A-F, a-f). + +#### Syntax + +`res =` [[stdlib_ascii(module):is_hex_digit(function)]] `(c)` + +#### Class + +Elemental function. + +#### Argument + +`c`: shall be an intrinsic `character(len=1)` type. It is an `intent(in)` argument. + +#### Result value + +The result is a `logical`. + +### `is_punctuation` + +#### Status + +Experimental + +#### Description + +Checks whether input character is a punctuation character. + +#### Syntax + +`res =` [[stdlib_ascii(module):is_punctuation(function)]] `(c)` + +#### Class + +Elemental function. + +#### Argument + +`c`: shall be an intrinsic `character(len=1)` type. It is an `intent(in)` argument. + +#### Result value + +The result is a `logical`. + +### `is_graphical` + +#### Status + +Experimental + +#### Description + +Checks whether input character is a graphical character (printable other than the space character). + +#### Syntax + +`res =` [[stdlib_ascii(module):is_graphical(function)]] `(c)` + +#### Class + +Elemental function. + +#### Argument + +`c`: shall be an intrinsic `character(len=1)` type. It is an `intent(in)` argument. + +#### Result value + +The result is a `logical`. + +### `is_printable` + +#### Status + +Experimental + +#### Description + +Checks whether input character is a printable character (including the space character). + +#### Syntax + +`res =` [[stdlib_ascii(module):is_printable(function)]] `(c)` + +#### Class + +Elemental function. + +#### Argument + +`c`: shall be an intrinsic `character(len=1)` type. It is an `intent(in)` argument. + +#### Result value + +The result is a `logical`. + +### `is_lower` + +#### Status + +Experimental + +#### Description + +Checks whether input character is a lowercase ASCII letter (a-z). + +#### Syntax + +`res =` [[stdlib_ascii(module):is_lower(function)]] `(c)` + +#### Class + +Elemental function. + +#### Argument + +`c`: shall be an intrinsic `character(len=1)` type. It is an `intent(in)` argument. + +#### Result value + +The result is a `logical`. + +### `is_upper` + +#### Status + +Experimental + +#### Description + +Checks whether input character is an uppercase ASCII letter (A-Z). + +#### Syntax + +`res =` [[stdlib_ascii(module):is_upper(function)]] `(c)` + +#### Class + +Elemental function. + +#### Argument + +`c`: shall be an intrinsic `character(len=1)` type. It is an `intent(in)` argument. + +#### Result value + +The result is a `logical`. + +### `is_white` + +#### Status + +Experimental + +#### Description + +Checks whether input character is a whitespace character (which includes space, horizontal tab, vertical tab, +carriage return, linefeed and form feed characters) + +#### Syntax + +`res =` [[stdlib_ascii(module):is_white(function)]] `(c)` + +#### Class + +Elemental function. + +#### Argument + +`c`: shall be an intrinsic `character(len=1)` type. It is an `intent(in)` argument. + +#### Result value + +The result is a `logical`. + +### `is_blank` + +#### Status + +Experimental + +#### Description + +Checks whether input character is a blank character (which includes space and tabs). + +#### Syntax + +`res =` [[stdlib_ascii(module):is_blank(function)]] `(c)` + +#### Class + +Elemental function. + +#### Argument + +`c`: shall be an intrinsic `character(len=1)` type. It is an `intent(in)` argument. + +#### Result value + +The result is a `logical`. ### `to_lower` @@ -52,7 +579,7 @@ The result is an intrinsic character type of the same length as `string`. ```fortran {!example/ascii/example_ascii_to_lower.f90!} -``` +``` ### `to_upper` diff --git a/doc/specs/stdlib_system.md b/doc/specs/stdlib_system.md index 56c6f70e6..c6c79fcea 100644 --- a/doc/specs/stdlib_system.md +++ b/doc/specs/stdlib_system.md @@ -535,6 +535,117 @@ The function returns a `logical` value: --- +## `make_directory` - Creates an empty directory + +### Status + +Experimental + +### Description + +It creates an empty directory with default permissions. +It is designed to work across multiple platforms. On Windows, paths with both forward `/` and backward `\` slashes are accepted. + +### Syntax + +`call [[stdlib_system(module):make_directory(subroutine)]] (path [,err])` + +### Class + +Subroutine + +### Arguments + +`path`: Shall be a character string containing the path of the directory to create. It is an `intent(in)` argument. + +`err`(optional): Shall be of type `state_type`, and is used for error handling. It is an `optional, intent(out)` argument. + +### Return values + +`err` is an optional state return flag. If not requested and an error occurs, an `FS_ERROR` will trigger an error stop. + +### Example + +```fortran +{!example/system/example_make_directory.f90!} +``` + +--- + +## `make_directory_all` - Creates an empty directory with all its parent directories + +### Status + +Experimental + +### Description + +It creates an empty directory with default permissions. +It also creates all the necessary parent directories in the path if they do not exist already. + +### Syntax + +`call [[stdlib_system(module):make_directory_all(subroutine)]] (path [,err])` + +### Class + +Subroutine + +### Arguments + +`path`: Shall be a character string containing the path of the directory to create. It is an `intent(in)` argument. + +`err`(optional): Shall be of type `state_type`, and is used for error handling. It is an `optional, intent(out)` argument. + +### Return values + +`err` is an optional state return flag. If not requested and an error occurs, an `FS_ERROR` will trigger an error stop. + +### Example + +```fortran +{!example/system/example_make_directory.f90!} +``` + +--- + +## `remove_directory` - Removes an empty directory + +### Status + +Experimental + +### Description + +It deletes an empty directory. +It is designed to work across multiple platforms. On Windows, paths with both forward `/` and backward `\` slashes are accepted. + +### Syntax + +`call [[stdlib_system(module):remove_directory(subroutine)]] (path, err)` + +### Class + +Subroutine + +### Arguments + +`path`: Shall be a character string containing the path of the directory to create. It is an `intent(in)` argument. + +`err`(optional): Shall be of type `state_type`, and is used for error handling. It is an `optional, intent(out)` argument. + +### Return values + +`err` is an optional state return flag. On error if not requested, an `FS_ERROR` will trigger an error stop. + +### Example + +```fortran +{!example/system/example_remove_directory.f90!} +``` + +--- + ## `null_device` - Return the null device file path ### Status diff --git a/example/system/CMakeLists.txt b/example/system/CMakeLists.txt index 57ec0c737..ea2408f11 100644 --- a/example/system/CMakeLists.txt +++ b/example/system/CMakeLists.txt @@ -16,4 +16,5 @@ ADD_EXAMPLE(path_join) ADD_EXAMPLE(path_split_path) ADD_EXAMPLE(path_base_name) ADD_EXAMPLE(path_dir_name) - +ADD_EXAMPLE(make_directory) +ADD_EXAMPLE(remove_directory) \ No newline at end of file diff --git a/example/system/example_make_directory.f90 b/example/system/example_make_directory.f90 new file mode 100644 index 000000000..e33aab730 --- /dev/null +++ b/example/system/example_make_directory.f90 @@ -0,0 +1,25 @@ +! Illustrate the usage of `make_directory`, `make_directory_all` +program example_make_directory + use stdlib_system, only: make_directory, make_directory_all + use stdlib_error, only: state_type + implicit none + + type(state_type) :: err + + call make_directory("temp_dir", err) + + if (err%error()) then + print *, err%print() + else + print *, "directory created sucessfully" + end if + + call make_directory_all("d1/d2/d3/d4", err) + + if (err%error()) then + print *, err%print() + else + print *, "nested directories created sucessfully" + end if + +end program example_make_directory diff --git a/example/system/example_remove_directory.f90 b/example/system/example_remove_directory.f90 new file mode 100644 index 000000000..03465312d --- /dev/null +++ b/example/system/example_remove_directory.f90 @@ -0,0 +1,17 @@ +! Illustrate the usage of `remove_directory` +program example_remove_directory + use stdlib_system, only: remove_directory + use stdlib_error, only: state_type + implicit none + + type(state_type) :: err + + call remove_directory("directory_to_be_removed", err) + + if (err%error()) then + print *, err%print() + else + print *, "directory removed successfully" + end if + +end program example_remove_directory diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7d52e7fcd..98c43a1ff 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -118,6 +118,7 @@ set(SRC stdlib_system_subprocess.c stdlib_system_subprocess.F90 stdlib_system_path.f90 + stdlib_system.c stdlib_system.F90 stdlib_sparse.f90 stdlib_specialfunctions_legendre.f90 diff --git a/src/stdlib_ascii.fypp b/src/stdlib_ascii.fypp index 7e5eec963..fa062900d 100644 --- a/src/stdlib_ascii.fypp +++ b/src/stdlib_ascii.fypp @@ -107,13 +107,13 @@ module stdlib_ascii contains !> Checks whether `c` is an ASCII letter (A .. Z, a .. z). - pure logical function is_alpha(c) + elemental logical function is_alpha(c) character(len=1), intent(in) :: c !! The character to test. is_alpha = (c >= 'A' .and. c <= 'Z') .or. (c >= 'a' .and. c <= 'z') end function !> Checks whether `c` is a letter or a number (0 .. 9, a .. z, A .. Z). - pure logical function is_alphanum(c) + elemental logical function is_alphanum(c) character(len=1), intent(in) :: c !! The character to test. is_alphanum = (c >= '0' .and. c <= '9') .or. (c >= 'a' .and. c <= 'z') & .or. (c >= 'A' .and. c <= 'Z') @@ -121,13 +121,13 @@ contains !> Checks whether or not `c` is in the ASCII character set - !> i.e. in the range 0 .. 0x7F. - pure logical function is_ascii(c) + elemental logical function is_ascii(c) character(len=1), intent(in) :: c !! The character to test. is_ascii = iachar(c) <= int(z'7F') end function !> Checks whether `c` is a control character. - pure logical function is_control(c) + elemental logical function is_control(c) character(len=1), intent(in) :: c !! The character to test. integer :: ic ic = iachar(c) @@ -135,19 +135,19 @@ contains end function !> Checks whether `c` is a digit (0 .. 9). - pure logical function is_digit(c) + elemental logical function is_digit(c) character(len=1), intent(in) :: c !! The character to test. is_digit = ('0' <= c) .and. (c <= '9') end function !> Checks whether `c` is a digit in base 8 (0 .. 7). - pure logical function is_octal_digit(c) + elemental logical function is_octal_digit(c) character(len=1), intent(in) :: c !! The character to test. is_octal_digit = (c >= '0') .and. (c <= '7'); end function !> Checks whether `c` is a digit in base 16 (0 .. 9, A .. F, a .. f). - pure logical function is_hex_digit(c) + elemental logical function is_hex_digit(c) character(len=1), intent(in) :: c !! The character to test. is_hex_digit = (c >= '0' .and. c <= '9') .or. (c >= 'a' .and. c <= 'f') & .or. (c >= 'A' .and. c <= 'F') @@ -156,7 +156,7 @@ contains !> Checks whether or not `c` is a punctuation character. That includes !> all ASCII characters which are not control characters, letters, !> digits, or whitespace. - pure logical function is_punctuation(c) + elemental logical function is_punctuation(c) character(len=1), intent(in) :: c !! The character to test. integer :: ic ic = iachar(c) ! '~' '!' @@ -166,7 +166,7 @@ contains !> Checks whether or not `c` is a printable character other than the !> space character. - pure logical function is_graphical(c) + elemental logical function is_graphical(c) character(len=1), intent(in) :: c !! The character to test. integer :: ic ic = iachar(c) @@ -177,7 +177,7 @@ contains !> Checks whether or not `c` is a printable character - including the !> space character. - pure logical function is_printable(c) + elemental logical function is_printable(c) character(len=1), intent(in) :: c !! The character to test. integer :: ic ic = iachar(c) @@ -186,7 +186,7 @@ contains end function !> Checks whether `c` is a lowercase ASCII letter (a .. z). - pure logical function is_lower(c) + elemental logical function is_lower(c) character(len=1), intent(in) :: c !! The character to test. integer :: ic ic = iachar(c) @@ -194,7 +194,7 @@ contains end function !> Checks whether `c` is an uppercase ASCII letter (A .. Z). - pure logical function is_upper(c) + elemental logical function is_upper(c) character(len=1), intent(in) :: c !! The character to test. is_upper = (c >= 'A') .and. (c <= 'Z') end function @@ -202,7 +202,7 @@ contains !> Checks whether or not `c` is a whitespace character. That includes the !> space, tab, vertical tab, form feed, carriage return, and linefeed !> characters. - pure logical function is_white(c) + elemental logical function is_white(c) character(len=1), intent(in) :: c !! The character to test. integer :: ic ic = iachar(c) ! TAB, LF, VT, FF, CR @@ -211,7 +211,7 @@ contains !> Checks whether or not `c` is a blank character. That includes the !> only the space and tab characters - pure logical function is_blank(c) + elemental logical function is_blank(c) character(len=1), intent(in) :: c !! The character to test. integer :: ic ic = iachar(c) ! TAB diff --git a/src/stdlib_intrinsics_dot_product.fypp b/src/stdlib_intrinsics_dot_product.fypp index ce6188c8a..74bb4b4de 100644 --- a/src/stdlib_intrinsics_dot_product.fypp +++ b/src/stdlib_intrinsics_dot_product.fypp @@ -34,10 +34,10 @@ pure module function stdlib_dot_product_${s}$(a,b) result(p) n = size(a,kind=ilp) r = mod(n,chunk) - abatch(1:r) = a(1:r)*${cnjg(t,'b(1:r)')}$ + abatch(1:r) = ${cnjg(t,'a(1:r)')}$*b(1:r) abatch(r+1:chunk) = zero_${s}$ do i = r+1, n-r, chunk - abatch(1:chunk) = abatch(1:chunk) + a(i:i+chunk-1)*${cnjg(t,'b(i:i+chunk-1)')}$ + abatch(1:chunk) = abatch(1:chunk) + ${cnjg(t,'a(i:i+chunk-1)')}$*b(i:i+chunk-1) end do p = zero_${s}$ @@ -60,11 +60,11 @@ pure module function stdlib_dot_product_kahan_${s}$(a,b) result(p) n = size(a,kind=ilp) r = mod(n,chunk) - abatch(1:r) = a(1:r)*${cnjg(t,'b(1:r)')}$ + abatch(1:r) = ${cnjg(t,'a(1:r)')}$*b(1:r) abatch(r+1:chunk) = zero_${s}$ cbatch = zero_${s}$ do i = r+1, n-r, chunk - call kahan_kernel( a(i:i+chunk-1)*${cnjg(t,'b(i:i+chunk-1)')}$ , abatch(1:chunk) , cbatch(1:chunk) ) + call kahan_kernel( ${cnjg(t,'a(i:i+chunk-1)')}$*b(i:i+chunk-1) , abatch(1:chunk) , cbatch(1:chunk) ) end do p = zero_${s}$ diff --git a/src/stdlib_system.F90 b/src/stdlib_system.F90 index bf8c9f0c7..bd6f9b001 100644 --- a/src/stdlib_system.F90 +++ b/src/stdlib_system.F90 @@ -2,8 +2,9 @@ module stdlib_system use, intrinsic :: iso_c_binding, only : c_int, c_long, c_ptr, c_null_ptr, c_int64_t, c_size_t, & c_f_pointer use stdlib_kinds, only: int64, dp, c_bool, c_char -use stdlib_strings, only: to_c_char, to_string +use stdlib_strings, only: to_c_char, find use stdlib_string_type, only: string_type +use stdlib_optval, only: optval use stdlib_error, only: state_type, STDLIB_SUCCESS, STDLIB_FS_ERROR implicit none private @@ -109,6 +110,52 @@ module stdlib_system !! public :: is_directory +!! version: experimental +!! +!! Makes an empty directory. +!! ([Specification](../page/specs/stdlib_system.html#make_directory)) +!! +!! ### Summary +!! Creates an empty directory with default permissions. +!! +!! ### Description +!! This function makes an empty directory according to the path provided. +!! Relative paths are supported. On Windows, paths involving either `/` or `\` are accepted. +!! An appropriate error message is returned whenever any error occurs. +!! +public :: make_directory + +!! version: experimental +!! +!! Makes an empty directory, also creating all the parent directories required. +!! ([Specification](../page/specs/stdlib_system.html#make_directory)) +!! +!! ### Summary +!! Creates an empty directory with all the parent directories required to do so. +!! +!! ### Description +!! This function makes an empty directory according to the path provided. +!! It also creates all the necessary parent directories in the path if they do not exist already. +!! Relative paths are supported. +!! An appropriate error message is returned whenever any error occurs. +!! +public :: make_directory_all + +!! version: experimental +!! +!! Removes an empty directory. +!! ([Specification](../page/specs/stdlib_system.html#remove_directory)) +!! +!! ### Summary +!! Removes an empty directory. +!! +!! ### Description +!! This function Removes an empty directory according to the path provided. +!! Relative paths are supported. On Windows paths involving either `/` or `\` are accepted. +!! An appropriate error message is returned whenever any error occurs. +!! +public :: remove_directory + !! version: experimental !! !! Deletes a specified file from the filesystem. @@ -849,6 +896,134 @@ end function stdlib_is_directory end function is_directory +! A helper function to get the result of the C function `strerror`. +! `strerror` is a function provided by ``. +! It returns a string describing the meaning of `errno` in the C header `` +function c_get_strerror() result(str) + character(len=:), allocatable :: str + + interface + type(c_ptr) function strerror(len) bind(C, name='stdlib_strerror') + import c_size_t, c_ptr + implicit none + integer(c_size_t), intent(out) :: len + end function strerror + end interface + + type(c_ptr) :: c_str_ptr + integer(c_size_t) :: len, i + character(kind=c_char), pointer :: c_str(:) + + c_str_ptr = strerror(len) + + call c_f_pointer(c_str_ptr, c_str, [len]) + + allocate(character(len=len) :: str) + + do concurrent (i=1:len) + str(i:i) = c_str(i) + end do +end function c_get_strerror + +!! makes an empty directory +subroutine make_directory(path, err) + character(len=*), intent(in) :: path + type(state_type), optional, intent(out) :: err + + integer :: code + type(state_type) :: err0 + + interface + integer function stdlib_make_directory(cpath) bind(C, name='stdlib_make_directory') + import c_char + character(kind=c_char), intent(in) :: cpath(*) + end function stdlib_make_directory + end interface + + code = stdlib_make_directory(to_c_char(trim(path))) + + if (code /= 0) then + err0 = FS_ERROR_CODE(code, c_get_strerror()) + call err0%handle(err) + end if + +end subroutine make_directory + +subroutine make_directory_all(path, err) + character(len=*), intent(in) :: path + type(state_type), optional, intent(out) :: err + + integer :: i, indx + type(state_type) :: err0 + character(len=1) :: sep + logical :: is_dir, check_is_dir + + sep = path_sep() + i = 1 + indx = find(path, sep, i) + check_is_dir = .true. + + do + ! Base case to exit the loop + if (indx == 0) then + is_dir = is_directory(path) + + if (.not. is_dir) then + call make_directory(path, err0) + + if (err0%error()) then + call err0%handle(err) + end if + end if + + return + end if + + if (check_is_dir) then + is_dir = is_directory(path(1:indx)) + end if + + if (.not. is_dir) then + ! no need for further `is_dir` checks + ! all paths going forward need to be created + check_is_dir = .false. + call make_directory(path(1:indx), err0) + + if (err0%error()) then + call err0%handle(err) + return + end if + end if + + i = i + 1 ! the next occurence of `sep` + indx = find(path, sep, i) + end do +end subroutine make_directory_all + +!! removes an empty directory +subroutine remove_directory(path, err) + character(len=*), intent(in) :: path + type(state_type), optional, intent(out) :: err + + integer :: code + type(state_type) :: err0 + + interface + integer function stdlib_remove_directory(cpath) bind(C, name='stdlib_remove_directory') + import c_char + character(kind=c_char), intent(in) :: cpath(*) + end function stdlib_remove_directory + end interface + + code = stdlib_remove_directory(to_c_char(trim(path))) + + if (code /= 0) then + err0 = FS_ERROR_CODE(code, c_get_strerror()) + call err0%handle(err) + end if + +end subroutine remove_directory + !> Returns the file path of the null device for the current operating system. !> !> Version: Helper function. diff --git a/src/stdlib_system.c b/src/stdlib_system.c new file mode 100644 index 000000000..0bef82b8c --- /dev/null +++ b/src/stdlib_system.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#else +#include +#endif /* ifdef _WIN32 */ + +// Returns the string describing the meaning of `errno` code (by calling `strerror`). +char* stdlib_strerror(size_t* len){ + char* err = strerror(errno); + *len = strlen(err); + return err; +} + +// Wrapper to the platform's `mkdir`(make directory) call. +// Uses `mkdir` on unix, `_mkdir` on windows. +// Returns 0 if successful, otherwise returns the `errno`. +int stdlib_make_directory(const char* path){ + int code; +#ifdef _WIN32 + code = _mkdir(path); +#else + // Default mode 0777 + code = mkdir(path, 0777); +#endif /* ifdef _WIN32 */ + + return (!code) ? 0 : errno; +} + +// Wrapper to the platform's `rmdir`(remove directory) call. +// Uses `rmdir` on unix, `_rmdir` on windows. +// Returns 0 if successful, otherwise returns the `errno`. +int stdlib_remove_directory(const char* path){ + int code; +#ifdef _WIN32 + code = _rmdir(path); +#else + code = rmdir(path); +#endif /* ifdef _WIN32 */ + + return (!code) ? 0 : errno; +} diff --git a/src/stdlib_system_path.f90 b/src/stdlib_system_path.f90 index c2ad1aec8..5ec7ef8c8 100644 --- a/src/stdlib_system_path.f90 +++ b/src/stdlib_system_path.f90 @@ -1,6 +1,6 @@ submodule(stdlib_system) stdlib_system_path use stdlib_ascii, only: reverse - use stdlib_strings, only: chomp, find, join + use stdlib_strings, only: chomp, join use stdlib_string_type, only: string_type, char, move contains module function join2_char_char(p1, p2) result(path) diff --git a/test/ascii/test_ascii.f90 b/test/ascii/test_ascii.f90 index 5a8878632..94b11d6ee 100644 --- a/test/ascii/test_ascii.f90 +++ b/test/ascii/test_ascii.f90 @@ -52,6 +52,7 @@ subroutine collect_ascii(testsuite) new_unittest("to_lower_long", test_to_lower_long), & new_unittest("to_upper_short", test_to_upper_short), & new_unittest("to_upper_long", test_to_upper_long), & + new_unittest("ascii_table", test_ascii_table), & new_unittest("to_upper_string", test_to_upper_string), & new_unittest("to_lower_string", test_to_lower_string), & new_unittest("to_title_string", test_to_title_string), & @@ -725,52 +726,27 @@ subroutine test_to_upper_long(error) ! This test reproduces the true/false table found at ! https://en.cppreference.com/w/cpp/string/byte ! - subroutine test_ascii_table + subroutine ascii_table(table) + logical, intent(out) :: table(15,12) integer :: i, j - logical :: table(15,12) - - abstract interface - pure logical function validation_func_interface(c) - character(len=1), intent(in) :: c - end function - end interface - - type :: proc_pointer_array - procedure(validation_func_interface), pointer, nopass :: pcf - end type proc_pointer_array - - type(proc_pointer_array) :: pcfs(12) - - pcfs(1)%pcf => is_control - pcfs(2)%pcf => is_printable - pcfs(3)%pcf => is_white - pcfs(4)%pcf => is_blank - pcfs(5)%pcf => is_graphical - pcfs(6)%pcf => is_punctuation - pcfs(7)%pcf => is_alphanum - pcfs(8)%pcf => is_alpha - pcfs(9)%pcf => is_upper - pcfs(10)%pcf => is_lower - pcfs(11)%pcf => is_digit - pcfs(12)%pcf => is_hex_digit ! loop through functions do i = 1, 12 - table(1,i) = all([(pcfs(i)%pcf(achar(j)),j=0,8)]) ! control codes - table(2,i) = pcfs(i)%pcf(achar(9)) ! tab - table(3,i) = all([(pcfs(i)%pcf(achar(j)),j=10,13)]) ! whitespaces - table(4,i) = all([(pcfs(i)%pcf(achar(j)),j=14,31)]) ! control codes - table(5,i) = pcfs(i)%pcf(achar(32)) ! space - table(6,i) = all([(pcfs(i)%pcf(achar(j)),j=33,47)]) ! !"#$%&'()*+,-./ - table(7,i) = all([(pcfs(i)%pcf(achar(j)),j=48,57)]) ! 0123456789 - table(8,i) = all([(pcfs(i)%pcf(achar(j)),j=58,64)]) ! :;<=>?@ - table(9,i) = all([(pcfs(i)%pcf(achar(j)),j=65,70)]) ! ABCDEF - table(10,i) = all([(pcfs(i)%pcf(achar(j)),j=71,90)]) ! GHIJKLMNOPQRSTUVWXYZ - table(11,i) = all([(pcfs(i)%pcf(achar(j)),j=91,96)]) ! [\]^_` - table(12,i) = all([(pcfs(i)%pcf(achar(j)),j=97,102)]) ! abcdef - table(13,i) = all([(pcfs(i)%pcf(achar(j)),j=103,122)]) ! ghijklmnopqrstuvwxyz - table(14,i) = all([(pcfs(i)%pcf(achar(j)),j=123,126)]) ! {|}~ - table(15,i) = pcfs(i)%pcf(achar(127)) ! backspace character + table(1,i) = all([(validate(j,i), j=0,8)]) + table(2,i) = validate(9,i) + table(3,i) = all([(validate(j,i), j=10,13)]) + table(4,i) = all([(validate(j,i), j=14,31)]) + table(5,i) = validate(32,i) + table(6,i) = all([(validate(j,i), j=33,47)]) + table(7,i) = all([(validate(j,i), j=48,57)]) + table(8,i) = all([(validate(j,i), j=58,64)]) + table(9,i) = all([(validate(j,i), j=65,70)]) + table(10,i) = all([(validate(j,i), j=71,90)]) + table(11,i) = all([(validate(j,i), j=91,96)]) + table(12,i) = all([(validate(j,i), j=97,102)]) + table(13,i) = all([(validate(j,i), j=103,122)]) + table(14,i) = all([(validate(j,i), j=123,126)]) + table(15,i) = validate(127,i) end do ! output table for verification @@ -779,6 +755,59 @@ pure logical function validation_func_interface(c) write(*,'(I3,2X,12(L4),2X,I3)') j, (table(j,i),i=1,12), count(table(j,:)) end do write(*,'(5X,12(I4))') (count(table(:,i)),i=1,12) + + contains + + elemental logical function validate(ascii_code, func) + integer, intent(in) :: ascii_code, func + character(len=1) :: c + + c = achar(ascii_code) + + select case (func) + case (1); validate = is_control(c) + case (2); validate = is_printable(c) + case (3); validate = is_white(c) + case (4); validate = is_blank(c) + case (5); validate = is_graphical(c) + case (6); validate = is_punctuation(c) + case (7); validate = is_alphanum(c) + case (8); validate = is_alpha(c) + case (9); validate = is_upper(c) + case (10); validate = is_lower(c) + case (11); validate = is_digit(c) + case (12); validate = is_hex_digit(c) + case default; validate = .false. + end select + end function validate + + end subroutine ascii_table + + subroutine test_ascii_table(error) + type(error_type), allocatable, intent(out) :: error + logical :: arr(15, 12) + logical, parameter :: ascii_class_table(15,12) = transpose(reshape([ & + ! iscntrl isprint isspace isblank isgraph ispunct isalnum isalpha isupper islower isdigit isxdigit + .true., .false., .false., .false., .false., .false., .false., .false., .false., .false., .false., .false., & ! 0–8 + .true., .false., .true., .true., .false., .false., .false., .false., .false., .false., .false., .false., & ! 9 + .true., .false., .true., .false., .false., .false., .false., .false., .false., .false., .false., .false., & ! 10–13 + .true., .false., .false., .false., .false., .false., .false., .false., .false., .false., .false., .false., & ! 14–31 + .false., .true., .true., .true., .false., .false., .false., .false., .false., .false., .false., .false., & ! 32 (space) + .false., .true., .false., .false., .true., .true., .false., .false., .false., .false., .false., .false., & ! 33–47 + .false., .true., .false., .false., .true., .false., .true., .false., .false., .false., .true., .true., & ! 48–57 + .false., .true., .false., .false., .true., .true., .false., .false., .false., .false., .false., .false., & ! 58–64 + .false., .true., .false., .false., .true., .false., .true., .true., .true., .false., .false., .true., & ! 65–70 + .false., .true., .false., .false., .true., .false., .true., .true., .true., .false., .false., .false., & ! 71–90 + .false., .true., .false., .false., .true., .true., .false., .false., .false., .false., .false., .false., & ! 91–96 + .false., .true., .false., .false., .true., .false., .true., .true., .false., .true., .false., .true., & ! 97–102 + .false., .true., .false., .false., .true., .false., .true., .true., .false., .true., .false., .false., & ! 103–122 + .false., .true., .false., .false., .true., .true., .false., .false., .false., .false., .false., .false., & ! 123–126 + .true., .false., .false., .false., .false., .false., .false., .false., .false., .false., .false., .false. & ! 127 + ], shape=[12,15])) + + call ascii_table(arr) + call check(error, all(arr .eqv. ascii_class_table), "ascii table was not accurately generated") + end subroutine test_ascii_table subroutine test_to_lower_string(error) diff --git a/test/intrinsics/test_intrinsics.fypp b/test/intrinsics/test_intrinsics.fypp index 8aefe09d3..11cf32fdc 100644 --- a/test/intrinsics/test_intrinsics.fypp +++ b/test/intrinsics/test_intrinsics.fypp @@ -246,6 +246,27 @@ subroutine test_dot_product(error) call check(error, all(err(:)