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