| 1 |
/******************************************************************************* |
|---|
| 2 |
|
|---|
| 3 |
copyright: Copyright (c) 2008 Kris Bell. All rights reserved |
|---|
| 4 |
|
|---|
| 5 |
license: BSD style: $(LICENSE) |
|---|
| 6 |
|
|---|
| 7 |
version: Mar 2008: Initial version |
|---|
| 8 |
|
|---|
| 9 |
author: Kris |
|---|
| 10 |
|
|---|
| 11 |
A more direct route to the file-system than FilePath. Use this |
|---|
| 12 |
if you don't need path editing features. For example, if all you |
|---|
| 13 |
want is to check some path exists, using this module would likely |
|---|
| 14 |
be more convenient than FilePath. For example: |
|---|
| 15 |
--- |
|---|
| 16 |
if (exists ("some/file/path")) |
|---|
| 17 |
... |
|---|
| 18 |
--- |
|---|
| 19 |
|
|---|
| 20 |
These functions may be less efficient than FilePath because they |
|---|
| 21 |
generally attach a null to the filename for each underlying O/S |
|---|
| 22 |
call. Use Path when you need pedestrian access to the file-system, |
|---|
| 23 |
and are not manipulating the path components. Use FilePath where |
|---|
| 24 |
path editing or mutation is desired. |
|---|
| 25 |
|
|---|
| 26 |
We encourage the use of "scoped import" with this module, such as |
|---|
| 27 |
--- |
|---|
| 28 |
import Path = tango.io.Path; |
|---|
| 29 |
|
|---|
| 30 |
if (Path.exists ("some/file/path")) |
|---|
| 31 |
... |
|---|
| 32 |
--- |
|---|
| 33 |
|
|---|
| 34 |
Also residing here is a lightweight path-parser, which splits a |
|---|
| 35 |
filepath into constituent components. See PathParser below: |
|---|
| 36 |
--- |
|---|
| 37 |
auto p = Path.parse ("some/file/path"); |
|---|
| 38 |
auto path = p.path; |
|---|
| 39 |
auto name = p.name; |
|---|
| 40 |
auto suffix = p.suffix; |
|---|
| 41 |
... |
|---|
| 42 |
... |
|---|
| 43 |
--- |
|---|
| 44 |
|
|---|
| 45 |
Compile with -version=Win32SansUnicode to enable Win95 & Win32s |
|---|
| 46 |
file support. |
|---|
| 47 |
|
|---|
| 48 |
*******************************************************************************/ |
|---|
| 49 |
|
|---|
| 50 |
module tango.io.Path; |
|---|
| 51 |
|
|---|
| 52 |
private import tango.sys.Common; |
|---|
| 53 |
|
|---|
| 54 |
private import tango.io.model.IFile : FileConst; |
|---|
| 55 |
|
|---|
| 56 |
public import tango.time.Time : Time, TimeSpan; |
|---|
| 57 |
|
|---|
| 58 |
public import tango.core.Exception : IOException, IllegalArgumentException; |
|---|
| 59 |
|
|---|
| 60 |
|
|---|
| 61 |
/******************************************************************************* |
|---|
| 62 |
|
|---|
| 63 |
Various imports |
|---|
| 64 |
|
|---|
| 65 |
*******************************************************************************/ |
|---|
| 66 |
|
|---|
| 67 |
version (Win32) |
|---|
| 68 |
{ |
|---|
| 69 |
version (Win32SansUnicode) |
|---|
| 70 |
{ |
|---|
| 71 |
private extern (C) int strlen (char *s); |
|---|
| 72 |
private alias WIN32_FIND_DATA FIND_DATA; |
|---|
| 73 |
} |
|---|
| 74 |
else |
|---|
| 75 |
{ |
|---|
| 76 |
private extern (C) int wcslen (wchar *s); |
|---|
| 77 |
private alias WIN32_FIND_DATAW FIND_DATA; |
|---|
| 78 |
} |
|---|
| 79 |
} |
|---|
| 80 |
|
|---|
| 81 |
version (Posix) |
|---|
| 82 |
{ |
|---|
| 83 |
private import tango.stdc.stdio; |
|---|
| 84 |
private import tango.stdc.string; |
|---|
| 85 |
private import tango.stdc.posix.utime; |
|---|
| 86 |
private import tango.stdc.posix.dirent; |
|---|
| 87 |
} |
|---|
| 88 |
|
|---|
| 89 |
|
|---|
| 90 |
/******************************************************************************* |
|---|
| 91 |
|
|---|
| 92 |
Wraps the O/S specific calls with a D API. Note that these accept |
|---|
| 93 |
null-terminated strings only, which is why it's not public. We need |
|---|
| 94 |
this declared first to avoid forward-reference issues |
|---|
| 95 |
|
|---|
| 96 |
*******************************************************************************/ |
|---|
| 97 |
|
|---|
| 98 |
package struct FS |
|---|
| 99 |
{ |
|---|
| 100 |
/*********************************************************************** |
|---|
| 101 |
|
|---|
| 102 |
TimeStamp information. Accurate to whatever the F/S supports |
|---|
| 103 |
|
|---|
| 104 |
***********************************************************************/ |
|---|
| 105 |
|
|---|
| 106 |
struct Stamps |
|---|
| 107 |
{ |
|---|
| 108 |
Time created, /// time created |
|---|
| 109 |
accessed, /// last time accessed |
|---|
| 110 |
modified; /// last time modified |
|---|
| 111 |
} |
|---|
| 112 |
|
|---|
| 113 |
/*********************************************************************** |
|---|
| 114 |
|
|---|
| 115 |
Passed around during file-scanning |
|---|
| 116 |
|
|---|
| 117 |
***********************************************************************/ |
|---|
| 118 |
|
|---|
| 119 |
struct FileInfo |
|---|
| 120 |
{ |
|---|
| 121 |
char[] path, |
|---|
| 122 |
name; |
|---|
| 123 |
ulong bytes; |
|---|
| 124 |
bool folder; |
|---|
| 125 |
} |
|---|
| 126 |
|
|---|
| 127 |
/*********************************************************************** |
|---|
| 128 |
|
|---|
| 129 |
Some fruct glue for directory listings |
|---|
| 130 |
|
|---|
| 131 |
***********************************************************************/ |
|---|
| 132 |
|
|---|
| 133 |
struct Listing |
|---|
| 134 |
{ |
|---|
| 135 |
char[] folder; |
|---|
| 136 |
|
|---|
| 137 |
int opApply (int delegate(ref FileInfo) dg) |
|---|
| 138 |
{ |
|---|
| 139 |
char[256] tmp = void; |
|---|
| 140 |
auto path = strz (folder, tmp); |
|---|
| 141 |
|
|---|
| 142 |
// sanity check on Win32 ... |
|---|
| 143 |
version (Win32) |
|---|
| 144 |
{ |
|---|
| 145 |
bool kosher(){foreach (c; path) if (c is '\\') return false; return true;}; |
|---|
| 146 |
assert (kosher, "attempting to use non-standard '\\' in a path for a folder listing"); |
|---|
| 147 |
} |
|---|
| 148 |
|
|---|
| 149 |
return list (path, dg); |
|---|
| 150 |
} |
|---|
| 151 |
} |
|---|
| 152 |
|
|---|
| 153 |
/*********************************************************************** |
|---|
| 154 |
|
|---|
| 155 |
Throw an exception using the last known error |
|---|
| 156 |
|
|---|
| 157 |
***********************************************************************/ |
|---|
| 158 |
|
|---|
| 159 |
static void exception (char[] filename) |
|---|
| 160 |
{ |
|---|
| 161 |
exception (filename[0..$-1] ~ ": ", SysError.lastMsg); |
|---|
| 162 |
} |
|---|
| 163 |
|
|---|
| 164 |
/*********************************************************************** |
|---|
| 165 |
|
|---|
| 166 |
Throw an IO exception |
|---|
| 167 |
|
|---|
| 168 |
***********************************************************************/ |
|---|
| 169 |
|
|---|
| 170 |
static void exception (char[] prefix, char[] error) |
|---|
| 171 |
{ |
|---|
| 172 |
throw new IOException (prefix ~ error); |
|---|
| 173 |
} |
|---|
| 174 |
|
|---|
| 175 |
/*********************************************************************** |
|---|
| 176 |
|
|---|
| 177 |
Return an adjusted path such that non-empty instances always |
|---|
| 178 |
have a trailing separator |
|---|
| 179 |
|
|---|
| 180 |
***********************************************************************/ |
|---|
| 181 |
|
|---|
| 182 |
static char[] padded (char[] path, char c = '/') |
|---|
| 183 |
{ |
|---|
| 184 |
if (path.length && path[$-1] != c) |
|---|
| 185 |
path = path ~ c; |
|---|
| 186 |
return path; |
|---|
| 187 |
} |
|---|
| 188 |
|
|---|
| 189 |
/*********************************************************************** |
|---|
| 190 |
|
|---|
| 191 |
Return an adjusted path such that non-empty instances do not |
|---|
| 192 |
have a trailing separator |
|---|
| 193 |
|
|---|
| 194 |
***********************************************************************/ |
|---|
| 195 |
|
|---|
| 196 |
static char[] stripped (char[] path, char c = '/') |
|---|
| 197 |
{ |
|---|
| 198 |
if (path.length && path[$-1] is c) |
|---|
| 199 |
path = path [0 .. $-1]; |
|---|
| 200 |
return path; |
|---|
| 201 |
} |
|---|
| 202 |
|
|---|
| 203 |
/*********************************************************************** |
|---|
| 204 |
|
|---|
| 205 |
Join a set of path specs together. A path separator is |
|---|
| 206 |
potentially inserted between each of the segments. |
|---|
| 207 |
|
|---|
| 208 |
***********************************************************************/ |
|---|
| 209 |
|
|---|
| 210 |
static char[] join (char[][] paths...) |
|---|
| 211 |
{ |
|---|
| 212 |
char[] result; |
|---|
| 213 |
|
|---|
| 214 |
foreach (path; paths) |
|---|
| 215 |
result ~= padded (path); |
|---|
| 216 |
|
|---|
| 217 |
return result.length ? result [0 .. $-1] : ""; |
|---|
| 218 |
} |
|---|
| 219 |
|
|---|
| 220 |
/*********************************************************************** |
|---|
| 221 |
|
|---|
| 222 |
Append a terminating null onto a string, cheaply where |
|---|
| 223 |
feasible |
|---|
| 224 |
|
|---|
| 225 |
***********************************************************************/ |
|---|
| 226 |
|
|---|
| 227 |
static char[] strz (char[] src, char[] dst) |
|---|
| 228 |
{ |
|---|
| 229 |
auto i = src.length + 1; |
|---|
| 230 |
if (dst.length < i) |
|---|
| 231 |
dst.length = i; |
|---|
| 232 |
dst [0 .. i-1] = src; |
|---|
| 233 |
dst[i-1] = 0; |
|---|
| 234 |
return dst [0 .. i]; |
|---|
| 235 |
} |
|---|
| 236 |
|
|---|
| 237 |
/*********************************************************************** |
|---|
| 238 |
|
|---|
| 239 |
Win32 API code |
|---|
| 240 |
|
|---|
| 241 |
***********************************************************************/ |
|---|
| 242 |
|
|---|
| 243 |
version (Win32) |
|---|
| 244 |
{ |
|---|
| 245 |
/*************************************************************** |
|---|
| 246 |
|
|---|
| 247 |
return a wchar[] instance of the path |
|---|
| 248 |
|
|---|
| 249 |
***************************************************************/ |
|---|
| 250 |
|
|---|
| 251 |
private static wchar[] toString16 (wchar[] tmp, char[] path) |
|---|
| 252 |
{ |
|---|
| 253 |
auto i = MultiByteToWideChar (CP_UTF8, 0, |
|---|
| 254 |
cast(PCHAR)path.ptr, path.length, |
|---|
| 255 |
tmp.ptr, tmp.length); |
|---|
| 256 |
return tmp [0..i]; |
|---|
| 257 |
} |
|---|
| 258 |
|
|---|
| 259 |
/*************************************************************** |
|---|
| 260 |
|
|---|
| 261 |
return a char[] instance of the path |
|---|
| 262 |
|
|---|
| 263 |
***************************************************************/ |
|---|
| 264 |
|
|---|
| 265 |
private static char[] toString (char[] tmp, wchar[] path) |
|---|
| 266 |
{ |
|---|
| 267 |
auto i = WideCharToMultiByte (CP_UTF8, 0, path.ptr, path.length, |
|---|
| 268 |
cast(PCHAR)tmp.ptr, tmp.length, null, null); |
|---|
| 269 |
return tmp [0..i]; |
|---|
| 270 |
} |
|---|
| 271 |
|
|---|
| 272 |
/*************************************************************** |
|---|
| 273 |
|
|---|
| 274 |
Get info about this path |
|---|
| 275 |
|
|---|
| 276 |
***************************************************************/ |
|---|
| 277 |
|
|---|
| 278 |
private static bool fileInfo (char[] name, inout WIN32_FILE_ATTRIBUTE_DATA info) |
|---|
| 279 |
{ |
|---|
| 280 |
version (Win32SansUnicode) |
|---|
| 281 |
{ |
|---|
| 282 |
if (! GetFileAttributesExA (name.ptr, GetFileInfoLevelStandard, &info)) |
|---|
| 283 |
return false; |
|---|
| 284 |
} |
|---|
| 285 |
else |
|---|
| 286 |
{ |
|---|
| 287 |
wchar[MAX_PATH] tmp = void; |
|---|
| 288 |
if (! GetFileAttributesExW (toString16(tmp, name).ptr, GetFileInfoLevelStandard, &info)) |
|---|
| 289 |
return false; |
|---|
| 290 |
} |
|---|
| 291 |
|
|---|
| 292 |
return true; |
|---|
| 293 |
} |
|---|
| 294 |
|
|---|
| 295 |
/*************************************************************** |
|---|
| 296 |
|
|---|
| 297 |
Get info about this path |
|---|
| 298 |
|
|---|
| 299 |
***************************************************************/ |
|---|
| 300 |
|
|---|
| 301 |
private static DWORD getInfo (char[] name, inout WIN32_FILE_ATTRIBUTE_DATA info) |
|---|
| 302 |
{ |
|---|
| 303 |
if (! fileInfo (name, info)) |
|---|
| 304 |
exception (name); |
|---|
| 305 |
return info.dwFileAttributes; |
|---|
| 306 |
} |
|---|
| 307 |
|
|---|
| 308 |
/*************************************************************** |
|---|
| 309 |
|
|---|
| 310 |
Get flags for this path |
|---|
| 311 |
|
|---|
| 312 |
***************************************************************/ |
|---|
| 313 |
|
|---|
| 314 |
private static DWORD getFlags (char[] name) |
|---|
| 315 |
{ |
|---|
| 316 |
WIN32_FILE_ATTRIBUTE_DATA info = void; |
|---|
| 317 |
|
|---|
| 318 |
return getInfo (name, info); |
|---|
| 319 |
} |
|---|
| 320 |
|
|---|
| 321 |
/*************************************************************** |
|---|
| 322 |
|
|---|
| 323 |
Return whether the file or path exists |
|---|
| 324 |
|
|---|
| 325 |
***************************************************************/ |
|---|
| 326 |
|
|---|
| 327 |
static bool exists (char[] name) |
|---|
| 328 |
{ |
|---|
| 329 |
WIN32_FILE_ATTRIBUTE_DATA info = void; |
|---|
| 330 |
|
|---|
| 331 |
return fileInfo (name, info); |
|---|
| 332 |
} |
|---|
| 333 |
|
|---|
| 334 |
/*************************************************************** |
|---|
| 335 |
|
|---|
| 336 |
Return the file length (in bytes) |
|---|
| 337 |
|
|---|
| 338 |
***************************************************************/ |
|---|
| 339 |
|
|---|
| 340 |
static ulong fileSize (char[] name) |
|---|
| 341 |
{ |
|---|
| 342 |
WIN32_FILE_ATTRIBUTE_DATA info = void; |
|---|
| 343 |
|
|---|
| 344 |
getInfo (name, info); |
|---|
| 345 |
return (cast(ulong) info.nFileSizeHigh << 32) + |
|---|
| 346 |
info.nFileSizeLow; |
|---|
| 347 |
} |
|---|
| 348 |
|
|---|
| 349 |
/*************************************************************** |
|---|
| 350 |
|
|---|
| 351 |
Is this file writable? |
|---|
| 352 |
|
|---|
| 353 |
***************************************************************/ |
|---|
| 354 |
|
|---|
| 355 |
static bool isWritable (char[] name) |
|---|
| 356 |
{ |
|---|
| 357 |
return (getFlags(name) & FILE_ATTRIBUTE_READONLY) == 0; |
|---|
| 358 |
} |
|---|
| 359 |
|
|---|
| 360 |
/*************************************************************** |
|---|
| 361 |
|
|---|
| 362 |
Is this file actually a folder/directory? |
|---|
| 363 |
|
|---|
| 364 |
***************************************************************/ |
|---|
| 365 |
|
|---|
| 366 |
static bool isFolder (char[] name) |
|---|
| 367 |
{ |
|---|
| 368 |
return (getFlags(name) & FILE_ATTRIBUTE_DIRECTORY) != 0; |
|---|
| 369 |
} |
|---|
| 370 |
|
|---|
| 371 |
/*************************************************************** |
|---|
| 372 |
|
|---|
| 373 |
Return timestamp information |
|---|
| 374 |
|
|---|
| 375 |
Timstamps are returns in a format dictated by the |
|---|
| 376 |
file-system. For example NTFS keeps UTC time, |
|---|
| 377 |
while FAT timestamps are based on the local time |
|---|
| 378 |
|
|---|
| 379 |
***************************************************************/ |
|---|
| 380 |
|
|---|
| 381 |
static Stamps timeStamps (char[] name) |
|---|
| 382 |
{ |
|---|
| 383 |
static Time convert (FILETIME time) |
|---|
| 384 |
{ |
|---|
| 385 |
return Time (TimeSpan.Epoch1601 + *cast(long*) &time); |
|---|
| 386 |
} |
|---|
| 387 |
|
|---|
| 388 |
WIN32_FILE_ATTRIBUTE_DATA info = void; |
|---|
| 389 |
Stamps time = void; |
|---|
| 390 |
|
|---|
| 391 |
getInfo (name, info); |
|---|
| 392 |
time.modified = convert (info.ftLastWriteTime); |
|---|
| 393 |
time.accessed = convert (info.ftLastAccessTime); |
|---|
| 394 |
time.created = convert (info.ftCreationTime); |
|---|
| 395 |
return time; |
|---|
| 396 |
} |
|---|
| 397 |
|
|---|
| 398 |
/*************************************************************** |
|---|
| 399 |
|
|---|
| 400 |
Transfer the content of another file to this one. |
|---|
| 401 |
Returns a reference to this class on success, or |
|---|
| 402 |
throws an IOException upon failure. |
|---|
| 403 |
|
|---|
| 404 |
***************************************************************/ |
|---|
| 405 |
|
|---|
| 406 |
static void copy (char[] src, char[] dst) |
|---|
| 407 |
{ |
|---|
| 408 |
version (Win32SansUnicode) |
|---|
| 409 |
{ |
|---|
| 410 |
if (! CopyFileA (src.ptr, dst.ptr, false)) |
|---|
| 411 |
exception (src); |
|---|
| 412 |
} |
|---|
| 413 |
else |
|---|
| 414 |
{ |
|---|
| 415 |
wchar[MAX_PATH+1] tmp1 = void; |
|---|
| 416 |
wchar[MAX_PATH+1] tmp2 = void; |
|---|
| 417 |
|
|---|
| 418 |
if (! CopyFileW (toString16(tmp1, src).ptr, toString16(tmp2, dst).ptr, false)) |
|---|
| 419 |
exception (src); |
|---|
| 420 |
} |
|---|
| 421 |
} |
|---|
| 422 |
|
|---|
| 423 |
/*************************************************************** |
|---|
| 424 |
|
|---|
| 425 |
Remove the file/directory from the file-system |
|---|
| 426 |
|
|---|
| 427 |
***************************************************************/ |
|---|
| 428 |
|
|---|
| 429 |
static void remove (char[] name) |
|---|
| 430 |
{ |
|---|
| 431 |
if (isFolder(name)) |
|---|
| 432 |
{ |
|---|
| 433 |
version (Win32SansUnicode) |
|---|
| 434 |
{ |
|---|
| 435 |
if (! RemoveDirectoryA (name.ptr)) |
|---|
| 436 |
exception (name); |
|---|
| 437 |
} |
|---|
| 438 |
else |
|---|
| 439 |
{ |
|---|
| 440 |
wchar[MAX_PATH] tmp = void; |
|---|
| 441 |
if (! RemoveDirectoryW (toString16(tmp, name).ptr)) |
|---|
| 442 |
exception (name); |
|---|
| 443 |
} |
|---|
| 444 |
} |
|---|
| 445 |
else |
|---|
| 446 |
version (Win32SansUnicode) |
|---|
| 447 |
{ |
|---|
| 448 |
if (! DeleteFileA (name.ptr)) |
|---|
| 449 |
exception (name); |
|---|
| 450 |
} |
|---|
| 451 |
else |
|---|
| 452 |
{ |
|---|
| 453 |
wchar[MAX_PATH] tmp = void; |
|---|
| 454 |
if (! DeleteFileW (toString16(tmp, name).ptr)) |
|---|
| 455 |
exception (name); |
|---|
| 456 |
} |
|---|
| 457 |
} |
|---|
| 458 |
|
|---|
| 459 |
/*************************************************************** |
|---|
| 460 |
|
|---|
| 461 |
change the name or location of a file/directory, and |
|---|
| 462 |
adopt the provided Path |
|---|
| 463 |
|
|---|
| 464 |
***************************************************************/ |
|---|
| 465 |
|
|---|
| 466 |
static void rename (char[] src, char[] dst) |
|---|
| 467 |
{ |
|---|
| 468 |
const int Typical = MOVEFILE_REPLACE_EXISTING + |
|---|
| 469 |
MOVEFILE_COPY_ALLOWED + |
|---|
| 470 |
MOVEFILE_WRITE_THROUGH; |
|---|
| 471 |
|
|---|
| 472 |
int result; |
|---|
| 473 |
version (Win32SansUnicode) |
|---|
| 474 |
result = MoveFileExA (src.ptr, dst.ptr, Typical); |
|---|
| 475 |
else |
|---|
| 476 |
{ |
|---|
| 477 |
wchar[MAX_PATH] tmp1 = void; |
|---|
| 478 |
wchar[MAX_PATH] tmp2 = void; |
|---|
| 479 |
result = MoveFileExW (toString16(tmp1, src).ptr, toString16(tmp2, dst).ptr, Typical); |
|---|
| 480 |
} |
|---|
| 481 |
|
|---|
| 482 |
if (! result) |
|---|
| 483 |
exception (src); |
|---|
| 484 |
} |
|---|
| 485 |
|
|---|
| 486 |
/*************************************************************** |
|---|
| 487 |
|
|---|
| 488 |
Create a new file |
|---|
| 489 |
|
|---|
| 490 |
***************************************************************/ |
|---|
| 491 |
|
|---|
| 492 |
static void createFile (char[] name) |
|---|
| 493 |
{ |
|---|
| 494 |
HANDLE h; |
|---|
| 495 |
|
|---|
| 496 |
version (Win32SansUnicode) |
|---|
| 497 |
h = CreateFileA (name.ptr, GENERIC_WRITE, |
|---|
| 498 |
0, null, CREATE_ALWAYS, |
|---|
| 499 |
FILE_ATTRIBUTE_NORMAL, cast(HANDLE) 0); |
|---|
| 500 |
else |
|---|
| 501 |
{ |
|---|
| 502 |
wchar[MAX_PATH] tmp = void; |
|---|
| 503 |
h = CreateFileW (toString16(tmp, name).ptr, GENERIC_WRITE, |
|---|
| 504 |
0, null, CREATE_ALWAYS, |
|---|
| 505 |
FILE_ATTRIBUTE_NORMAL, cast(HANDLE) 0); |
|---|
| 506 |
} |
|---|
| 507 |
|
|---|
| 508 |
if (h == INVALID_HANDLE_VALUE) |
|---|
| 509 |
exception (name); |
|---|
| 510 |
|
|---|
| 511 |
if (! CloseHandle (h)) |
|---|
| 512 |
exception (name); |
|---|
| 513 |
} |
|---|
| 514 |
|
|---|
| 515 |
/*************************************************************** |
|---|
| 516 |
|
|---|
| 517 |
Create a new directory |
|---|
| 518 |
|
|---|
| 519 |
***************************************************************/ |
|---|
| 520 |
|
|---|
| 521 |
static void createFolder (char[] name) |
|---|
| 522 |
{ |
|---|
| 523 |
version (Win32SansUnicode) |
|---|
| 524 |
{ |
|---|
| 525 |
if (! CreateDirectoryA (name.ptr, null)) |
|---|
| 526 |
exception (name); |
|---|
| 527 |
} |
|---|
| 528 |
else |
|---|
| 529 |
{ |
|---|
| 530 |
wchar[MAX_PATH] tmp = void; |
|---|
| 531 |
if (! CreateDirectoryW (toString16(tmp, name).ptr, null)) |
|---|
| 532 |
exception (name); |
|---|
| 533 |
} |
|---|
| 534 |
} |
|---|
| 535 |
|
|---|
| 536 |
/*************************************************************** |
|---|
| 537 |
|
|---|
| 538 |
List the set of filenames within this folder. |
|---|
| 539 |
|
|---|
| 540 |
Each path and filename is passed to the provided |
|---|
| 541 |
delegate, along with the path prefix and whether |
|---|
| 542 |
the entry is a folder or not. |
|---|
| 543 |
|
|---|
| 544 |
Returns the number of files scanned. |
|---|
| 545 |
|
|---|
| 546 |
***************************************************************/ |
|---|
| 547 |
|
|---|
| 548 |
static int list (char[] folder, int delegate(ref FileInfo) dg) |
|---|
| 549 |
{ |
|---|
| 550 |
HANDLE h; |
|---|
| 551 |
int ret; |
|---|
| 552 |
char[] prefix; |
|---|
| 553 |
char[MAX_PATH+1] tmp = void; |
|---|
| 554 |
FIND_DATA fileinfo = void; |
|---|
| 555 |
|
|---|
| 556 |
version (Win32SansUnicode) |
|---|
| 557 |
alias char T; |
|---|
| 558 |
else |
|---|
| 559 |
alias wchar T; |
|---|
| 560 |
|
|---|
| 561 |
int next() |
|---|
| 562 |
{ |
|---|
| 563 |
version (Win32SansUnicode) |
|---|
| 564 |
return FindNextFileA (h, &fileinfo); |
|---|
| 565 |
else |
|---|
| 566 |
return FindNextFileW (h, &fileinfo); |
|---|
| 567 |
} |
|---|
| 568 |
|
|---|
| 569 |
static T[] padded (T[] s, T[] ext) |
|---|
| 570 |
{ |
|---|
| 571 |
if (s.length && s[$-1] is '/') |
|---|
| 572 |
return s ~ ext; |
|---|
| 573 |
return s ~ "/" ~ ext; |
|---|
| 574 |
} |
|---|
| 575 |
|
|---|
| 576 |
version (Win32SansUnicode) |
|---|
| 577 |
h = FindFirstFileA (padded(folder[0..$-1], "*\0").ptr, &fileinfo); |
|---|
| 578 |
else |
|---|
| 579 |
{ |
|---|
| 580 |
wchar[MAX_PATH] host = void; |
|---|
| 581 |
h = FindFirstFileW (padded(toString16(host, folder[0..$-1]), "*\0").ptr, &fileinfo); |
|---|
| 582 |
} |
|---|
| 583 |
|
|---|
| 584 |
if (h is INVALID_HANDLE_VALUE) |
|---|
| 585 |
exception (folder); |
|---|
| 586 |
|
|---|
| 587 |
scope (exit) |
|---|
| 588 |
FindClose (h); |
|---|
| 589 |
|
|---|
| 590 |
prefix = FS.padded (folder[0..$-1]); |
|---|
| 591 |
do { |
|---|
| 592 |
version (Win32SansUnicode) |
|---|
| 593 |
{ |
|---|
| 594 |
auto len = strlen (fileinfo.cFileName.ptr); |
|---|
| 595 |
auto str = fileinfo.cFileName.ptr [0 .. len]; |
|---|
| 596 |
} |
|---|
| 597 |
else |
|---|
| 598 |
{ |
|---|
| 599 |
auto len = wcslen (fileinfo.cFileName.ptr); |
|---|
| 600 |
auto str = toSt |
|---|