https://www.cdslab.org/paramonte/fortran/2
Current view: top level - main - pm_sysShell@routines.F90 (source / functions) Hit Total Coverage
Test: ParaMonte 2.0.0 :: Serial Fortran - Code Coverage Report Lines: 169 184 91.8 %
Date: 2024-04-08 03:18:57 Functions: 23 23 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
       2             : !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
       3             : !!!!                                                                                                                            !!!!
       4             : !!!!    ParaMonte: Parallel Monte Carlo and Machine Learning Library.                                                           !!!!
       5             : !!!!                                                                                                                            !!!!
       6             : !!!!    Copyright (C) 2012-present, The Computational Data Science Lab                                                          !!!!
       7             : !!!!                                                                                                                            !!!!
       8             : !!!!    This file is part of the ParaMonte library.                                                                             !!!!
       9             : !!!!                                                                                                                            !!!!
      10             : !!!!    LICENSE                                                                                                                 !!!!
      11             : !!!!                                                                                                                            !!!!
      12             : !!!!       https://github.com/cdslaborg/paramonte/blob/main/LICENSE.md                                                          !!!!
      13             : !!!!                                                                                                                            !!!!
      14             : !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
      15             : !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
      16             : 
      17             : !>  \brief
      18             : !>  This file contains procedure implementations of [pm_sysShell](@ref pm_sysShell).
      19             : !>
      20             : !>  \finmain
      21             : !>
      22             : !>  \author
      23             : !>  \FatemehBagheri, Wednesday 12:20 AM, October 13, 2021, Dallas, TX
      24             : 
      25             : !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      26             : 
      27             : submodule (pm_sysShell) routines ! LCOV_EXCL_LINE
      28             : 
      29             : #if CHECK_ENABLED
      30             :     use pm_err, only: getFine
      31             :     use pm_val2str, only: getStr
      32             :     use pm_err, only: setAsserted
      33             : #define CHECK_ASSERTION(LINE,ASSERTION,MSG) \
      34             : call setAsserted(ASSERTION,getFine(__FILE__,LINE)//MODULE_NAME//MSG);
      35             : #else
      36             : #define CHECK_ASSERTION(LINE,ASSERTION,MSG) continue;
      37             : #endif
      38             : 
      39             :     use pm_strASCII, only: getStrLower
      40             :     use pm_io, only: setContentsFrom
      41             :     use pm_io, only: LEN_IOMSG
      42             : 
      43             :     implicit none
      44             : 
      45             : !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      46             : 
      47             :     !>  \brief
      48             :     !>  The scalar `private` module object of type [shell_type](@ref pm_sysShell::shell_type).
      49             :     !>
      50             :     !>  \details
      51             :     !>  This object is set only once throughout the life of the program to avoid costly redundant construction of shell objects.<br>
      52             :     !>  It is set by and exclusively used within the routines of this submodule and nowhere else.<br>
      53             :     !>  The allocation status of the object is used as an indicator of its initialization.<br>
      54             :     !>
      55             :     !>  \finmain{mc_shell}
      56             :     !>
      57             :     !>  \author
      58             :     !>  \AmirShahmoradi, Tuesday March 7, 2017, 3:50 AM, Institute for Computational Engineering and Sciences (ICES), The University of Texas at Austin
      59             :     type, extends(shell_type) :: shellinit_type
      60             :         logical(LK) :: initialized = .false._LK
      61             :     end type
      62             :     type(shellinit_type) :: mc_shell
      63             : 
      64             :     !>  \brief
      65             :     !>  The scalar `private` module object of type `logical` of default kind \LK.
      66             :     !>
      67             :     !>  \details
      68             :     !>  This scalar runtime constant is used to indicate whether the scalar runtime constant [mc_shell](@ref pm_sysShell::mc_shell) is set.<br>
      69             :     !>  This indicator could have readily been the allocation status of an `allocatable` mc_shell.<br>
      70             :     !>  However, gfortran as of V. 13 appears to have bug not being able to set the components of the object correctly.<br>
      71             :     !>
      72             :     !>  \finmain{mc_shellSet}
      73             :     !>
      74             :     !>  \author
      75             :     !>  \AmirShahmoradi, Tuesday March 7, 2017, 3:50 AM, Institute for Computational Engineering and Sciences (ICES), The University of Texas at Austin
      76             :     !logical(LK) :: mc_shellSet = .false._LK
      77             : 
      78             : !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      79             : 
      80             :     !>  \brief
      81             :     !>  The `allocatable` scalar of type `character` of default kind \SK containing the path to the system temporary directory.<br>
      82             :     !>  This runtime module constant is meant to be allocated and set once at runtime and used later throughout the program.<br>
      83             :     character(:, SK), save, allocatable :: mc_sysDirTemp
      84             : 
      85             : !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      86             : 
      87             : contains
      88             : 
      89             : !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      90             : 
      91             :     !function isFailedInitShell(errmsg) result(failed)
      92             :     !    use pm_kind, only: SKC => SK
      93             :     !    character(*,SKC), intent(inout), optional :: errmsg
      94             :     !    logical(LK) :: failed
      95             :     !    failed = .not. allocated(mc_shell)
      96             :     !    if (failed) then
      97             :     !        if (present(errmsg)) then
      98             :     !            allocate(mc_shell, source = shell_type(failed, errmsg))
      99             :     !        else
     100             :     !            allocate(mc_shell, source = shell_type(failed))
     101             :     !        end if
     102             :     !        !mc_shellSet = .not. failed
     103             :     !        if (failed .and. allocated(mc_shell)) deallocate(mc_shell)
     104             :     !    end if
     105             :     !end function
     106             : 
     107             : !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     108             : 
     109          45 :     module procedure isFailedGetDirTemp
     110             :         character(LEN_IOMSG, SK) :: errmsg
     111          45 :         errmsg = SK_""
     112          45 :         failed = isFailedGetDirTemp(dirTemp, errmsg)
     113          45 :     end procedure
     114             : 
     115             :     !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     116             : 
     117      387620 :     module procedure isFailedGetDirTempMsg
     118             :         use pm_kind, only: SKC => SK
     119             :         use pm_sysPath, only: isDir
     120             :         character(*, SK), parameter :: PROCEDURE_NAME = MODULE_NAME//SK_"@isFailedGetDirTemp()"
     121             :         integer(IK), parameter :: MAXLEN = max(len(VARENV_DIRTEMP_UNIX), len(VARENV_DIRTEMP_WINDOWS))
     122             :         character(MAXLEN, SK), parameter :: VARENV_DIRTEMP(*) = [character(MAXLEN,SKC) :: VARENV_DIRTEMP_UNIX, VARENV_DIRTEMP_WINDOWS]
     123             :         integer(IK) :: ienv
     124             : #if     OMP_ENABLED
     125             :         !$omp critical
     126             : #endif
     127      387212 :         if (allocated(mc_sysDirTemp)) then
     128      387076 :             dirTemp = mc_sysDirTemp
     129             :             failed = .false._LK
     130             :         else
     131             :             ! We cannot infer the runtime shell through `isShellWindows` or similar functions as they create unlimited circular recursive calls.
     132             :             ! Instead, we check all environmental variables for all shells, from posix to windows.
     133         952 :             do ienv = 1_IK, size(VARENV_DIRTEMP, 1, IK)
     134         816 :                 failed = isFailedGetEnvVar(trim(VARENV_DIRTEMP(ienv)), dirTemp, errmsg) ! returns empty upon error
     135         816 :                 failed = failed .or. logical(len_trim(dirTemp, IK) == 0_IK, LK)
     136         136 :                 if (.not. failed) exit
     137             :             end do
     138             :             ! One last try.
     139         136 :             if (failed) dirTemp = SKC_"/tmp"
     140         136 :             failed = .not. isDir(dirTemp, ienv, errmsg)
     141         136 :             failed = failed .or. ienv /= 0_IK
     142         136 :             if (.not. failed) mc_sysDirTemp = dirTemp
     143             :         end if
     144             : #if     OMP_ENABLED
     145             :         !$omp end critical
     146             : #endif
     147      387212 :     end procedure
     148             :     !#if         INTEL_ENABLED
     149             :     !#define     FILE_ARG directory = SKC_"/tmp/"
     150             :     !#else
     151             :     !#define     FILE_ARG file = SKC_"/tmp/"
     152             :     !#endif
     153             :     !            inquire(FILE_ARG, exist = exists, iostat = i, iomsg = errmsg) ! last forward slash in FILE_ARG is significant.
     154             :     !#undef      FILE_ARG
     155             :     !            failed = logical(i /= 0_IK, LK) .or. .not. exists
     156             :     !            if (failed) then
     157             :     !                errmsg = PROCEDURE_NAME//SK_": Failed to fetch shell temporary dir. "//trim(errmsg) ! LCOV_EXCL_LINE
     158             :     !                return ! LCOV_EXCL_LINE
     159             :     !            end if
     160             : 
     161             : !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     162             : 
     163      192810 :     module procedure isFailedGetOutput
     164             :         character(LEN_IOMSG, SK) :: errmsg
     165      192810 :         errmsg = SK_""
     166      192810 :         failed = isFailedGetOutput(command, output, errmsg)
     167      192810 :     end procedure
     168             : 
     169             :     !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     170             : 
     171      579838 :     module procedure isFailedGetOutputMsg
     172             :         use pm_err, only: getLine
     173             :         use pm_kind, only: SKC => SK
     174             :         use pm_str, only: isEndedWith
     175             :         use pm_arrayStrip, only: getStripped
     176             :         use pm_parallelism, only: getImageID
     177             :         use pm_distUnif, only: setUnifRand, xoshiro256ssw_type
     178             :         type(xoshiro256ssw_type)        :: rng
     179             :         character(*, SK), parameter     :: NLC = new_line(SKC_"a"), LF = char(10, SKC), CR = char(13, SKC)
     180             :         character(*, SK), parameter     :: PROCEDURE_NAME = MODULE_NAME//SK_"@isFailedGetOutputMsg()"
     181      193280 :         character(:,SKC), allocatable   :: filetemp, basetemp
     182             :         integer(IK)     , parameter     :: lenNLC = len(NLC, IK)
     183             :         integer(IK)     , parameter     :: NTRY = 100000_IK
     184             :        !integer(IK)                     :: lenBaseTempPlus1
     185             :         integer(IK)                     :: lenBaseTemp
     186             :         integer(IK)                     :: lenFileTemp
     187             :         integer(IK)                     :: counter
     188             :        !integer(IK)                     :: length
     189             :         integer(IK)                     :: iostat
     190             : #define SET_ERRMSG(LINE)\
     191             : errmsg = PROCEDURE_NAME//getLine(LINE)//SK_": Failed to fetch command output. "//trim(errmsg)
     192             :         ! Determine the shell type.
     193             : 
     194             :         !type(shell_type) :: shell
     195             :         !shell = shell_type(failed, errmsg)
     196             :         !RETURN_IF(failed) ! fpp
     197             : 
     198             :         ! Fetch the system temporary dir and create the base of the path.
     199             : 
     200      386560 :         failed = isFailedGetDirTemp(basetemp, errmsg)
     201      193280 :         if (failed) then
     202           0 :             basetemp = SKC_"isFailedGetOutputMsg.tmp."
     203             :         else
     204             :         !basetemp = basetemp//shell%dirsep//SKC_"isFailedGetOutputMsg.tmp"
     205             :             !   \todo
     206             :             !   \warning
     207             :             !   The following assumes that all platforms and runtime shells recognize forward slash as a directory separator.
     208             :             !   While this is currently the case, it may not be so in the future. A more robust solution may be necessary.
     209             :             !   One solution is to remove dependence on the temporary directory and create the file in the current directory.
     210      193280 :             basetemp = basetemp//SKC_"/isFailedGetOutputMsg.tmp."
     211             :         end if
     212             : 
     213             :         ! Generate a unique file to avoid racing conditions in parallel.
     214             : 
     215      193280 :         lenBaseTemp = len(basetemp, IK)
     216      193280 :         lenFileTemp = lenBaseTemp + 20_IK ! 18 + 12_IK ! date_and_time() is 18 characters.
     217             :         if (allocated(filetemp)) deallocate(filetemp) ! \bug gfortran bug in automatic deallocation as of version 11.
     218      193280 :         allocate(character(lenFileTemp, SKC) :: filetemp)
     219      193280 :         rng = xoshiro256ssw_type(imageID = getImageID())
     220      193280 :         filetemp(1 : lenBaseTemp) = basetemp
     221      193280 :         do counter = 1_IK, NTRY
     222             :             !call setStr(filetemp(lenBaseTempPlus1 : lenFileTemp), length, counter)
     223             :             !inquire(file = filetemp(1 : lenBaseTempPlus1 + length - 1), exist = failed, iostat = iostat, iomsg = errmsg)
     224             :             !call setUnifRand(filetemp(lenBaseTemp + 19 :), SKC_"a", SKC_"z")
     225             :             !call date_and_time(date = filetemp(lenBaseTemp + 1 : lenBaseTemp + 8), time = filetemp(lenBaseTemp + 9 : lenBaseTemp + 18))
     226      193280 :             call setUnifRand(rng, filetemp(lenBaseTemp + 1 :), SKC_"a", SKC_"z")
     227      193280 :             inquire(file = filetemp, exist = failed, iostat = iostat, iomsg = errmsg)
     228      193280 :             failed = failed .or. logical(iostat /= 0_IK, LK)
     229      193280 :             if (.not. failed) exit
     230             :         end do
     231             :         !lenFileTemp = lenBaseTempPlus1 + length - 1_IK
     232             : 
     233      193280 :         if (.not. failed) then
     234             :             ! Run the command. Beware that the command execution can fail if the command is nonsensical.
     235      193280 :             failed = isFailedExec(command//SKC_" 1>"""//filetemp(1 : lenFileTemp)//SKC_""" 2>&1", cmdmsg = errmsg, exitstat = exitstat)
     236      193280 :             if (.not. failed) then
     237             :                 ! Read the command output from file if it exists.
     238      193280 :                 call setContentsFrom(filetemp(1 : lenFileTemp), contents = output, iostat = iostat, iomsg = errmsg, del = .true._LK)
     239      193280 :                 failed = logical(iostat /= 0_IK, LK)
     240      193280 :                 if (failed) then
     241           1 :                     SET_ERRMSG(__LINE__)
     242             :                 else
     243             :                     ! Remove the end-of-file (linefeed) and Carriage Return characters from the captured text,
     244             :                     ! because this is artificially added to the command output when written to the file.
     245             :                     !if (output(max(1_IK, len(output, IK) - lenNLC + 1_IK) : len(output, IK)) == NLC) output = output(1 : len(output, IK) - lenNLC)
     246             :                     !output = output(getSIL(output, new_line(SKC_"a")) : getSIR(output, new_line(SKC_"a")))
     247      193279 :                     output = getStripped(output, NLC)
     248           0 :                     loopStrip: do
     249      193279 :                         counter = min(1, len(output))
     250      193279 :                         if (output(1 : counter) == LF .or. isEndedWith(output, LF) .or. output(1 : counter) == CR .or. isEndedWith(output, CR)) then
     251           0 :                             output = getStripped(getStripped(output, new_line(SKC_"a")), achar(13, SKC)) ! CR must be removed on Windows OS.
     252             :                         else
     253             :                             exit loopStrip
     254             :                         end if
     255             :                     end do loopStrip
     256             :                 end if
     257             :             else
     258           0 :                 SET_ERRMSG(__LINE__)
     259             :             end if
     260             :         else
     261           0 :             SET_ERRMSG(__LINE__)
     262             :         end if
     263             : #undef  SET_ERRMSG
     264     1159680 :     end procedure
     265             : 
     266             : !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     267             : 
     268      775170 :     module procedure isFailedExec
     269             :         use pm_kind, only: SKC => SK
     270             :         use pm_val2str, only: getStr
     271             :         character(:, SK), allocatable               :: details
     272             :         logical                                     :: wait_def
     273             :         integer                                     :: cmdstat_def
     274             :         integer                                     :: exitstat_def
     275             :         character(*, SK), parameter                 :: PROCEDURE_NAME = MODULE_NAME//SK_"@isFailedExec()"
     276             :         character(*, SK), parameter                 :: LF = new_line(SK_"a")
     277             : 
     278      775170 :         if (present(wait)) then
     279          56 :             wait_def = logical(wait, LK)
     280             :         else
     281      775114 :             wait_def = .true._LK
     282             :         end if
     283             : 
     284      775170 :         if (present(exitstat)) exitstat_def = int(exitstat)
     285             : 
     286      775170 :         if (present(cmdmsg)) then
     287      193330 :             call execute_command_line(command, wait_def, exitstat_def, cmdstat_def, cmdmsg)
     288             :         else
     289      581840 :             call execute_command_line(command, wait_def, exitstat_def, cmdstat_def)
     290             :         end if
     291             : 
     292      775170 :         if (present(exitstat)) exitstat = int(exitstat_def, IK)
     293      775170 :         if (present(cmdstat)) cmdstat = int(cmdstat_def, IK)
     294             : 
     295      775170 :         failed = logical(cmdstat_def /= 0, LK)
     296             : 
     297      775170 :         if (failed .and. present(cmdmsg)) then
     298             :             details = LF//SK_"exitstat = "//getStr(exitstat_def)//LF//SK_"cmdstat = "//getStr(cmdstat_def)//LF//SK_"command = "//getStr(command) ! LCOV_EXCL_LINE
     299             :             if (cmdstat_def == -1) then ! LCOV_EXCL_LINE
     300             :                 cmdmsg = PROCEDURE_NAME//SK_": The processor does not support command execution. command: "//details ! LCOV_EXCL_LINE
     301             :             elseif (cmdstat_def == -2 .and. wait_def) then ! LCOV_EXCL_LINE
     302             :                 cmdmsg = PROCEDURE_NAME//SK_": processor does not support asynchronous command execution. command: "//details ! LCOV_EXCL_LINE
     303             :             elseif (cmdstat_def > 0) then ! LCOV_EXCL_LINE
     304             :                 cmdmsg = PROCEDURE_NAME//SK_": "//trim(adjustl(cmdmsg))//details ! LCOV_EXCL_LINE
     305             :             else ! LCOV_EXCL_LINE
     306             :                 error stop "How on Earth this could happen?! Hey compiler, you are violating the 2018 Fortran standard here." ! LCOV_EXCL_LINE
     307             :             end if
     308             :         end if
     309      775170 :     end procedure
     310             : 
     311             : !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     312             : 
     313           9 :     module procedure isShellWindows
     314             :         logical(LK) :: failed
     315             :         character(LEN_IOMSG, SK) :: errmsg
     316           9 :         shellIsWindows = isShellWindows(failed, errmsg)
     317             :         if (failed) error stop MODULE_NAME//SK_"@isShellWindows(): "//trim(errmsg) ! LCOV_EXCL_LINE
     318           9 :     end procedure
     319             : 
     320             :     !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     321             : 
     322           1 :     module procedure isShellWindowsFailed
     323             :         character(LEN_IOMSG, SK) :: errmsg
     324           1 :         shellIsWindows = isShellWindows(failed, errmsg)
     325           1 :     end procedure
     326             : 
     327             :     !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     328             : 
     329          11 :     module procedure isShellWindowsFailedMsg
     330          11 :         type(shell_type) :: shell
     331          11 :         shell = shell_type(failed, errmsg)
     332             :         !failed = isFailedInitShell(errmsg)
     333          11 :         if (failed) then
     334             :             errmsg = MODULE_NAME//SK_"@isShellWindowsFailedMsg(): Failed to fetch shell type. "//trim(errmsg) ! LCOV_EXCL_LINE
     335             :             shellIsWindows = .false._LK
     336             :         else
     337          11 :             shellIsWindows = shell%is%windows
     338             :         end if
     339          22 :     end procedure
     340             : 
     341             : !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     342             : 
     343           9 :     module procedure isShellPosix
     344             :         logical(LK) :: failed
     345             :         character(LEN_IOMSG, SK) :: errmsg
     346           9 :         shellIsPosix = isShellPosix(failed, errmsg)
     347             :         if (failed) error stop MODULE_NAME//SK_"@isShellPosix(): "//trim(errmsg) ! LCOV_EXCL_LINE
     348           9 :     end procedure
     349             : 
     350             :     !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     351             : 
     352           1 :     module procedure isShellPosixFailed
     353             :         character(LEN_IOMSG, SK) :: errmsg
     354           1 :         shellIsPosix = isShellPosix(failed, errmsg)
     355           1 :     end procedure
     356             : 
     357             :     !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     358             : 
     359          11 :     module procedure isShellPosixFailedMsg
     360             :         !failed = .not. mc_shellSet
     361             :         !if (failed) then
     362             :         !    if (allocated(mc_shell)) deallocate(mc_shell); mc_shell = shell_type(failed, errmsg)
     363             :         !    mc_shellSet = .not. failed
     364             :         !end if
     365          11 :         type(shell_type) :: shell
     366          11 :         shell = shell_type(failed, errmsg)
     367             :         !failed = isFailedInitShell(errmsg)
     368          11 :         if (failed) then
     369             :             errmsg = MODULE_NAME//SK_"@isShellPosixFailedMsg(): Failed to fetch shell type. "//trim(errmsg) ! LCOV_EXCL_LINE
     370             :             shellIsPosix = .false._LK
     371             :         else
     372          11 :             shellIsPosix = shell%is%posix
     373             :         end if
     374          22 :     end procedure
     375             : 
     376             : !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     377             : 
     378           1 :     module procedure isShellCMD
     379             :         logical(LK) :: failed
     380             :         character(LEN_IOMSG, SK) :: errmsg
     381           1 :         shellIsCMD = isShellCMD(failed, errmsg)
     382             :         if (failed) error stop MODULE_NAME//SK_"@isShellCMD(): "//trim(errmsg) ! LCOV_EXCL_LINE
     383           1 :     end procedure
     384             : 
     385             :     !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     386             : 
     387           1 :     module procedure isShellCMDFailed
     388             :         character(LEN_IOMSG, SK) :: errmsg
     389           1 :         shellIsCMD = isShellCMD(failed, errmsg)
     390           1 :     end procedure
     391             : 
     392             :     !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     393             : 
     394           3 :     module procedure isShellCMDFailedMsg
     395             :         !failed = .not. mc_shellSet
     396             :         !if (failed) then
     397             :         !    if (allocated(mc_shell)) deallocate(mc_shell); mc_shell = shell_type(failed, errmsg)
     398             :         !    mc_shellSet = .not. failed
     399             :         !end if
     400             :         !failed = isFailedInitShell(errmsg)
     401           3 :         type(shell_type) :: shell
     402           3 :         shell = shell_type(failed, errmsg)
     403           3 :         if (failed) then
     404             :             errmsg = MODULE_NAME//SK_"@isShellCMDFailedMsg(): Failed to fetch shell type. "//trim(errmsg) ! LCOV_EXCL_LINE
     405             :             shellIsCMD = .false._LK
     406             :         else
     407           3 :             shellIsCMD = shell%is%cmd
     408             :         end if
     409           6 :     end procedure
     410             : 
     411             : !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     412             : 
     413           1 :     module procedure isShellPowerShell
     414             :         logical(LK) :: failed
     415             :         character(LEN_IOMSG, SK) :: errmsg
     416           1 :         shellIsPowerShell = isShellPowerShell(failed, errmsg)
     417             :         if (failed) error stop MODULE_NAME//SK_"@isShellPowerShell(): "//trim(errmsg) ! LCOV_EXCL_LINE
     418           1 :     end procedure
     419             : 
     420             :     !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     421             : 
     422           1 :     module procedure isShellPowerShellFailed
     423             :         character(LEN_IOMSG, SK) :: errmsg
     424           1 :         shellIsPowerShell = isShellPowerShell(failed, errmsg)
     425           1 :     end procedure
     426             : 
     427             :     !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     428             : 
     429           3 :     module procedure isShellPowerShellFailedMsg
     430             :         !failed = .not. mc_shellSet
     431             :         !if (failed) then
     432             :         !    if (allocated(mc_shell)) deallocate(mc_shell); mc_shell = shell_type(failed, errmsg)
     433             :         !    mc_shellSet = .not. failed
     434             :         !end if
     435             :         !failed = isFailedInitShell(errmsg)
     436           3 :         type(shell_type) :: shell
     437           3 :         shell = shell_type(failed, errmsg)
     438           3 :         if (failed) then
     439             :             errmsg = MODULE_NAME//SK_"@isShellPowerShellFailedMsg(): Failed to fetch shell type. "//trim(errmsg) ! LCOV_EXCL_LINE
     440             :             shellIsPowerShell = .false._LK
     441             :         else
     442           3 :             shellIsPowerShell = shell%is%powershell
     443             :         end if
     444           6 :     end procedure
     445             : 
     446             : !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     447             : 
     448         158 :     module procedure constructShellIs
     449             :         character(LEN_IOMSG, SK) :: errmsg
     450             :         logical(LK) :: failed
     451         158 :         errmsg = SK_""
     452         158 :         shellis = shellis_type(failed, errmsg)
     453             :         if (failed) error stop MODULE_NAME//SK_"@constructShellIs(): "//trim(errmsg) ! LCOV_EXCL_LINE
     454         158 :     end procedure
     455             : 
     456             :     !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     457             : 
     458         134 :     module procedure constructShellIsFailed
     459             :         character(LEN_IOMSG, SK) :: errmsg
     460         134 :         errmsg = SK_""
     461         134 :         shellis = shellis_type(failed, errmsg)
     462         134 :     end procedure
     463             : 
     464             :     !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     465             : 
     466         481 :     module procedure constructShellIsFailedMsg
     467             :         !failed = .not. mc_shellSet
     468             :         !if (failed) then
     469             :         !    if (allocated(mc_shell)) deallocate(mc_shell); mc_shell = shell_type(failed, errmsg)
     470             :         !    mc_shellSet = .not. failed
     471             :         !end if
     472             :         !failed = isFailedInitShell(errmsg)
     473         481 :         type(shell_type) :: shell
     474         481 :         shell = shell_type(failed, errmsg)
     475         481 :         if (failed) then
     476             :             errmsg = MODULE_NAME//SK_"@constructShellIsFailedMsg(): "//trim(errmsg) ! LCOV_EXCL_LINE
     477             :         else
     478         481 :             shellis = shell%is
     479             :         end if
     480         962 :     end procedure
     481             : 
     482             : !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     483             : 
     484          77 :     module procedure constructShell
     485             :         character(LEN_IOMSG, SK) :: errmsg
     486             :         logical(LK) :: failed
     487          77 :         errmsg = SK_""
     488          77 :         shell = shell_type(failed, errmsg)
     489             :         if (failed) error stop MODULE_NAME//SK_"@constructShell(): "//trim(errmsg) ! LCOV_EXCL_LINE
     490          77 :     end procedure
     491             : 
     492             :     !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     493             : 
     494      192832 :     module procedure constructShellFailed
     495             :         character(31, SK) :: errmsg
     496      192832 :         errmsg = SK_""
     497      192832 :         shell = shell_type(failed, errmsg)
     498      192832 :     end procedure
     499             : 
     500             :     !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     501             : 
     502      193886 :     module procedure constructShellFailedMsg
     503             : 
     504             :         use pm_parallelism, only: getImageID
     505             :         use pm_sysPath, only: DIR_SEP_WINDOWS, DIR_SEP_WINDOWS_ALL
     506             :         use pm_sysPath, only: PATH_SEP_POSIX, PATH_SEP_WINDOWS
     507             :         use pm_sysPath, only: DIR_SEP_POSIX, DIR_SEP_POSIX_ALL
     508             :         use pm_distUnif, only: setUnifRand, xoshiro256ssw_type
     509             :         use pm_arrayStrip, only: getSIL, getSIR
     510             :         use pm_kind, only: SKC => SK
     511             :         use pm_err, only: getLine
     512             : 
     513             :         type(xoshiro256ssw_type)        :: rng
     514             :         character(*, SK), parameter     :: PROCEDURE_NAME = MODULE_NAME//SK_"@constructShell()"
     515             :         character(*, SK), parameter     :: LF = new_line(SK_"a")
     516      193886 :         character(:, SK), allocatable   :: filetemp, basetemp
     517             :         integer(IK)     , parameter     :: NTRY = 100000_IK
     518             :         integer(IK)                     :: iostat, counter
     519             :         integer(IK)                     :: lenBaseTemp
     520             :         integer(IK)                     :: lenFileTemp
     521             : #define SET_ERRMSG(LINE)\
     522             : errmsg = PROCEDURE_NAME//getLine(LINE)//SK_": Failed to fetch shell name. "//trim(errmsg)
     523             : 
     524     1163316 :         if (mc_shell%initialized) then
     525             : 
     526           0 :             shell = mc_shell%shell_type
     527           0 :             failed = .false._LK
     528             : 
     529             :         else
     530             : 
     531             :             ! Initialize the allocatable components to the default.
     532             : 
     533      193886 :             shell%name = SKC_""
     534      193886 :             shell%dirseps = DIR_SEP_POSIX_ALL
     535             : 
     536             :             ! Fetch the system temporary dir and create the base of the path.
     537             : 
     538      193886 :             failed = isFailedGetDirTemp(basetemp, errmsg)
     539      193886 :             if (failed) then
     540           0 :                 basetemp = SKC_"constructShell.tmp."
     541             :             else
     542             :                 !   \todo
     543             :                 !   \warning
     544             :                 !   The following assumes that all platforms and runtime shells recognize forward slash as a directory separator.
     545             :                 !   While this is currently the case, it may not be so in the future. A more robust solution may be necessary.
     546             :                 !   One solution is to remove dependence on the temporary directory and create the file in the current directory.
     547      193886 :                 basetemp = basetemp//SKC_"/constructShell.tmp."
     548             :             end if
     549             : 
     550             :             ! Generate a unique file name to avoid racing conditions in parallel.
     551             : 
     552      193886 :             lenBaseTemp = len(basetemp, IK)
     553      193886 :             lenFileTemp = lenBaseTemp + 21 ! + 12 ! + 1 dot + date and time length is 18.
     554             :             if (allocated(filetemp)) deallocate(filetemp) ! \bug gfortran bug in automatic deallocation as of version 11.
     555      193886 :             allocate(character(lenFileTemp, SKC) :: filetemp)
     556      193886 :             rng = xoshiro256ssw_type(imageID = getImageID())
     557      193886 :             filetemp(1 : lenBaseTemp) = basetemp
     558      193886 :             do counter = 1_IK, NTRY
     559      193886 :                 call setUnifRand(rng, filetemp(lenBaseTemp + 1 :), SKC_"a", SKC_"z")
     560      193886 :                 inquire(file = filetemp, exist = failed, iostat = iostat, iomsg = errmsg)
     561      193886 :                 failed = failed .or. logical(iostat /= 0_IK, LK)
     562      193886 :                 if (.not. failed) exit
     563             :             end do
     564             : 
     565      193886 :             if (failed) then
     566           0 :                 SET_ERRMSG(__LINE__)
     567             :             else
     568             :                 ! First check for Unix shells, as the command does not lead to odd syntax errors on a Windows shell.
     569             :                 ! $SHELL gives the full path to the default shell (not the runtime shell).
     570             :                 ! $0 gives the name (OR the path, e.g., in Git Bash) of the current shell.
     571             :                 ! $0 works in the following shells as of 2022: ash, bash, csh, dash, sh, tcsh, zsh, yash
     572             :                 ! $0 does not work in the following shells as of 2022: cmd, fish, powershell, pwsh,
     573             :                 ! WARNING
     574             :                 ! You may think `isFailedExec()` can be replaced with `isFailedGetOutput()` in the following.
     575             :                 ! That is a false premise.
     576             :                 ! Such a replacement creates cyclic dependence on shell_type constructor which then fails. Do not replace it.
     577      193886 :                 failed = isFailedExec(SK_"echo $0 1>"""//filetemp//""" 2>&1")
     578             :                 !write(*,*) filetemp
     579      193886 :                 call setContentsFrom(file = filetemp, contents = shell%name, iostat = iostat, iomsg = errmsg)!, del = .true._LK)
     580      193886 :                 failed = logical(iostat /= 0_IK, LK)
     581      193886 :                 if (failed) then
     582             :                     SET_ERRMSG(__LINE__) ! LCOV_EXCL_LINE
     583             :                 else
     584      193886 :                     if (0 < len(shell%name)) shell%name = shell%name(getSIL(shell%name, LF) : getSIR(shell%name, LF))
     585             :                     ! Assign the component values based on the contents of the command output.
     586      193886 :                     call setShell()
     587      193886 :                     if (shell%is%posix .and. shell%is%sh) then
     588             :                         ! If shell is sh, it may be a symlink, or some other shell renamed. If so, get the target shell type.
     589      193886 :                         failed = isFailedExec(SK_"command -v sh 1>"""//filetemp//""" 2>&1")
     590      193886 :                         if (.not. failed) then
     591      193886 :                             call setContentsFrom(file = filetemp, contents = shell%name, iostat = iostat, iomsg = errmsg, del = .true._LK)
     592      193886 :                             shell%name = shell%name(getSIL(shell%name, LF) : getSIR(shell%name, LF))
     593      193886 :                             failed = logical(iostat /= 0_IK, LK)
     594             :                         end if
     595             :                         ! Fetch the symlink target.
     596      193886 :                         if (.not. failed) then
     597      193886 :                             failed = isFailedExec(SKC_"ls -l """//shell%name//SKC_""" 1>"""//filetemp//""" 2>&1")
     598      193886 :                             if (.not. failed) then
     599      193886 :                                 call setContentsFrom(file = filetemp, contents = shell%name, iostat = iostat, iomsg = errmsg, del = .true._LK)
     600      193886 :                                 failed = logical(iostat /= 0_IK, LK)
     601      193886 :                                 if (.not. failed) call setShell()
     602             :                             end if
     603             :                         end if
     604      193886 :                         if (failed) then
     605             :                             errmsg = PROCEDURE_NAME//SK_": "//trim(errmsg) ! LCOV_EXCL_LINE
     606             :                             shell%name = SKC_"sh" ! LCOV_EXCL_LINE
     607             :                             failed = .false._LK ! LCOV_EXCL_LINE
     608             :                         end if
     609           0 :                     elseif (.not. shell%is%posix) then
     610             :                         ! First, check for fish shell.
     611           0 :                         failed = isFailedGetEnvVar("FISH_VERSION", shell%name, errmsg, length = 63_IK)
     612           0 :                         if (failed) then
     613             :                             errmsg = PROCEDURE_NAME//SK_": "//trim(errmsg) ! LCOV_EXCL_LINE
     614             :                             shell%name = SKC_"" ! LCOV_EXCL_LINE
     615           0 :                         elseif (len_trim(shell%name, IK) > 0_IK) then
     616             :                             !shell%is%posix = .true._LK ! fish is not posix-compliant. Dont you dare setting this to .true.!
     617           0 :                             shell%is%fish = .true._LK
     618           0 :                             shell%name = SKC_"fish"
     619             :                         end if
     620             :                     end if
     621             :                     !  Test for Windows-based terminals.
     622      193886 :                     if (.not. failed) then
     623      193886 :                         if (.not. (shell%is%posix .or. shell%is%fish)) then
     624             :                             ! \note
     625             :                             ! Note PowerShell Core pwsh.exe is also available on Unix systems and recognizes the same syntax as in Windows PowerShell BUT NOT ALL.
     626             :                             ! It also recognizes POXIS commands but not all!
     627             :                             ! Even worse, not all Windows PowerShell commands are defined in Unix PowerShell Core.
     628             :                             ! This is super-confusing. What the hell Microsoft are you doing?!
     629             :                             ! Fortunately the command for CMD shell recognition is platform-independent.
     630             :                             ! LCOV_EXCL_START
     631             :                             failed = isFailedExec(SK_"(dir 2>&1 *`|echo CMD >"""//filetemp//""");&<# rem #>echo PowerShell >"""//filetemp//""" 2>&1")
     632             :                             call setContentsFrom(file = filetemp, contents = shell%name, iostat = iostat, iomsg = errmsg, del = .true._LK)
     633             :                             failed = logical(iostat /= 0_IK, LK)
     634             :                             if (failed) SET_ERRMSG(__LINE__) ! LCOV_EXCL_LINE
     635             :                             if (.not. failed) then
     636             :                                 if (index(shell%name, SKC_"CMD", kind = IK) > 0_IK) then
     637             :                                     shell%is%windows = .true._LK
     638             :                                     shell%is%cmd = .true._LK
     639             :                                 elseif (index(shell%name, SKC_"PowerShell", kind = IK) > 0_IK) then
     640             :                                     shell%is%powershell = .true._LK
     641             : #if                                 WINDOWS_ENABLED
     642             :                                     shell%is%windows = .true._LK
     643             : #elif                               DARWIN_ENABLED || LINUX_ENABLED
     644             :                                     shell%is%posix = .true._LK
     645             : #else
     646             :                                     failed = isFailedGetEnvVar(SK_"OS", basetemp, errmsg, length = 10_IK)
     647             :                                     if (failed) then
     648             :                                         SET_ERRMSG(__LINE__)
     649             :                                     else
     650             :                                         shell%is%windows = index(getStrLower(basetemp), SK_"windows") /= 0)
     651             :                                         shell%is%posix = .not. shell%is%windows
     652             :                                     end if
     653             : #endif
     654             :                                 end if
     655             :                             end if
     656             :                         end if
     657             :                         if (.not. failed) then
     658             :                             if (shell%is%windows) then
     659             :                                 shell%dirseps = DIR_SEP_WINDOWS_ALL
     660             :                                 shell%pathsep = PATH_SEP_WINDOWS
     661             :                                 shell%dirsep = DIR_SEP_WINDOWS
     662             :                             elseif (shell%is%posix .or. shell%is%fish) then
     663             :                                 shell%dirseps = DIR_SEP_POSIX_ALL
     664             :                                 shell%pathsep = PATH_SEP_POSIX
     665             :                                 shell%dirsep = DIR_SEP_POSIX
     666             :                             else
     667             :                                 SET_ERRMSG(__LINE__) ! LCOV_EXCL_LINE
     668             :                                 failed = .true._LK ! LCOV_EXCL_LINE
     669             :                             end if
     670             :                             ! Cache the results.
     671             :                             mc_shell%shell_type = shell
     672             :                         end if
     673             :                     end if
     674             :                 end if
     675             :             end if
     676             :         end if
     677             : 
     678             :     contains
     679             : 
     680             :         subroutine setShell()
     681             :             ! Do **not** change the order of the following name checks.
     682             :             if (index(shell%name, SKC_"bash", kind = IK) > 0_IK) then
     683             :                 shell%name = SKC_"bash"
     684             :                 shell%is%sh = .false._LK
     685             :                 shell%is%bash = .true._LK
     686             :                 shell%is%posix = .true._LK
     687             :             elseif (index(shell%name, SKC_"dash", kind = IK) > 0_IK) then
     688             :                 shell%name = SKC_"dash"
     689             :                 shell%is%sh = .false._LK
     690             :                 shell%is%dash = .true._LK
     691             :                 shell%is%posix = .true._LK
     692             :             elseif (index(shell%name, SKC_"yash", kind = IK) > 0_IK) then
     693             :                 shell%name = SKC_"yash"
     694             :                 shell%is%sh = .false._LK
     695             :                 shell%is%yash = .true._LK
     696             :                 shell%is%posix = .true._LK
     697             :             elseif (index(shell%name, SKC_"ash", kind = IK) > 0_IK) then
     698             :                 shell%name = SKC_"ash"
     699             :                 shell%is%sh = .false._LK
     700             :                 shell%is%ash = .true._LK
     701             :                 shell%is%posix = .true._LK
     702             :             elseif (index(shell%name, SKC_"tcsh", kind = IK) > 0_IK) then
     703             :                 shell%name = SKC_"tcsh"
     704             :                 shell%is%sh = .false._LK
     705             :                 shell%is%tcsh = .true._LK
     706             :                 shell%is%posix = .true._LK
     707             :             elseif (index(shell%name, SKC_"csh", kind = IK) > 0_IK) then
     708             :                 shell%name = SKC_"csh"
     709             :                 shell%is%sh = .false._LK
     710             :                 shell%is%csh = .true._LK
     711             :                 shell%is%posix = .true._LK
     712             :             elseif (index(shell%name, SKC_"ksh", kind = IK) > 0_IK) then
     713             :                 shell%name = SKC_"ksh"
     714             :                 shell%is%sh = .false._LK
     715             :                 shell%is%ksh = .true._LK
     716             :                 shell%is%posix = .true._LK
     717             :             elseif (index(shell%name, SKC_"zsh", kind = IK) > 0_IK) then
     718             :                 shell%name = SKC_"zsh"
     719             :                 shell%is%sh = .false._LK
     720             :                 shell%is%zsh = .true._LK
     721             :                 shell%is%posix = .true._LK
     722             :             elseif (index(shell%name, SKC_"sh", kind = IK) > 0_IK) then
     723             :                 shell%name = SKC_"sh"
     724             :                 shell%is%sh = .true._LK
     725             :                 shell%is%posix = .true._LK
     726             :             end if
     727             :         end subroutine
     728             : 
     729             :     end procedure
     730             : 
     731             : !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     732             : 
     733             :     module procedure isFailedGetEnvVar
     734             :         character(LEN_IOMSG, SK) :: errmsg
     735             :         errmsg = SK_""
     736             :         failed = isFailedGetEnvVarMsg(name, value, errmsg, length)
     737             :     end procedure
     738             : 
     739             :     !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     740             : 
     741             :     module procedure isFailedGetEnvVarMsg
     742             : 
     743             :         use pm_kind, only: SKC => SK
     744             :         use pm_val2str, only: getStr
     745             :         character(*, SK), parameter :: PROCEDURE_NAME = MODULE_NAME//SK_"@isFailedGetEnvVarMsg()"
     746             :         integer :: status, lengthReal
     747             :         integer(IK) :: lenVal
     748             : 
     749             :         if (present(length)) then
     750             :             CHECK_ASSERTION(__LINE__, 0_IK < length, PROCEDURE_NAME//SK_": The input `length` value must be a positive integer. length = "//getStr(length)) ! fpp
     751             :             lenVal = length
     752             :         else
     753             :             lenVal = 8191_IK ! 2**13 - 1
     754             :         end if
     755             : 
     756             :         allocate(character(lenVal, SKC) :: value, stat = status)
     757             :         failed = logical(status /= 0, LK)
     758             :         if (failed) then
     759             :             errmsg = PROCEDURE_NAME//SK_": Fortran runtime error: Allocation of value failed with size = "//getStr(lenVal)//SK_". stat = "//getStr(status)//SK_"." ! LCOV_EXCL_LINE
     760             :             !value = SKC_"" ! LCOV_EXCL_LINE
     761             :             return ! LCOV_EXCL_LINE
     762             :         end if
     763             : 
     764             :         failed = logical(name == SKC_"", LK)
     765             :         if (failed) then
     766             :             errmsg = PROCEDURE_NAME//SK_": Fortran runtime error: Zero-length string passed as name to get_environment_variable()."
     767             :             !value = SK_""
     768             :             return
     769             :         end if
     770             : 
     771             :         loopAdjustLength: do
     772             :             call get_environment_variable(name, value, lengthReal, status)
     773             :             if (status == 0) then
     774             :                 if (lengthReal < len(value)) value = value(1 : lengthReal)
     775             :                 return
     776             :             elseif (status == +1) then ! the environment variable does not exist.
     777             :                 failed = .false._LK
     778             :                 !value = SK_""
     779             :                 return
     780             :             elseif (status == -1) then ! the value argument is present but too short.
     781             :                 deallocate(value)
     782             :                 lenVal = lenVal * 2_IK
     783             :                 allocate(character(lenVal, SK) :: value)
     784             :                 cycle loopAdjustLength
     785             :             elseif (status == +2) then
     786             :                 failed = .true._LK ! LCOV_EXCL_LINE
     787             :                 errmsg = PROCEDURE_NAME//SK_": Failed to fetch the value of environment variable """//name//""". The processor does not support environment variables. status = "//getStr(status) ! LCOV_EXCL_LINE
     788             :                 !value = SK_"" ! LCOV_EXCL_LINE
     789             :                 return ! LCOV_EXCL_LINE
     790             :             elseif (status > +2) then
     791             :                 failed = .true._LK ! LCOV_EXCL_LINE
     792             :                 errmsg = PROCEDURE_NAME//SK_": Unknown error occurred while fetching the value of the environment variable """//name//""". status = "//getStr(status) ! LCOV_EXCL_LINE
     793             :                 !value = SK_"" ! LCOV_EXCL_LINE
     794             :                 return ! LCOV_EXCL_LINE
     795             :             else
     796             :                 error stop "How on Earth such an error value could happen?! Hey compiler, you have violated the 2018 Fortran Standard. status = "//getStr(status) ! LCOV_EXCL_LINE
     797             :             end if
     798             :         end do loopAdjustLength
     799             : 
     800             :     end procedure
     801             : 
     802             : !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     803             : 
     804             :     module procedure isFailedPutEnvVar
     805             :         ! \warning : The input `dir` must be of default character kind.
     806             :         use iso_c_binding, only: c_null_char, c_int
     807             : #if     WINDOWS_ENABLED
     808             :         interface ! See WIN32 API for details of `_putenv_s`, `_wputenv_s`.
     809             :             function setEnvVar(name, value) result(stat) bind(C, name = "_putenv_s")
     810             :                 use iso_c_binding, only: c_char, c_int
     811             :                 character(1,c_char), intent(in) :: name(*), value(*)
     812             :                 integer(c_int) :: stat
     813             :             end function
     814             :         end interface
     815             :         failed = logical(setEnvVar(name//c_null_char, value//c_null_char) /= int(0, c_int), LK)
     816             : #else
     817             :         integer(c_int), parameter :: OVERWRITE_ENABLED = int(1, c_int)
     818             :         interface
     819             :             function setEnvVar(name, value, overwrite) result(stat) bind(C, name = "setenv")
     820             :                 use iso_c_binding, only: c_char, c_int
     821             :                 character(1,c_char), intent(in) :: name(*), value(*)
     822             :                 integer(c_int), intent(in) :: overwrite
     823             :                 integer(c_int) :: stat
     824             :             end function
     825             :         end interface
     826             :         failed = logical(setEnvVar(name//c_null_char, value//c_null_char, OVERWRITE_ENABLED) /= int(0, c_int), LK)
     827             : #endif
     828             :     end procedure
     829             : 
     830             : !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     831             : 
     832             : #define isFailedGetShellShape_ENABLED 1
     833             :     module procedure isFailedGetShellShape
     834             : #include "pm_sysShell@routines.inc.F90"
     835             :     end procedure
     836             : #undef isFailedGetShellShape_ENABLED
     837             : 
     838             : !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     839             : 
     840             : #define isFailedGetShellWidth_ENABLED 1
     841             :     module procedure isFailedGetShellWidth
     842             : #include "pm_sysShell@routines.inc.F90"
     843             :     end procedure
     844             : #undef isFailedGetShellWidth_ENABLED
     845             : 
     846             : !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     847             : 
     848             : #define isFailedGetShellHeight_ENABLED 1
     849             :     module procedure isFailedGetShellHeight
     850             : #include "pm_sysShell@routines.inc.F90"
     851             :     end procedure
     852             : #undef isFailedGetShellHeight_ENABLED
     853             : 
     854             : !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     855             : 
     856             : #undef CHECK_ASSERTION
     857             : 
     858             : end submodule routines

ParaMonte: Parallel Monte Carlo and Machine Learning Library 
The Computational Data Science Lab
© Copyright 2012 - 2024