1 /******************************************************************************* 2 3 copyright: Copyright (c) 2007 Tango. All rights reserved 4 5 license: BSD style: $(LICENSE) 6 7 version: Feb 2007: Initial release 8 9 author: Deewiant, Maxter, Gregor, Kris 10 11 *******************************************************************************/ 12 13 module tango.sys.Environment; 14 15 private import tango.sys.Common; 16 17 private import tango.io.Path, 18 tango.io.FilePath; 19 20 private import tango.core.Exception; 21 22 private import tango.io.model.IFile; 23 24 private import Text = tango.text.Util; 25 26 private import tango.core.Octal; 27 28 /******************************************************************************* 29 30 Platform decls 31 32 *******************************************************************************/ 33 34 version (Windows) 35 { 36 private import tango.text.convert.Utf; 37 38 pragma (lib, "kernel32.lib"); 39 40 extern (Windows) 41 { 42 private void* GetEnvironmentStringsW(); 43 private bool FreeEnvironmentStringsW(wchar**); 44 } 45 extern (Windows) 46 { 47 private int SetEnvironmentVariableW(const(wchar)*, const(wchar)*); 48 private uint GetEnvironmentVariableW(const(wchar)*, wchar*, uint); 49 private const int ERROR_ENVVAR_NOT_FOUND = 203; 50 } 51 } 52 else 53 { 54 version (darwin) 55 { 56 extern (C) char*** _NSGetEnviron(); 57 private __gshared char** environ; 58 59 shared static this () 60 { 61 environ = *_NSGetEnviron(); 62 } 63 } 64 65 else 66 private extern (C) extern __gshared char** environ; 67 68 import tango.stdc.posix.stdlib; 69 import tango.stdc.string; 70 } 71 72 73 /******************************************************************************* 74 75 Exposes the system Environment settings, along with some handy 76 utilities 77 78 *******************************************************************************/ 79 80 struct Environment 81 { 82 public alias cwd directory; 83 84 /*********************************************************************** 85 86 Throw an exception 87 88 ***********************************************************************/ 89 90 private static void exception (immutable(char)[] msg) 91 { 92 throw new PlatformException (msg); 93 } 94 95 /*********************************************************************** 96 97 Returns an absolute version of the provided path, where cwd is used 98 as the prefix. 99 100 The provided path is returned as is if already absolute. 101 102 ***********************************************************************/ 103 104 static char[] toAbsolute(char[] path) 105 { 106 scope fp = new FilePath(path); 107 if (fp.isAbsolute) 108 return path; 109 110 fp.absolute(cwd()); 111 return fp.cString()[0..$-1]; 112 } 113 114 /*********************************************************************** 115 116 Returns the full path location of the provided executable 117 file, rifling through the PATH as necessary. 118 119 Returns null if the provided filename was not found 120 121 ***********************************************************************/ 122 123 static FilePath exePath (char[] file) 124 { 125 auto bin = new FilePath (file); 126 127 // on Windows, this is a .exe 128 version (Windows) 129 if (bin.ext.length is 0) 130 bin.suffix = "exe"; 131 132 // is this a directory? Potentially make it absolute 133 if (bin.isChild && !bin.isAbsolute) 134 return bin.absolute (cwd()); 135 136 // is it in cwd? 137 version (Windows) 138 if (bin.path(cwd()).exists) 139 return bin; 140 141 // rifle through the path (after converting to standard format) 142 foreach (pe; Text.patterns (standard(get("PATH")), tango.io.model.IFile.FileConst.SystemPathString)) 143 if (bin.path(pe).exists) 144 { 145 version (Windows) 146 return bin; 147 else 148 { 149 stat_t stats; 150 stat(bin.cString().ptr, &stats); 151 if (stats.st_mode & octal!(100)) 152 return bin; 153 } 154 } 155 return null; 156 } 157 158 /*********************************************************************** 159 160 Windows implementation 161 162 ***********************************************************************/ 163 164 version (Windows) 165 { 166 /************************************************************** 167 168 Returns the provided 'def' value if the variable 169 does not exist 170 171 **************************************************************/ 172 173 static char[] get (const(char)[] variable, char[] def = null) 174 { 175 const(wchar)[] var = toString16(variable) ~ "\0"; 176 177 uint size = GetEnvironmentVariableW(var.ptr, cast(wchar*)null, 0); 178 if (size is 0) 179 { 180 if (SysError.lastCode is ERROR_ENVVAR_NOT_FOUND) 181 return def; 182 else 183 exception (SysError.lastMsg.idup); 184 } 185 186 auto buffer = new wchar[size]; 187 size = GetEnvironmentVariableW(var.ptr, buffer.ptr, size); 188 if (size is 0) 189 exception (SysError.lastMsg.idup); 190 191 return toString (buffer[0 .. size]); 192 } 193 194 /************************************************************** 195 196 clears the variable if value is null or empty 197 198 **************************************************************/ 199 200 static void set (const(char)[] variable, const(char)[] value = null) 201 { 202 const(wchar) * var, val; 203 204 var = (toString16 (variable) ~ "\0").ptr; 205 206 if (value.length > 0) 207 val = (toString16 (value) ~ "\0").ptr; 208 209 if (! SetEnvironmentVariableW(var, val)) 210 exception (SysError.lastMsg.idup); 211 } 212 213 /************************************************************** 214 215 Get all set environment variables as an associative 216 array. 217 218 **************************************************************/ 219 220 static char[][char[]] get () 221 { 222 char[][char[]] arr; 223 224 wchar[] key = new wchar[20], 225 value = new wchar[40]; 226 227 wchar** env = cast(wchar**) GetEnvironmentStringsW(); 228 scope (exit) 229 FreeEnvironmentStringsW (env); 230 231 for (wchar* str = cast(wchar*) env; *str; ++str) 232 { 233 size_t k = 0, v = 0; 234 235 while (*str != '=') 236 { 237 key[k++] = *str++; 238 239 if (k is key.length) 240 key.length = 2 * key.length; 241 } 242 243 ++str; 244 245 while (*str) 246 { 247 value [v++] = *str++; 248 249 if (v is value.length) 250 value.length = 2 * value.length; 251 } 252 253 arr [toString(key[0 .. k]).idup] = toString(value[0 .. v]); 254 } 255 256 return arr; 257 } 258 259 /************************************************************** 260 261 Set the current working directory 262 263 **************************************************************/ 264 265 static void cwd (const(char)[] path) 266 { 267 version (Win32SansUnicode) 268 { 269 char[MAX_PATH+1] tmp = void; 270 tmp[0..path.length] = path; 271 tmp[path.length] = 0; 272 273 if (! SetCurrentDirectoryA (tmp.ptr)) 274 exception ("Failed to set current directory"); 275 } 276 else 277 { 278 // convert into output buffer 279 wchar[MAX_PATH+1] tmp = void; 280 assert (path.length < tmp.length); 281 auto i = MultiByteToWideChar (CP_UTF8, 0, 282 cast(PCHAR)path.ptr, path.length, 283 tmp.ptr, tmp.length); 284 tmp[i] = 0; 285 286 if (! SetCurrentDirectoryW (tmp.ptr)) 287 exception ("Failed to set current directory"); 288 } 289 } 290 291 /************************************************************** 292 293 Get the current working directory 294 295 **************************************************************/ 296 297 static char[] cwd () 298 { 299 char[] path; 300 301 version (Win32SansUnicode) 302 { 303 int len = GetCurrentDirectoryA (0, null); 304 auto dir = new char [len]; 305 GetCurrentDirectoryA (len, dir.ptr); 306 if (len) 307 { 308 if (dir[len-2] is '/') 309 dir.length = len-1; 310 else 311 dir[len-1] = '/'; 312 path = standard (dir); 313 } 314 else 315 exception ("Failed to get current directory"); 316 } 317 else 318 { 319 wchar[MAX_PATH+2] tmp = void; 320 321 auto len = GetCurrentDirectoryW (0, null); 322 assert (len < tmp.length); 323 auto dir = new char [len * 3]; 324 GetCurrentDirectoryW (len, tmp.ptr); 325 auto i = WideCharToMultiByte (CP_UTF8, 0, tmp.ptr, len, 326 cast(PCHAR)dir.ptr, dir.length, null, null); 327 if (len && i) 328 { 329 path = standard (dir[0..i]); 330 if (path[$-2] is '/') 331 path.length = path.length-1; 332 else 333 path[$-1] = '/'; 334 } 335 else 336 exception ("Failed to get current directory"); 337 } 338 339 return path; 340 } 341 342 } 343 344 /*********************************************************************** 345 346 Posix implementation 347 348 ***********************************************************************/ 349 350 version (Posix) 351 { 352 /************************************************************** 353 354 Returns the provided 'def' value if the variable 355 does not exist 356 357 **************************************************************/ 358 359 static char[] get (const(char)[] variable, char[] def = null) 360 { 361 char* ptr = getenv ((variable ~ '\0').ptr); 362 363 if (ptr is null) 364 return def; 365 366 return ptr[0 .. strlen(ptr)].dup; 367 } 368 369 /************************************************************** 370 371 clears the variable, if value is null or empty 372 373 **************************************************************/ 374 375 static void set (const(char)[] variable, const(char)[] value = null) 376 { 377 int result; 378 379 if (value.length is 0) 380 unsetenv ((variable ~ '\0').ptr); 381 else 382 result = setenv ((variable ~ '\0').ptr, (value ~ '\0').ptr, 1); 383 384 if (result != 0) 385 exception (SysError.lastMsg.idup); 386 } 387 388 /************************************************************** 389 390 Get all set environment variables as an associative 391 array. 392 393 **************************************************************/ 394 395 static char[][char[]] get () 396 { 397 char[][char[]] arr; 398 399 for (char** p = environ; *p; ++p) 400 { 401 size_t k = 0; 402 char* str = *p; 403 404 while (*str++ != '=') 405 ++k; 406 char[] key = (*p)[0..k]; 407 408 k = 0; 409 char* val = str; 410 while (*str++) 411 ++k; 412 arr[key.idup] = val[0 .. k]; 413 } 414 415 return arr; 416 } 417 418 /************************************************************** 419 420 Set the current working directory 421 422 **************************************************************/ 423 424 static void cwd (const(char)[] path) 425 { 426 char[512] tmp = void; 427 tmp [path.length] = 0; 428 tmp[0..path.length] = path[]; 429 430 if (tango.stdc.posix.unistd.chdir (tmp.ptr)) 431 exception ("Failed to set current directory"); 432 } 433 434 /************************************************************** 435 436 Get the current working directory 437 438 **************************************************************/ 439 440 static char[] cwd () 441 { 442 char[512] tmp = void; 443 444 char *s = tango.stdc.posix.unistd.getcwd (tmp.ptr, tmp.length); 445 if (s is null) 446 exception ("Failed to get current directory"); 447 448 auto path = s[0 .. strlen(s)+1].dup; 449 if (path[$-2] is '/') // root path has the slash 450 path.length = path.length-1; 451 else 452 path[$-1] = '/'; 453 return path; 454 } 455 } 456 } 457 458 459 /******************************************************************************* 460 461 462 *******************************************************************************/ 463 464 debug (Environment) 465 { 466 import tango.io.Console; 467 468 469 void main(const(char)[][] args) 470 { 471 enum immutable(char)[] VAR = "TESTENVVAR"; 472 enum immutable(char)[] VAL1 = "VAL1"; 473 enum immutable(char)[] VAL2 = "VAL2"; 474 475 assert(Environment.get(VAR) is null); 476 477 Environment.set(VAR, VAL1); 478 assert(Environment.get(VAR) == VAL1); 479 480 Environment.set(VAR, VAL2); 481 assert(Environment.get(VAR) == VAL2); 482 483 Environment.set(VAR, null); 484 assert(Environment.get(VAR) is null); 485 486 Environment.set(VAR, VAL1); 487 Environment.set(VAR, ""); 488 489 assert(Environment.get(VAR) is null); 490 491 foreach (key, value; Environment.get) 492 Cout (key) ("=") (value).newline; 493 494 if (args.length > 0) 495 { 496 auto p = Environment.exePath (args[0]); 497 Cout (p).newline; 498 } 499 500 if (args.length > 1) 501 { 502 if (auto p = Environment.exePath (args[1])) 503 Cout (p).newline; 504 } 505 } 506 } 507