1 /** 2 * Functions for retrieving standard paths in cross-platform manner. 3 * Authors: 4 * $(LINK2 https://github.com/MyLittleRobo, Roman Chistokhodov) 5 * License: 6 * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 7 * Copyright: 8 * Roman Chistokhodov 2015-2016 9 */ 10 11 module standardpaths; 12 13 private { 14 import std.process : environment; 15 import std.path; 16 import std.file; 17 import std.exception; 18 import std.range; 19 20 import isfreedesktop; 21 22 debug { 23 import std.stdio : stderr; 24 } 25 26 static if( __VERSION__ < 2066 ) enum nogc = 1; 27 } 28 29 version(Windows) { 30 private { 31 static if (__VERSION__ < 2070) { 32 import std.c.windows.windows; 33 } else { 34 import core.sys.windows.windows; 35 } 36 37 import std.utf; 38 } 39 } else version(Posix) { 40 private { 41 import std.string : toStringz; 42 43 static if (is(typeof({import std.string : fromStringz;}))) { 44 import std.string : fromStringz; 45 } else { //own fromStringz implementation for compatibility reasons 46 import std.c.string : strlen; 47 @system pure inout(char)[] fromStringz(inout(char)* cString) { 48 return cString ? cString[0..strlen(cString)] : null; 49 } 50 } 51 52 //Concat two strings, but if the first one is empty, then null string is returned. 53 string maybeConcat(string start, string path) nothrow @safe 54 { 55 return start.empty ? null : start ~ path; 56 } 57 58 string maybeBuild(string start, string path) nothrow @safe 59 { 60 return start.empty ? null : buildPath(start, path); 61 } 62 63 string verifyIfNeeded(string path, bool shouldVerify) nothrow @trusted 64 { 65 if (path.length && shouldVerify) { 66 bool dirExists; 67 collectException(path.isDir, dirExists); 68 return dirExists ? path : null; 69 } else { 70 return path; 71 } 72 } 73 74 string createIfNeeded(string path, bool shouldCreate) nothrow @trusted 75 { 76 if (path.length && shouldCreate) { 77 bool pathExist; 78 collectException(path.isDir, pathExist); 79 if (pathExist || collectException(mkdirRecurse(path)) is null) { 80 return path; 81 } else { 82 return null; 83 } 84 } else { 85 return path; 86 } 87 } 88 } 89 } else { 90 static assert(false, "Unsupported platform"); 91 } 92 93 /** 94 * Location types that can be passed to $(D writablePath) and $(D standardPaths) functions. 95 * 96 * Not all these paths are suggested for showing in file managers or file dialogs. 97 * Some of them are meant for internal application usage or should be treated in special way. 98 * On usual circumstances user wants to see Desktop, Documents, Downloads, Pictures, Music and Videos directories. 99 * 100 * See_Also: 101 * $(D writablePath), $(D standardPaths) 102 */ 103 enum StandardPath { 104 /** 105 * General location of persisted application data. Every application should have its own subdirectory here. 106 * Note: on Windows it's the same as $(B config) path. 107 */ 108 data, 109 /** 110 * General location of configuration files. Every application should have its own subdirectory here. 111 * Note: on Windows it's the same as $(B data) path. 112 */ 113 config, 114 /** 115 * Location of cached data. 116 * Note: Not available on Windows. 117 */ 118 cache, 119 ///User's desktop directory. 120 desktop, 121 ///User's documents. 122 documents, 123 ///User's pictures. 124 pictures, 125 126 ///User's music. 127 music, 128 129 ///User's videos (movies). 130 videos, 131 132 ///Directory for user's downloaded files. 133 downloads, 134 135 /** 136 * Location of file templates (e.g. office suite document templates). 137 * Note: Not available on OS X. 138 */ 139 templates, 140 141 /** 142 * Public share folder. 143 * Note: Not available on Windows. 144 */ 145 publicShare, 146 /** 147 * Location of fonts files. 148 * Note: don't rely on this on freedesktop, since it uses hardcoded paths there. Better consider using $(LINK2 http://www.freedesktop.org/wiki/Software/fontconfig/, fontconfig library) 149 */ 150 fonts, 151 /** 152 * User's applications. This has different meaning across platforms. 153 * On Windows it's directory where links (.lnk) to programs for Start menu are stored. 154 * On OS X it's folder where applications are typically put. 155 * On Freedesktop it's directory where .desktop files are put. 156 */ 157 applications, 158 159 /** 160 * Automatically started applications. 161 * On Windows it's directory where links (.lnk) to autostarted programs are stored. 162 * On OSX it's not available. 163 * On Freedesktop it's directory where autostarted .desktop files are stored. 164 */ 165 startup 166 } 167 168 /** 169 * Control behavior of functions. 170 * See_Also: $(D writablePath) 171 */ 172 enum FolderFlag 173 { 174 none = 0, /// Don't verify that folder exist. 175 /** 176 * Create if folder does not exist. 177 * On Windows created directory will have appropriate icon and other settings specific for this kind of folder. 178 */ 179 create = 1, 180 verify = 2 /// Verify that folder exists. 181 } 182 183 /** 184 * Current user home directory. 185 * Returns: Path to user home directory, or an empty string if could not determine home directory. 186 * Relies on environment variables. 187 * Note: This function does not cache its result. 188 */ 189 string homeDir() nothrow @safe 190 { 191 try { 192 version(Windows) { 193 //Use GetUserProfileDirectoryW from Userenv.dll? 194 string home = environment.get("USERPROFILE"); 195 if (home.empty) { 196 string homeDrive = environment.get("HOMEDRIVE"); 197 string homePath = environment.get("HOMEPATH"); 198 if (homeDrive.length && homePath.length) { 199 home = homeDrive ~ homePath; 200 } 201 } 202 return home; 203 } else { 204 string home = environment.get("HOME"); 205 return home; 206 } 207 } 208 catch (Exception e) { 209 debug { 210 @trusted void writeException(Exception e) nothrow { 211 collectException(stderr.writefln("Error when getting home directory %s", e.msg)); 212 } 213 writeException(e); 214 } 215 return null; 216 } 217 } 218 219 /** 220 * Getting writable paths for various locations. 221 * Returns: Path where files of $(U type) should be written to by current user, or an empty string if could not determine path. 222 * Params: 223 * type = Directory to lookup. 224 * params = Union of $(D FolderFlag)s. 225 * Note: This function does not cache its results. 226 * See_Also: $(D StandardPath), $(D FolderFlag) 227 */ 228 string writablePath(StandardPath type, FolderFlag params = FolderFlag.none) nothrow @safe; 229 230 /** 231 * Getting paths for various locations. 232 * Returns: Array of paths where files of $(U type) belong including one returned by $(D writablePath), or an empty array if no paths are defined for $(U type). 233 * This function does not ensure if all returned paths exist and appear to be accessible directories. Returned strings are not required to be unique. 234 * Note: This function does cache its results. 235 * It may cause performance impact to call this function often since retrieving some paths can be relatively expensive operation. 236 * See_Also: $(D StandardPath), $(D writablePath) 237 */ 238 string[] standardPaths(StandardPath type) nothrow @safe; 239 240 241 version(D_Ddoc) 242 { 243 /** 244 * Path to $(B Roaming) data directory. Windows only. 245 * Returns: User's Roaming directory. On fail returns an empty string. 246 * See_Also: $(D writablePath), $(D FolderFlag) 247 */ 248 string roamingPath(FolderFlag params = FolderFlag.none) nothrow @safe; 249 250 /** 251 * Location where games may store their saves. Windows only. 252 * Note: This is common path for games. One should use subfolder for their game saves. 253 * Returns: User's Saved Games directory. On fail returns an empty string. 254 * See_Also: $(D writablePath), $(D FolderFlag) 255 */ 256 string savedGames(FolderFlag params = FolderFlag.none) nothrow @safe; 257 } 258 259 version(Windows) { 260 261 private { 262 enum { 263 CSIDL_DESKTOP = 0, 264 CSIDL_INTERNET, 265 CSIDL_PROGRAMS, 266 CSIDL_CONTROLS, 267 CSIDL_PRINTERS, 268 CSIDL_PERSONAL, 269 CSIDL_FAVORITES, 270 CSIDL_STARTUP, 271 CSIDL_RECENT, 272 CSIDL_SENDTO, 273 CSIDL_BITBUCKET, 274 CSIDL_STARTMENU, // = 11 275 CSIDL_MYMUSIC = 13, 276 CSIDL_MYVIDEO, // = 14 277 CSIDL_DESKTOPDIRECTORY = 16, 278 CSIDL_DRIVES, 279 CSIDL_NETWORK, 280 CSIDL_NETHOOD, 281 CSIDL_FONTS, 282 CSIDL_TEMPLATES, 283 CSIDL_COMMON_STARTMENU, 284 CSIDL_COMMON_PROGRAMS, 285 CSIDL_COMMON_STARTUP, 286 CSIDL_COMMON_DESKTOPDIRECTORY, 287 CSIDL_APPDATA, 288 CSIDL_PRINTHOOD, 289 CSIDL_LOCAL_APPDATA, 290 CSIDL_ALTSTARTUP, 291 CSIDL_COMMON_ALTSTARTUP, 292 CSIDL_COMMON_FAVORITES, 293 CSIDL_INTERNET_CACHE, 294 CSIDL_COOKIES, 295 CSIDL_HISTORY, 296 CSIDL_COMMON_APPDATA, 297 CSIDL_WINDOWS, 298 CSIDL_SYSTEM, 299 CSIDL_PROGRAM_FILES, 300 CSIDL_MYPICTURES, 301 CSIDL_PROFILE, 302 CSIDL_SYSTEMX86, 303 CSIDL_PROGRAM_FILESX86, 304 CSIDL_PROGRAM_FILES_COMMON, 305 CSIDL_PROGRAM_FILES_COMMONX86, 306 CSIDL_COMMON_TEMPLATES, 307 CSIDL_COMMON_DOCUMENTS, 308 CSIDL_COMMON_ADMINTOOLS, 309 CSIDL_ADMINTOOLS, 310 CSIDL_CONNECTIONS, // = 49 311 CSIDL_COMMON_MUSIC = 53, 312 CSIDL_COMMON_PICTURES, 313 CSIDL_COMMON_VIDEO, 314 CSIDL_RESOURCES, 315 CSIDL_RESOURCES_LOCALIZED, 316 CSIDL_COMMON_OEM_LINKS, 317 CSIDL_CDBURN_AREA, // = 59 318 CSIDL_COMPUTERSNEARME = 61, 319 CSIDL_FLAG_DONT_VERIFY = 0x4000, 320 CSIDL_FLAG_CREATE = 0x8000, 321 CSIDL_FLAG_MASK = 0xFF00 322 } 323 324 enum { 325 KF_FLAG_SIMPLE_IDLIST = 0x00000100, 326 KF_FLAG_NOT_PARENT_RELATIVE = 0x00000200, 327 KF_FLAG_DEFAULT_PATH = 0x00000400, 328 KF_FLAG_INIT = 0x00000800, 329 KF_FLAG_NO_ALIAS = 0x00001000, 330 KF_FLAG_DONT_UNEXPAND = 0x00002000, 331 KF_FLAG_DONT_VERIFY = 0x00004000, 332 KF_FLAG_CREATE = 0x00008000, 333 KF_FLAG_NO_APPCONTAINER_REDIRECTION = 0x00010000, 334 KF_FLAG_ALIAS_ONLY = 0x80000000 335 }; 336 337 alias GUID KNOWNFOLDERID; 338 339 enum KNOWNFOLDERID FOLDERID_LocalAppData = {0xf1b32785, 0x6fba, 0x4fcf, [0x9d,0x55,0x7b,0x8e,0x7f,0x15,0x70,0x91]}; 340 enum KNOWNFOLDERID FOLDERID_RoamingAppData = {0x3eb685db, 0x65f9, 0x4cf6, [0xa0,0x3a,0xe3,0xef,0x65,0x72,0x9f,0x3d]}; 341 342 enum KNOWNFOLDERID FOLDERID_Desktop = {0xb4bfcc3a, 0xdb2c, 0x424c, [0xb0,0x29,0x7f,0xe9,0x9a,0x87,0xc6,0x41]}; 343 enum KNOWNFOLDERID FOLDERID_Documents = {0xfdd39ad0, 0x238f, 0x46af, [0xad,0xb4,0x6c,0x85,0x48,0x3,0x69,0xc7]}; 344 enum KNOWNFOLDERID FOLDERID_Downloads = {0x374de290, 0x123f, 0x4565, [0x91,0x64,0x39,0xc4,0x92,0x5e,0x46,0x7b]}; 345 enum KNOWNFOLDERID FOLDERID_Favorites = {0x1777f761, 0x68ad, 0x4d8a, [0x87,0xbd,0x30,0xb7,0x59,0xfa,0x33,0xdd]}; 346 enum KNOWNFOLDERID FOLDERID_Links = {0xbfb9d5e0, 0xc6a9, 0x404c, [0xb2,0xb2,0xae,0x6d,0xb6,0xaf,0x49,0x68]}; 347 enum KNOWNFOLDERID FOLDERID_Music = {0x4bd8d571, 0x6d19, 0x48d3, [0xbe,0x97,0x42,0x22,0x20,0x8,0xe,0x43]}; 348 enum KNOWNFOLDERID FOLDERID_Pictures = {0x33e28130, 0x4e1e, 0x4676, [0x83,0x5a,0x98,0x39,0x5c,0x3b,0xc3,0xbb]}; 349 enum KNOWNFOLDERID FOLDERID_Programs = {0xa77f5d77, 0x2e2b, 0x44c3, [0xa6,0xa2,0xab,0xa6,0x1,0x5,0x4a,0x51]}; 350 enum KNOWNFOLDERID FOLDERID_SavedGames = {0x4c5c32ff, 0xbb9d, 0x43b0, [0xb5,0xb4,0x2d,0x72,0xe5,0x4e,0xaa,0xa4]}; 351 enum KNOWNFOLDERID FOLDERID_Startup = {0xb97d20bb, 0xf46a, 0x4c97, [0xba,0x10,0x5e,0x36,0x8,0x43,0x8,0x54]}; 352 enum KNOWNFOLDERID FOLDERID_Templates = {0xa63293e8, 0x664e, 0x48db, [0xa0,0x79,0xdf,0x75,0x9e,0x5,0x9,0xf7]}; 353 enum KNOWNFOLDERID FOLDERID_Videos = {0x18989b1d, 0x99b5, 0x455b, [0x84,0x1c,0xab,0x7c,0x74,0xe4,0xdd,0xfc]}; 354 355 enum KNOWNFOLDERID FOLDERID_Fonts = {0xfd228cb7, 0xae11, 0x4ae3, [0x86,0x4c,0x16,0xf3,0x91,0xa,0xb8,0xfe]}; 356 enum KNOWNFOLDERID FOLDERID_ProgramData = {0x62ab5d82, 0xfdc1, 0x4dc3, [0xa9,0xdd,0x7,0xd,0x1d,0x49,0x5d,0x97]}; 357 enum KNOWNFOLDERID FOLDERID_CommonPrograms = {0x139d44e, 0x6afe, 0x49f2, [0x86,0x90,0x3d,0xaf,0xca,0xe6,0xff,0xb8]}; 358 enum KNOWNFOLDERID FOLDERID_CommonStartup = {0x82a5ea35, 0xd9cd, 0x47c5, [0x96,0x29,0xe1,0x5d,0x2f,0x71,0x4e,0x6e]}; 359 enum KNOWNFOLDERID FOLDERID_CommonTemplates = {0xb94237e7, 0x57ac, 0x4347, [0x91,0x51,0xb0,0x8c,0x6c,0x32,0xd1,0xf7]}; 360 361 enum KNOWNFOLDERID FOLDERID_PublicDesktop = {0xc4aa340d, 0xf20f, 0x4863, [0xaf,0xef,0xf8,0x7e,0xf2,0xe6,0xba,0x25]}; 362 enum KNOWNFOLDERID FOLDERID_PublicDocuments = {0xed4824af, 0xdce4, 0x45a8, [0x81,0xe2,0xfc,0x79,0x65,0x8,0x36,0x34]}; 363 enum KNOWNFOLDERID FOLDERID_PublicDownloads = {0x3d644c9b, 0x1fb8, 0x4f30, [0x9b,0x45,0xf6,0x70,0x23,0x5f,0x79,0xc0]}; 364 enum KNOWNFOLDERID FOLDERID_PublicMusic = {0x3214fab5, 0x9757, 0x4298, [0xbb,0x61,0x92,0xa9,0xde,0xaa,0x44,0xff]}; 365 enum KNOWNFOLDERID FOLDERID_PublicPictures = {0xb6ebfb86, 0x6907, 0x413c, [0x9a,0xf7,0x4f,0xc2,0xab,0xf0,0x7c,0xc5]}; 366 enum KNOWNFOLDERID FOLDERID_PublicVideos = {0x2400183a, 0x6185, 0x49fb, [0xa2,0xd8,0x4a,0x39,0x2a,0x60,0x2b,0xa3]}; 367 } 368 369 private { 370 extern(Windows) @nogc @system BOOL _dummy_SHGetSpecialFolderPath(HWND, wchar*, int, BOOL) nothrow { return 0; } 371 extern(Windows) @nogc @system HRESULT _dummy_SHGetKnownFolderPath(const(KNOWNFOLDERID)* rfid, DWORD dwFlags, HANDLE hToken, wchar** ppszPath) nothrow { return 0; } 372 extern(Windows) @nogc @system void _dummy_CoTaskMemFree(void* pv) nothrow {return;} 373 374 __gshared typeof(&_dummy_SHGetSpecialFolderPath) ptrSHGetSpecialFolderPath = null; 375 __gshared typeof(&_dummy_SHGetKnownFolderPath) ptrSHGetKnownFolderPath = null; 376 __gshared typeof(&_dummy_CoTaskMemFree) ptrCoTaskMemFree = null; 377 378 @nogc @trusted bool hasSHGetSpecialFolderPath() nothrow { 379 return ptrSHGetSpecialFolderPath !is null; 380 } 381 382 @nogc @trusted bool hasSHGetKnownFolderPath() nothrow { 383 return ptrSHGetKnownFolderPath !is null && ptrCoTaskMemFree !is null; 384 } 385 } 386 387 shared static this() 388 { 389 HMODULE shellLib = LoadLibraryA("Shell32"); 390 if (shellLib !is null) { 391 ptrSHGetKnownFolderPath = cast(typeof(ptrSHGetKnownFolderPath))enforce(GetProcAddress(shellLib, "SHGetKnownFolderPath")); 392 if (ptrSHGetKnownFolderPath) { 393 HMODULE ole = LoadLibraryA("Ole32"); 394 if (ole !is null) { 395 ptrCoTaskMemFree = cast(typeof(ptrCoTaskMemFree))enforce(GetProcAddress(ole, "CoTaskMemFree")); 396 if (!ptrCoTaskMemFree) { 397 FreeLibrary(ole); 398 } 399 } 400 } 401 402 if (!hasSHGetKnownFolderPath()) { 403 ptrSHGetSpecialFolderPath = cast(typeof(ptrSHGetSpecialFolderPath))GetProcAddress(shellLib, "SHGetSpecialFolderPathW"); 404 } 405 } 406 } 407 408 private string getCSIDLFolder(int csidl, FolderFlag params = FolderFlag.none) nothrow @trusted { 409 import core.stdc.wchar_ : wcslen; 410 411 if (params & FolderFlag.create) { 412 csidl |= CSIDL_FLAG_CREATE; 413 } 414 if (!(params & FolderFlag.verify)) { 415 csidl |= CSIDL_FLAG_DONT_VERIFY; 416 } 417 wchar[MAX_PATH] path = void; 418 if (hasSHGetSpecialFolderPath() && ptrSHGetSpecialFolderPath(null, path.ptr, csidl, FALSE)) { 419 size_t len = wcslen(path.ptr); 420 try { 421 return toUTF8(path[0..len]); 422 } catch(Exception e) { 423 424 } 425 } 426 return null; 427 } 428 429 private string getKnownFolder(const(KNOWNFOLDERID) folder, FolderFlag params = FolderFlag.none) nothrow @trusted { 430 import core.stdc.wchar_ : wcslen; 431 432 wchar* str; 433 434 DWORD flags = 0; 435 if (params & FolderFlag.create) { 436 flags |= KF_FLAG_CREATE; 437 } 438 if (!(params & FolderFlag.verify)) { 439 flags |= KF_FLAG_DONT_VERIFY; 440 } 441 442 if (hasSHGetKnownFolderPath() && ptrSHGetKnownFolderPath(&folder, flags, null, &str) == S_OK) { 443 scope(exit) ptrCoTaskMemFree(str); 444 try { 445 return str[0..wcslen(str)].toUTF8; 446 } catch(Exception e) { 447 448 } 449 } 450 return null; 451 } 452 453 string roamingPath(FolderFlag params = FolderFlag.none) nothrow @safe 454 { 455 if (hasSHGetKnownFolderPath()) { 456 return getKnownFolder(FOLDERID_RoamingAppData, params); 457 } else if (hasSHGetSpecialFolderPath()) { 458 return getCSIDLFolder(CSIDL_APPDATA, params); 459 } else { 460 return null; 461 } 462 } 463 464 string savedGames(FolderFlag params = FolderFlag.none) nothrow @safe 465 { 466 if (hasSHGetKnownFolderPath()) { 467 return getKnownFolder(FOLDERID_SavedGames, params); 468 } else { 469 return null; 470 } 471 } 472 473 string writablePath(StandardPath type, FolderFlag params = FolderFlag.none) nothrow @safe 474 { 475 if (hasSHGetKnownFolderPath()) { 476 final switch(type) { 477 case StandardPath.config: 478 case StandardPath.data: 479 return getKnownFolder(FOLDERID_LocalAppData, params); 480 case StandardPath.cache: 481 return null; 482 case StandardPath.desktop: 483 return getKnownFolder(FOLDERID_Desktop, params); 484 case StandardPath.documents: 485 return getKnownFolder(FOLDERID_Documents, params); 486 case StandardPath.pictures: 487 return getKnownFolder(FOLDERID_Pictures, params); 488 case StandardPath.music: 489 return getKnownFolder(FOLDERID_Music, params); 490 case StandardPath.videos: 491 return getKnownFolder(FOLDERID_Videos, params); 492 case StandardPath.downloads: 493 return getKnownFolder(FOLDERID_Downloads, params); 494 case StandardPath.templates: 495 return getKnownFolder(FOLDERID_Templates, params); 496 case StandardPath.publicShare: 497 return null; 498 case StandardPath.fonts: 499 return null; 500 case StandardPath.applications: 501 return getKnownFolder(FOLDERID_Programs, params); 502 case StandardPath.startup: 503 return getKnownFolder(FOLDERID_Startup, params); 504 } 505 } else if (hasSHGetSpecialFolderPath()) { 506 final switch(type) { 507 case StandardPath.config: 508 case StandardPath.data: 509 return getCSIDLFolder(CSIDL_LOCAL_APPDATA, params); 510 case StandardPath.cache: 511 return null; 512 case StandardPath.desktop: 513 return getCSIDLFolder(CSIDL_DESKTOPDIRECTORY, params); 514 case StandardPath.documents: 515 return getCSIDLFolder(CSIDL_PERSONAL, params); 516 case StandardPath.pictures: 517 return getCSIDLFolder(CSIDL_MYPICTURES, params); 518 case StandardPath.music: 519 return getCSIDLFolder(CSIDL_MYMUSIC, params); 520 case StandardPath.videos: 521 return getCSIDLFolder(CSIDL_MYVIDEO, params); 522 case StandardPath.downloads: 523 return null; 524 case StandardPath.templates: 525 return getCSIDLFolder(CSIDL_TEMPLATES, params); 526 case StandardPath.publicShare: 527 return null; 528 case StandardPath.fonts: 529 return null; 530 case StandardPath.applications: 531 return getCSIDLFolder(CSIDL_PROGRAMS, params); 532 case StandardPath.startup: 533 return getCSIDLFolder(CSIDL_STARTUP, params); 534 } 535 } else { 536 return null; 537 } 538 } 539 540 string[] standardPaths(StandardPath type) nothrow @safe 541 { 542 string commonPath; 543 544 if (hasSHGetKnownFolderPath()) { 545 switch(type) { 546 case StandardPath.config: 547 case StandardPath.data: 548 commonPath = getKnownFolder(FOLDERID_ProgramData); 549 break; 550 case StandardPath.desktop: 551 commonPath = getKnownFolder(FOLDERID_PublicDesktop); 552 break; 553 case StandardPath.documents: 554 commonPath = getKnownFolder(FOLDERID_PublicDocuments); 555 break; 556 case StandardPath.pictures: 557 commonPath = getKnownFolder(FOLDERID_PublicPictures); 558 break; 559 case StandardPath.music: 560 commonPath = getKnownFolder(FOLDERID_PublicMusic); 561 break; 562 case StandardPath.videos: 563 commonPath = getKnownFolder(FOLDERID_PublicVideos); 564 break; 565 case StandardPath.downloads: 566 commonPath = getKnownFolder(FOLDERID_PublicDownloads); 567 break; 568 case StandardPath.templates: 569 commonPath = getKnownFolder(FOLDERID_CommonTemplates); 570 break; 571 case StandardPath.fonts: 572 commonPath = getKnownFolder(FOLDERID_Fonts); 573 break; 574 case StandardPath.applications: 575 commonPath = getKnownFolder(FOLDERID_CommonPrograms); 576 break; 577 case StandardPath.startup: 578 commonPath = getKnownFolder(FOLDERID_CommonStartup); 579 break; 580 default: 581 break; 582 } 583 } else if (hasSHGetSpecialFolderPath()) { 584 switch(type) { 585 case StandardPath.config: 586 case StandardPath.data: 587 commonPath = getCSIDLFolder(CSIDL_COMMON_APPDATA); 588 break; 589 case StandardPath.desktop: 590 commonPath = getCSIDLFolder(CSIDL_COMMON_DESKTOPDIRECTORY); 591 break; 592 case StandardPath.documents: 593 commonPath = getCSIDLFolder(CSIDL_COMMON_DOCUMENTS); 594 break; 595 case StandardPath.pictures: 596 commonPath = getCSIDLFolder(CSIDL_COMMON_PICTURES); 597 break; 598 case StandardPath.music: 599 commonPath = getCSIDLFolder(CSIDL_COMMON_MUSIC); 600 break; 601 case StandardPath.videos: 602 commonPath = getCSIDLFolder(CSIDL_COMMON_VIDEO); 603 break; 604 case StandardPath.templates: 605 commonPath = getCSIDLFolder(CSIDL_COMMON_TEMPLATES); 606 break; 607 case StandardPath.fonts: 608 commonPath = getCSIDLFolder(CSIDL_FONTS); 609 break; 610 case StandardPath.applications: 611 commonPath = getCSIDLFolder(CSIDL_COMMON_PROGRAMS); 612 break; 613 case StandardPath.startup: 614 commonPath = getCSIDLFolder(CSIDL_COMMON_STARTUP); 615 break; 616 default: 617 break; 618 } 619 } 620 621 string[] paths; 622 string userPath = writablePath(type); 623 if (userPath.length) 624 paths ~= userPath; 625 if (commonPath.length) 626 paths ~= commonPath; 627 return paths; 628 } 629 } else version(OSX) { 630 private { 631 version(StandardPathsCocoa) { 632 alias size_t NSUInteger; 633 634 635 enum objectiveC_declarations = q{ 636 extern (Objective-C) 637 interface NSString 638 { 639 NSString initWithUTF8String(in char* str) @selector("initWithUTF8String:"); 640 const(char)* UTF8String() @selector("UTF8String"); 641 void release() @selector("release"); 642 } 643 644 extern(Objective-C) 645 interface NSArray 646 { 647 NSString objectAtIndex(size_t) @selector("objectAtIndex:"); 648 NSString firstObject() @selector("firstObject"); 649 NSUInteger count() @selector("count"); 650 void release() @selector("release"); 651 } 652 653 extern(Objective-C) 654 interface NSURL 655 { 656 NSString absoluteString() @selector("absoluteString"); 657 void release() @selector("release"); 658 } 659 660 extern(Objective-C) 661 interface NSError 662 { 663 664 } 665 666 extern (C) NSFileManager objc_lookUpClass(in char* name); 667 668 extern(Objective-C) 669 interface NSFileManager 670 { 671 NSFileManager defaultManager() @selector("defaultManager"); 672 NSURL URLForDirectory(NSSearchPathDirectory, NSSearchPathDomainMask domain, NSURL url, int shouldCreate, NSError* error) @selector("URLForDirectory:inDomain:appropriateForURL:create:error:"); 673 } 674 }; 675 676 mixin(objectiveC_declarations); 677 678 enum : NSUInteger { 679 NSApplicationDirectory = 1, 680 NSDemoApplicationDirectory, 681 NSDeveloperApplicationDirectory, 682 NSAdminApplicationDirectory, 683 NSLibraryDirectory, 684 NSDeveloperDirectory, 685 NSUserDirectory, 686 NSDocumentationDirectory, 687 NSDocumentDirectory, 688 NSCoreServiceDirectory, 689 NSAutosavedInformationDirectory = 11, 690 NSDesktopDirectory = 12, 691 NSCachesDirectory = 13, 692 NSApplicationSupportDirectory = 14, 693 NSDownloadsDirectory = 15, 694 NSInputMethodsDirectory = 16, 695 NSMoviesDirectory = 17, 696 NSMusicDirectory = 18, 697 NSPicturesDirectory = 19, 698 NSPrinterDescriptionDirectory = 20, 699 NSSharedPublicDirectory = 21, 700 NSPreferencePanesDirectory = 22, 701 NSItemReplacementDirectory = 99, 702 NSAllApplicationsDirectory = 100, 703 NSAllLibrariesDirectory = 101, 704 }; 705 706 alias NSUInteger NSSearchPathDirectory; 707 708 enum : NSUInteger { 709 NSUserDomainMask = 1, 710 NSLocalDomainMask = 2, 711 NSNetworkDomainMask = 4, 712 NSSystemDomainMask = 8, 713 NSAllDomainsMask = 0x0ffff, 714 }; 715 716 alias NSUInteger NSSearchPathDomainMask; 717 718 string domainDir(NSSearchPathDirectory dir, NSSearchPathDomainMask domain, bool shouldCreate = false) nothrow @trusted 719 { 720 import std.uri; 721 import std.algorithm : startsWith; 722 723 try { 724 auto managerInterface = objc_lookUpClass("NSFileManager"); 725 if (!managerInterface) { 726 return null; 727 } 728 729 auto manager = managerInterface.defaultManager(); 730 if (!manager) { 731 return null; 732 } 733 734 NSURL url = manager.URLForDirectory(dir, domain, null, shouldCreate, null); 735 if (!url) { 736 return null; 737 } 738 scope(exit) url.release(); 739 NSString nsstr = url.absoluteString(); 740 scope(exit) nsstr.release(); 741 742 string str = fromStringz(nsstr.UTF8String()).idup; 743 744 enum fileProtocol = "file://"; 745 if (str.startsWith(fileProtocol)) { 746 str = str.decode()[fileProtocol.length..$]; 747 if (str.length > 1 && str[$-1] == '/') { 748 return str[0..$-1]; 749 } else { 750 return str; 751 } 752 } 753 } catch(Exception e) { 754 755 } 756 return null; 757 } 758 } else { 759 private enum : short { 760 kOnSystemDisk = -32768L, /* previously was 0x8000 but that is an unsigned value whereas vRefNum is signed*/ 761 kOnAppropriateDisk = -32767, /* Generally, the same as kOnSystemDisk, but it's clearer that this isn't always the 'boot' disk.*/ 762 /* Folder Domains - Carbon only. The constants above can continue to be used, but the folder/volume returned will*/ 763 /* be from one of the domains below.*/ 764 kSystemDomain = -32766, /* Read-only system hierarchy.*/ 765 kLocalDomain = -32765, /* All users of a single machine have access to these resources.*/ 766 kNetworkDomain = -32764, /* All users configured to use a common network server has access to these resources.*/ 767 kUserDomain = -32763, /* Read/write. Resources that are private to the user.*/ 768 kClassicDomain = -32762, /* Domain referring to the currently configured Classic System Folder. Not supported in Mac OS X Leopard and later.*/ 769 kFolderManagerLastDomain = -32760 770 } 771 772 private @nogc int k(string s) nothrow { 773 return s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3]; 774 } 775 776 private enum { 777 kDesktopFolderType = k("desk"), /* the desktop folder; objects in this folder show on the desktop. */ 778 kTrashFolderType = k("trsh"), /* the trash folder; objects in this folder show up in the trash */ 779 kWhereToEmptyTrashFolderType = k("empt"), /* the "empty trash" folder; Finder starts empty from here down */ 780 kFontsFolderType = k("font"), /* Fonts go here */ 781 kPreferencesFolderType = k("pref"), /* preferences for applications go here */ 782 kSystemPreferencesFolderType = k("sprf"), /* the PreferencePanes folder, where Mac OS X Preference Panes go */ 783 kTemporaryFolderType = k("temp"), /* On Mac OS X, each user has their own temporary items folder, and the Folder Manager attempts to set permissions of these*/ 784 /* folders such that other users can not access the data inside. On Mac OS X 10.4 and later the data inside the temporary*/ 785 /* items folder is deleted at logout and at boot, but not otherwise. Earlier version of Mac OS X would delete items inside*/ 786 /* the temporary items folder after a period of inaccess. You can ask for a temporary item in a specific domain or on a */ 787 /* particular volume by FSVolumeRefNum. If you want a location for temporary items for a short time, then use either*/ 788 /* ( kUserDomain, kkTemporaryFolderType ) or ( kSystemDomain, kTemporaryFolderType ). The kUserDomain varient will always be*/ 789 /* on the same volume as the user's home folder, while the kSystemDomain version will be on the same volume as /var/tmp/ ( and*/ 790 /* will probably be on the local hard drive in case the user's home is a network volume ). If you want a location for a temporary*/ 791 /* file or folder to use for saving a document, especially if you want to use FSpExchangeFile() to implement a safe-save, then*/ 792 /* ask for the temporary items folder on the same volume as the file you are safe saving.*/ 793 /* However, be prepared for a failure to find a temporary folder in any domain or on any volume. Some volumes may not have*/ 794 /* a location for a temporary folder, or the permissions of the volume may be such that the Folder Manager can not return*/ 795 /* a temporary folder for the volume.*/ 796 /* If your application creates an item in a temporary items older you should delete that item as soon as it is not needed,*/ 797 /* and certainly before your application exits, since otherwise the item is consuming disk space until the user logs out or*/ 798 /* restarts. Any items left inside a temporary items folder should be moved into a folder inside the Trash folder on the disk*/ 799 /* when the user logs in, inside a folder named "Recovered items", in case there is anything useful to the end user.*/ 800 kChewableItemsFolderType = k("flnt"), /* similar to kTemporaryItemsFolderType, except items in this folder are deleted at boot or when the disk is unmounted */ 801 kTemporaryItemsInCacheDataFolderType = k("vtmp"), /* A folder inside the kCachedDataFolderType for the given domain which can be used for transient data*/ 802 kApplicationsFolderType = k("apps"), /* Applications on Mac OS X are typically put in this folder ( or a subfolder ).*/ 803 kVolumeRootFolderType = k("root"), /* root folder of a volume or domain */ 804 kDomainTopLevelFolderType = k("dtop"), /* The top-level of a Folder domain, e.g. "/System"*/ 805 kDomainLibraryFolderType = k("dlib"), /* the Library subfolder of a particular domain*/ 806 kUsersFolderType = k("usrs"), /* "Users" folder, usually contains one folder for each user. */ 807 kCurrentUserFolderType = k("cusr"), /* The folder for the currently logged on user; domain passed in is ignored. */ 808 kSharedUserDataFolderType = k("sdat"), /* A Shared folder, readable & writeable by all users */ 809 kCachedDataFolderType = k("cach"), /* Contains various cache files for different clients*/ 810 kDownloadsFolderType = k("down"), /* Refers to the ~/Downloads folder*/ 811 kApplicationSupportFolderType = k("asup"), /* third-party items and folders */ 812 813 814 kDocumentsFolderType = k("docs"), /* User documents are typically put in this folder ( or a subfolder ).*/ 815 kPictureDocumentsFolderType = k("pdoc"), /* Refers to the "Pictures" folder in a users home directory*/ 816 kMovieDocumentsFolderType = k("mdoc"), /* Refers to the "Movies" folder in a users home directory*/ 817 kMusicDocumentsFolderType = 0xB5646F63/*'µdoc'*/, /* Refers to the "Music" folder in a users home directory*/ 818 kInternetSitesFolderType = k("site"), /* Refers to the "Sites" folder in a users home directory*/ 819 kPublicFolderType = k("pubb"), /* Refers to the "Public" folder in a users home directory*/ 820 821 kDropBoxFolderType = k("drop") /* Refers to the "Drop Box" folder inside the user's home directory*/ 822 }; 823 824 struct FSRef { 825 char[80] hidden; /* private to File Manager*/ 826 }; 827 828 alias ubyte Boolean; 829 alias int OSType; 830 alias short OSErr; 831 alias int OSStatus; 832 833 extern(C) @nogc @system OSErr _dummy_FSFindFolder(short, OSType, Boolean, FSRef*) nothrow { return 0; } 834 extern(C) @nogc @system OSStatus _dummy_FSRefMakePath(const(FSRef)*, char*, uint) nothrow { return 0; } 835 836 __gshared typeof(&_dummy_FSFindFolder) ptrFSFindFolder = null; 837 __gshared typeof(&_dummy_FSRefMakePath) ptrFSRefMakePath = null; 838 } 839 } 840 841 version(StandardPathsCocoa) { 842 843 } else { 844 shared static this() 845 { 846 enum carbonPath = "CoreServices.framework/Versions/A/CoreServices"; 847 848 import core.sys.posix.dlfcn; 849 850 void* handle = dlopen(toStringz(carbonPath), RTLD_NOW | RTLD_LOCAL); 851 if (handle) { 852 ptrFSFindFolder = cast(typeof(ptrFSFindFolder))dlsym(handle, "FSFindFolder"); 853 ptrFSRefMakePath = cast(typeof(ptrFSRefMakePath))dlsym(handle, "FSRefMakePath"); 854 } 855 if (ptrFSFindFolder == null || ptrFSRefMakePath == null) { 856 debug collectException(stderr.writeln("Could not load carbon functions")); 857 if (handle) dlclose(handle); 858 } 859 } 860 861 private @nogc @trusted bool isCarbonLoaded() nothrow 862 { 863 return ptrFSFindFolder != null && ptrFSRefMakePath != null; 864 } 865 866 private enum OSErr noErr = 0; 867 868 private string fsPath(short domain, OSType type, bool shouldCreate = false) nothrow @trusted 869 { 870 import std.stdio; 871 FSRef fsref; 872 if (isCarbonLoaded() && ptrFSFindFolder(domain, type, shouldCreate, &fsref) == noErr) { 873 874 char[2048] buf; 875 char* path = buf.ptr; 876 if (ptrFSRefMakePath(&fsref, path, buf.sizeof) == noErr) { 877 try { 878 return fromStringz(path).idup; 879 } 880 catch(Exception e) { 881 882 } 883 } 884 } 885 return null; 886 } 887 } 888 889 private string writablePathImpl(StandardPath type, bool shouldCreate = false) nothrow @safe 890 { 891 version(StandardPathsCocoa) { 892 final switch(type) { 893 case StandardPath.config: 894 return domainDir(NSLibraryDirectory, NSUserDomainMask, shouldCreate).maybeBuild("Preferences").createIfNeeded(shouldCreate); 895 case StandardPath.cache: 896 return domainDir(NSCachesDirectory, NSUserDomainMask, shouldCreate); 897 case StandardPath.data: 898 return domainDir(NSApplicationSupportDirectory, NSUserDomainMask, shouldCreate); 899 case StandardPath.desktop: 900 return domainDir(NSDesktopDirectory, NSUserDomainMask, shouldCreate); 901 case StandardPath.documents: 902 return domainDir(NSDocumentDirectory, NSUserDomainMask, shouldCreate); 903 case StandardPath.pictures: 904 return domainDir(NSPicturesDirectory, NSUserDomainMask, shouldCreate); 905 case StandardPath.music: 906 return domainDir(NSMusicDirectory, NSUserDomainMask, shouldCreate); 907 case StandardPath.videos: 908 return domainDir(NSMoviesDirectory, NSUserDomainMask, shouldCreate); 909 case StandardPath.downloads: 910 return domainDir(NSDownloadsDirectory, NSUserDomainMask, shouldCreate); 911 case StandardPath.templates: 912 return null; 913 case StandardPath.publicShare: 914 return domainDir(NSSharedPublicDirectory, NSUserDomainMask, shouldCreate); 915 case StandardPath.fonts: 916 return domainDir(NSLibraryDirectory, NSUserDomainMask, shouldCreate).maybeBuild("Fonts").createIfNeeded(shouldCreate); 917 case StandardPath.applications: 918 return domainDir(NSApplicationDirectory, NSUserDomainMask, shouldCreate); 919 case StandardPath.startup: 920 return null; 921 } 922 } else { 923 final switch(type) { 924 case StandardPath.config: 925 return fsPath(kUserDomain, kPreferencesFolderType, shouldCreate); 926 case StandardPath.cache: 927 return fsPath(kUserDomain, kCachedDataFolderType, shouldCreate); 928 case StandardPath.data: 929 return fsPath(kUserDomain, kApplicationSupportFolderType, shouldCreate); 930 case StandardPath.desktop: 931 return fsPath(kUserDomain, kDesktopFolderType, shouldCreate); 932 case StandardPath.documents: 933 return fsPath(kUserDomain, kDocumentsFolderType, shouldCreate); 934 case StandardPath.pictures: 935 return fsPath(kUserDomain, kPictureDocumentsFolderType, shouldCreate); 936 case StandardPath.music: 937 return fsPath(kUserDomain, kMusicDocumentsFolderType, shouldCreate); 938 case StandardPath.videos: 939 return fsPath(kUserDomain, kMovieDocumentsFolderType, shouldCreate); 940 case StandardPath.downloads: 941 return fsPath(kUserDomain, kDownloadsFolderType, shouldCreate); 942 case StandardPath.templates: 943 return null; 944 case StandardPath.publicShare: 945 return fsPath(kUserDomain, kPublicFolderType, shouldCreate); 946 case StandardPath.fonts: 947 return fsPath(kUserDomain, kFontsFolderType, shouldCreate); 948 case StandardPath.applications: 949 return fsPath(kUserDomain, kApplicationsFolderType, shouldCreate); 950 case StandardPath.startup: 951 return null; 952 } 953 } 954 } 955 956 string writablePath(StandardPath type, FolderFlag params = FolderFlag.none) nothrow @safe 957 { 958 const bool shouldCreate = (params & FolderFlag.create) != 0; 959 const bool shouldVerify = (params & FolderFlag.verify) != 0; 960 return writablePathImpl(type, shouldCreate).verifyIfNeeded(shouldVerify); 961 } 962 963 string[] standardPaths(StandardPath type) nothrow @safe 964 { 965 string commonPath; 966 967 version(StandardPathsCocoa) { 968 switch(type) { 969 case StandardPath.fonts: 970 commonPath = domainDir(NSLibraryDirectory, NSSystemDomainMask).maybeBuild("Fonts"); 971 break; 972 case StandardPath.applications: 973 commonPath = domainDir(NSApplicationDirectory, NSSystemDomainMask); 974 break; 975 case StandardPath.data: 976 commonPath = domainDir(NSApplicationSupportDirectory, NSSystemDomainMask); 977 break; 978 case StandardPath.cache: 979 commonPath = domainDir(NSCachesDirectory, NSSystemDomainMask); 980 break; 981 default: 982 break; 983 } 984 } else { 985 switch(type) { 986 case StandardPath.fonts: 987 commonPath = fsPath(kOnAppropriateDisk, kFontsFolderType); 988 break; 989 case StandardPath.applications: 990 commonPath = fsPath(kOnAppropriateDisk, kApplicationsFolderType); 991 break; 992 case StandardPath.data: 993 commonPath = fsPath(kOnAppropriateDisk, kApplicationSupportFolderType); 994 break; 995 case StandardPath.cache: 996 commonPath = fsPath(kOnAppropriateDisk, kCachedDataFolderType); 997 break; 998 default: 999 break; 1000 } 1001 } 1002 1003 string[] paths; 1004 string userPath = writablePath(type); 1005 if (userPath.length) 1006 paths ~= userPath; 1007 if (commonPath.length) 1008 paths ~= commonPath; 1009 return paths; 1010 } 1011 1012 } else { 1013 1014 static if (!isFreedesktop) { 1015 static assert(false, "Unsupported platform"); 1016 } else { 1017 public import xdgpaths; 1018 1019 private { 1020 import std.stdio : File; 1021 import std.algorithm : startsWith; 1022 import std.string; 1023 import std.traits; 1024 } 1025 1026 unittest 1027 { 1028 assert(maybeConcat(null, "path") == string.init); 1029 assert(maybeConcat("path", "/file") == "path/file"); 1030 } 1031 1032 private @trusted string getFromUserDirs(Range)(string xdgdir, string home, Range range) if (isInputRange!Range && isSomeString!(ElementType!Range)) 1033 { 1034 foreach(line; range) { 1035 line = strip(line); 1036 auto index = xdgdir.length; 1037 if (line.startsWith(xdgdir) && line.length > index && line[index] == '=') { 1038 line = line[index+1..$]; 1039 if (line.length > 2 && line[0] == '"' && line[$-1] == '"') 1040 { 1041 line = line[1..$-1]; 1042 1043 if (line.startsWith("$HOME")) { 1044 return maybeConcat(home, assumeUnique(line[5..$])); 1045 } 1046 if (line.length == 0 || line[0] != '/') { 1047 continue; 1048 } 1049 return assumeUnique(line); 1050 } 1051 } 1052 } 1053 return null; 1054 } 1055 1056 1057 unittest 1058 { 1059 string content = 1060 `# Comment 1061 1062 XDG_DOCUMENTS_DIR="$HOME/My Documents" 1063 XDG_MUSIC_DIR="/data/Music" 1064 XDG_VIDEOS_DIR="data/Video" 1065 `; 1066 string home = "/home/user"; 1067 1068 assert(getFromUserDirs("XDG_DOCUMENTS_DIR", home, content.splitLines) == "/home/user/My Documents"); 1069 assert(getFromUserDirs("XDG_MUSIC_DIR", home, content.splitLines) == "/data/Music"); 1070 assert(getFromUserDirs("XDG_DOWNLOAD_DIR", home, content.splitLines).empty); 1071 assert(getFromUserDirs("XDG_VIDEOS_DIR", home, content.splitLines).empty); 1072 } 1073 1074 private @trusted string getFromDefaultDirs(Range)(string key, string home, Range range) if (isInputRange!Range && isSomeString!(ElementType!Range)) 1075 { 1076 foreach(line; range) { 1077 line = strip(line); 1078 auto index = key.length; 1079 if (line.startsWith(key) && line.length > index && line[index] == '=') 1080 { 1081 line = line[index+1..$]; 1082 return home ~ "/" ~ assumeUnique(line); 1083 } 1084 } 1085 return null; 1086 } 1087 1088 unittest 1089 { 1090 string content = 1091 `# Comment 1092 1093 DOCUMENTS=MyDocuments 1094 PICTURES=Images 1095 `; 1096 string home = "/home/user"; 1097 assert(getFromDefaultDirs("DOCUMENTS", home, content.splitLines) == "/home/user/MyDocuments"); 1098 assert(getFromDefaultDirs("PICTURES", home, content.splitLines) == "/home/user/Images"); 1099 assert(getFromDefaultDirs("VIDEOS", home, content.splitLines).empty); 1100 } 1101 1102 private string xdgUserDir(string key, string fallback = null) nothrow @trusted { 1103 string fileName = maybeConcat(writablePath(StandardPath.config), "/user-dirs.dirs"); 1104 string home = homeDir(); 1105 try { 1106 auto f = File(fileName, "r"); 1107 auto xdgdir = "XDG_" ~ key ~ "_DIR"; 1108 auto path = getFromUserDirs(xdgdir, home, f.byLine()); 1109 if (path.length) { 1110 return path; 1111 } 1112 } catch(Exception e) { 1113 1114 } 1115 1116 if (home.length) { 1117 try { 1118 auto f = File("/etc/xdg/user-dirs.defaults", "r"); 1119 auto path = getFromDefaultDirs(key, home, f.byLine()); 1120 if (path.length) { 1121 return path; 1122 } 1123 } catch (Exception e) { 1124 1125 } 1126 if (fallback.length) { 1127 return home ~ fallback; 1128 } 1129 } 1130 return null; 1131 } 1132 1133 private string homeFontsPath() nothrow @trusted { 1134 return maybeConcat(homeDir(), "/.fonts"); 1135 } 1136 1137 private string[] fontPaths() nothrow @trusted 1138 { 1139 enum localShare = "/usr/local/share/fonts"; 1140 enum share = "/usr/share/fonts"; 1141 1142 string homeFonts = homeFontsPath(); 1143 if (homeFonts.length) { 1144 return [homeFonts, localShare, share]; 1145 } else { 1146 return [localShare, share]; 1147 } 1148 } 1149 1150 private string writablePathImpl(StandardPath type, bool shouldCreate) nothrow @safe 1151 { 1152 final switch(type) { 1153 case StandardPath.config: 1154 return xdgConfigHome(null, shouldCreate); 1155 case StandardPath.cache: 1156 return xdgCacheHome(null, shouldCreate); 1157 case StandardPath.data: 1158 return xdgDataHome(null, shouldCreate); 1159 case StandardPath.desktop: 1160 return xdgUserDir("DESKTOP", "/Desktop").createIfNeeded(shouldCreate); 1161 case StandardPath.documents: 1162 return xdgUserDir("DOCUMENTS").createIfNeeded(shouldCreate); 1163 case StandardPath.pictures: 1164 return xdgUserDir("PICTURES").createIfNeeded(shouldCreate); 1165 case StandardPath.music: 1166 return xdgUserDir("MUSIC").createIfNeeded(shouldCreate); 1167 case StandardPath.videos: 1168 return xdgUserDir("VIDEOS").createIfNeeded(shouldCreate); 1169 case StandardPath.downloads: 1170 return xdgUserDir("DOWNLOAD").createIfNeeded(shouldCreate); 1171 case StandardPath.templates: 1172 return xdgUserDir("TEMPLATES", "/Templates").createIfNeeded(shouldCreate); 1173 case StandardPath.publicShare: 1174 return xdgUserDir("PUBLICSHARE", "/Public").createIfNeeded(shouldCreate); 1175 case StandardPath.fonts: 1176 return homeFontsPath().createIfNeeded(shouldCreate); 1177 case StandardPath.applications: 1178 return xdgDataHome("applications", shouldCreate); 1179 case StandardPath.startup: 1180 return xdgConfigHome("autostart", shouldCreate); 1181 } 1182 } 1183 1184 string writablePath(StandardPath type, FolderFlag params = FolderFlag.none) nothrow @safe 1185 { 1186 const bool shouldCreate = (params & FolderFlag.create) != 0; 1187 const bool shouldVerify = (params & FolderFlag.verify) != 0; 1188 return writablePathImpl(type, shouldCreate).verifyIfNeeded(shouldVerify); 1189 } 1190 1191 string[] standardPaths(StandardPath type) nothrow @safe 1192 { 1193 string[] paths; 1194 1195 switch(type) { 1196 case StandardPath.data: 1197 return xdgAllDataDirs(); 1198 case StandardPath.config: 1199 return xdgAllConfigDirs(); 1200 case StandardPath.applications: 1201 return xdgAllDataDirs("applications"); 1202 case StandardPath.startup: 1203 return xdgAllConfigDirs("autostart"); 1204 case StandardPath.fonts: 1205 return fontPaths(); 1206 default: 1207 break; 1208 } 1209 1210 string userPath = writablePath(type); 1211 if (userPath.length) { 1212 return [userPath]; 1213 } 1214 return null; 1215 } 1216 } 1217 }