Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

root/trunk/tango/io/Path.d

Revision 3998, 55.9 kB (checked in by kris, 1 month ago)

cleaned up the documentation regarding Win32 '\' characters. FilePath? does an automatic conversion to '/', whereas the immutable Path does not.

  • Property svn:mime-type set to text/x-dsrc
  • Property svn:eol-style set to native
Line 
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