Skip to content

Commit 153cd24

Browse files
hasufellMistuke
authored andcommitted
Implement getEnv and getEnvironment
1 parent 350ebd4 commit 153cd24

File tree

3 files changed

+86
-4
lines changed

3 files changed

+86
-4
lines changed

System/Win32/Console.hsc

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#if __GLASGOW_HASKELL__ >= 709
2-
{-# LANGUAGE Safe #-}
2+
{-# LANGUAGE Trustworthy #-}
33
#else
44
{-# LANGUAGE Trustworthy #-}
55
#endif
@@ -56,22 +56,31 @@ module System.Win32.Console (
5656
getConsoleScreenBufferInfo,
5757
getCurrentConsoleScreenBufferInfo,
5858
getConsoleScreenBufferInfoEx,
59-
getCurrentConsoleScreenBufferInfoEx
59+
getCurrentConsoleScreenBufferInfoEx,
60+
61+
-- * Env
62+
getEnv,
63+
getEnvironment
6064
) where
6165

6266
#include <windows.h>
6367
#include "alignment.h"
6468
##include "windows_cconv.h"
6569
#include "wincon_compat.h"
6670

71+
import Data.Char (chr)
6772
import System.Win32.Types
73+
import System.Win32.String
6874
import System.Win32.Console.Internal
6975
import Graphics.Win32.Misc
7076
import Graphics.Win32.GDI.Types (COLORREF)
7177

72-
import Foreign.C.String (withCWString)
78+
import GHC.IO (bracket)
79+
import Foreign.Ptr (plusPtr)
80+
import Foreign.C.Types (CWchar)
81+
import Foreign.C.String (withCWString, CWString)
7382
import Foreign.Storable (Storable(..))
74-
import Foreign.Marshal.Array (peekArray)
83+
import Foreign.Marshal.Array (peekArray, peekArray0)
7584
import Foreign.Marshal.Alloc (alloca)
7685

7786

@@ -154,3 +163,65 @@ getCurrentConsoleScreenBufferInfoEx :: IO CONSOLE_SCREEN_BUFFER_INFOEX
154163
getCurrentConsoleScreenBufferInfoEx = do
155164
h <- failIf (== nullHANDLE) "getStdHandle" $ getStdHandle sTD_OUTPUT_HANDLE
156165
getConsoleScreenBufferInfoEx h
166+
167+
168+
-- c_GetEnvironmentVariableW :: LPCWSTR -> LPWSTR -> DWORD -> IO DWORD
169+
getEnv :: String -> IO (Maybe String)
170+
getEnv name =
171+
withCWString name $ \c_name -> withTStringBufferLen maxLength $ \(buf, len) -> do
172+
let c_len = fromIntegral len
173+
c_len' <- c_GetEnvironmentVariableW c_name buf c_len
174+
if c_len' == 0
175+
then do
176+
err_code <- getLastError
177+
if err_code == eERROR_ENVVAR_NOT_FOUND
178+
then return Nothing
179+
else errorWin "GetEnvironmentVariableW"
180+
else do
181+
let len' = fromIntegral c_len'
182+
Just <$> peekTStringLen (buf, len')
183+
where
184+
-- according to https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-getenvironmentvariablew
185+
maxLength :: Int
186+
maxLength = 32767
187+
188+
189+
getEnvironment :: IO [(String, String)]
190+
getEnvironment = bracket c_GetEnvironmentStringsW c_FreeEnvironmentStrings $ \lpwstr -> do
191+
strs <- builder lpwstr
192+
return (divvy <$> strs)
193+
where
194+
divvy :: String -> (String, String)
195+
divvy str =
196+
case break (=='=') str of
197+
(xs,[]) -> (xs,[]) -- don't barf (like Posix.getEnvironment)
198+
(name,_:value) -> (name,value)
199+
200+
builder :: LPWSTR -> IO [String]
201+
builder ptr = go 0
202+
where
203+
go :: Int -> IO [String]
204+
go off = do
205+
(str, l) <- peekCWStringOff ptr off
206+
if l == 0
207+
then pure []
208+
else (str:) <$> go (((l + 1) * 2) + off)
209+
210+
211+
peekCWStringOff :: CWString -> Int -> IO (String, Int)
212+
peekCWStringOff cp off = do
213+
cs <- peekArray0 wNUL (cp `plusPtr` off)
214+
return (cWcharsToChars cs, length cs)
215+
216+
wNUL :: CWchar
217+
wNUL = 0
218+
219+
cWcharsToChars :: [CWchar] -> [Char]
220+
cWcharsToChars = map chr . fromUTF16 . map fromIntegral
221+
where
222+
fromUTF16 (c1:c2:wcs)
223+
| 0xd800 <= c1 && c1 <= 0xdbff && 0xdc00 <= c2 && c2 <= 0xdfff =
224+
((c1 - 0xd800)*0x400 + (c2 - 0xdc00) + 0x10000) : fromUTF16 wcs
225+
fromUTF16 (c:wcs) = c : fromUTF16 wcs
226+
fromUTF16 [] = []
227+

System/Win32/Console/Internal.hsc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,15 @@ foreign import WINDOWS_CCONV unsafe "Shellapi.h CommandLineToArgvW"
6666
foreign import WINDOWS_CCONV unsafe "processenv.h GetCommandLineW"
6767
getCommandLineW :: IO LPWSTR
6868

69+
foreign import WINDOWS_CCONV unsafe "processenv.h GetEnvironmentVariableW"
70+
c_GetEnvironmentVariableW :: LPCWSTR -> LPWSTR -> DWORD -> IO DWORD
71+
72+
foreign import WINDOWS_CCONV unsafe "processenv.h GetEnvironmentStringsW"
73+
c_GetEnvironmentStringsW :: IO LPWSTR
74+
75+
foreign import WINDOWS_CCONV unsafe "processenv.h FreeEnvironmentStringsW"
76+
c_FreeEnvironmentStrings :: LPWSTR -> IO Bool
77+
6978
data CONSOLE_SCREEN_BUFFER_INFO = CONSOLE_SCREEN_BUFFER_INFO
7079
{ dwSize :: COORD
7180
, dwCursorPosition :: COORD

System/Win32/Types.hsc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,8 @@ eRROR_MOD_NOT_FOUND = #const ERROR_MOD_NOT_FOUND
456456
eRROR_PROC_NOT_FOUND :: ErrCode
457457
eRROR_PROC_NOT_FOUND = #const ERROR_PROC_NOT_FOUND
458458

459+
eERROR_ENVVAR_NOT_FOUND :: ErrCode
460+
eERROR_ENVVAR_NOT_FOUND = #const ERROR_ENVVAR_NOT_FOUND
459461

460462
errorWin :: String -> IO a
461463
errorWin fn_name = do

0 commit comments

Comments
 (0)