## core libc calls ## @unix_only begin _getenv(var::String) = ccall(:getenv, Ptr{Uint8}, (Ptr{Uint8},), var) _hasenv(s::String) = _getenv(s) != C_NULL end @windows_only begin const ERROR_ENVVAR_NOT_FOUND = uint32(203) const FORMAT_MESSAGE_ALLOCATE_BUFFER = uint32(0x100) const FORMAT_MESSAGE_FROM_SYSTEM = uint32(0x1000) const FORMAT_MESSAGE_IGNORE_INSERTS = uint32(0x200) const FORMAT_MESSAGE_MAX_WIDTH_MASK = uint32(0xFF) GetLastError() = ccall(:GetLastError,stdcall,Uint32,()) function FormatMessage(e=GetLastError()) lpMsgBuf = Array(Ptr{Uint16}) lpMsgBuf[1] = 0 len = ccall(:FormatMessageW,stdcall,Uint32,(Cint, Ptr{Void}, Cint, Cint, Ptr{Ptr{Uint16}}, Cint, Ptr{Void}), FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, C_NULL, e, 0, lpMsgBuf, 0, C_NULL) p = lpMsgBuf[1] len == 0 && return utf8("") len = len + 1 buf = Array(Uint16, len) unsafe_copy!(pointer(buf), p, len) ccall(:LocalFree,stdcall,Ptr{Void},(Ptr{Void},),p) return utf8(UTF16String(buf)) end _getenvlen(var::UTF16String) = ccall(:GetEnvironmentVariableW,stdcall,Uint32,(Ptr{Uint16},Ptr{Uint8},Uint32),utf16(var),C_NULL,0) _hasenv(s::UTF16String) = _getenvlen(s)!=0 || GetLastError()!=ERROR_ENVVAR_NOT_FOUND _hasenv(s::String) = _hasenv(utf16(s)) function _jl_win_getenv(s::UTF16String,len::Uint32) val=zeros(Uint16,len) ret=ccall(:GetEnvironmentVariableW,stdcall,Uint32,(Ptr{Uint16},Ptr{Uint16},Uint32),s,val,len) if ret==0 || ret != len-1 || val[end] != 0 error(string("system error getenv: ", s, ' ', len, "-1 != ", ret, ": ", FormatMessage())) end val end end macro accessEnv(var,errorcase) @unix_only return quote val=_getenv($(esc(var))) if val == C_NULL $(esc(errorcase)) end bytestring(val) end @windows_only return quote let var = utf16($(esc(var))) len=_getenvlen(var) if len == 0 if GetLastError() != ERROR_ENVVAR_NOT_FOUND return utf8("") else $(esc(errorcase)) end end utf8(UTF16String(_jl_win_getenv(var,len))) end end end function _setenv(var::String, val::String, overwrite::Bool) @unix_only begin ret = ccall(:setenv, Int32, (Ptr{Uint8},Ptr{Uint8},Int32), var, val, overwrite) systemerror(:setenv, ret != 0) end @windows_only begin var = utf16(var) if overwrite || !_hasenv(var) ret = ccall(:SetEnvironmentVariableW,stdcall,Int32,(Ptr{Uint16},Ptr{Uint16}),utf16(var),utf16(val)) systemerror(:setenv, ret == 0) end end end _setenv(var::String, val::String) = _setenv(var, val, true) function _unsetenv(var::String) @unix_only begin ret = ccall(:unsetenv, Int32, (Ptr{Uint8},), var) systemerror(:unsetenv, ret != 0) end @windows_only begin ret = ccall(:SetEnvironmentVariableW,stdcall,Int32,(Ptr{Uint16},Ptr{Uint16}),utf16(var),C_NULL) systemerror(:setenv, ret == 0) end end ## ENV: hash interface ## type EnvHash <: Associative{ByteString,ByteString}; end const ENV = EnvHash() similar(::EnvHash) = Dict{ByteString,ByteString}() getindex(::EnvHash, k::String) = @accessEnv k throw(KeyError(k)) get(::EnvHash, k::String, def) = @accessEnv k (return def) in(k::String, ::KeyIterator{EnvHash}) = _hasenv(k) pop!(::EnvHash, k::String) = (v = ENV[k]; _unsetenv(k); v) pop!(::EnvHash, k::String, def) = haskey(ENV,k) ? pop!(ENV,k) : def function delete!(::EnvHash, k::String) warn_once(""" delete!(ENV,key) now returns the modified environment. Use pop!(ENV,key) to retrieve the value instead. """) _unsetenv(k) ENV end delete!(::EnvHash, k::String, def) = haskey(ENV,k) ? delete!(ENV,k) : def setindex!(::EnvHash, v, k::String) = _setenv(k,string(v)) push!(::EnvHash, k::String, v) = setindex!(ENV, v, k) @unix_only begin start(::EnvHash) = 0 done(::EnvHash, i) = (ccall(:jl_environ, Any, (Int32,), i) == nothing) function next(::EnvHash, i) env = ccall(:jl_environ, Any, (Int32,), i) if env == nothing error(BoundsError) end env::ByteString m = match(r"^(.*?)=(.*)$"s, env) if m == nothing error("malformed environment entry: $env") end (ByteString[convert(typeof(env),x) for x in m.captures], i+1) end end @windows_only begin start(hash::EnvHash) = (pos = ccall(:GetEnvironmentStringsW,stdcall,Ptr{Uint16},()); (pos,pos)) function done(hash::EnvHash, block::(Ptr{Uint16},Ptr{Uint16})) if unsafe_load(block[1])==0 ccall(:FreeEnvironmentStringsW,stdcall,Int32,(Ptr{Uint16},),block[2]) return true end false end function next(hash::EnvHash, block::(Ptr{Uint16},Ptr{Uint16})) pos = block[1] blk = block[2] len = ccall(:wcslen, Uint, (Ptr{Uint16},), pos)+1 buf = Array(Uint16, len) unsafe_copy!(pointer(buf), pos, len) env = utf8(UTF16String(buf)) m = match(r"^(=?[^=]+)=(.*)$"s, env) if m == nothing error("malformed environment entry: $env") end (ByteString[convert(typeof(env),x) for x in m.captures], (pos+len*2, blk)) end end #TODO: Make these more efficent function length(::EnvHash) i = 0 for (k,v) in ENV i += 1 end return i end function show(io::IO, ::EnvHash) for (k,v) = ENV println(io, "$k=$v") end end # temporarily set and then restore an environment value function with_env(f::Function, key::String, val) old = get(ENV,key,nothing) val != nothing ? (ENV[key]=val) : _unsetenv(key) try f() finally old != nothing ? (ENV[key]=old) : _unsetenv(key) catch rethrow() end end ## misc environment-related functionality ## function tty_size() if isdefined(Base, :active_repl) os = REPL.outstream(Base.active_repl) if isa(os, Terminals.TTYTerminal) return size(os) end end return (parseint(get(ENV,"LINES","24")), parseint(get(ENV,"COLUMNS","80"))) end