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

root/trunk/tango/io/FilePath.d

Revision 4068, 48.3 kB (checked in by kris, 3 weeks ago)

removed redundant import

  • Property svn:mime-type set to text/x-dsrc
  • Property svn:eol-style set to native
Line 
1 /*******************************************************************************
2
3         copyright:      Copyright (c) 2004 Kris Bell. All rights reserved
4
5         license:        BSD style: $(LICENSE)
6
7         version:        Oct 2004: Initial version
8         version:        Nov 2006: Australian version
9         version:        Feb 2007: Mutating version
10         version:        Mar 2007: Folded FileProxy in
11         version:        Nov 2007: VFS dictates '/' always be used
12         version:        Feb 2008: Split file-system calls into a struct
13
14         author:         Kris
15
16         FilePath provides a means to efficiently edit path components and
17         of accessing the underlying file system.
18
19         Use module Path.d instead when you need pedestrian access to the
20         file-system, and are not mutating the path components themselves
21
22 *******************************************************************************/
23
24 module tango.io.FilePath;
25
26 private import  tango.io.Path;
27
28 private import  tango.io.model.IFile : FileConst;
29
30 /*******************************************************************************
31
32 *******************************************************************************/
33
34 private extern (C) void memmove (void* dst, void* src, uint bytes);
35
36 /*******************************************************************************
37
38         Models a file path. These are expected to be used as the constructor
39         argument to various file classes. The intention is that they easily
40         convert to other representations such as absolute, canonical, or Url.
41
42         File paths containing non-ansi characters should be UTF-8 encoded.
43         Supporting Unicode in this manner was deemed to be more suitable
44         than providing a wchar version of FilePath, and is both consistent
45         & compatible with the approach taken with the Uri class.
46
47         FilePath is designed to be transformed, thus each mutating method
48         modifies the internal content. See module Path.d for a lightweight
49         immutable variation.
50
51         Note that patterns of adjacent '.' separators are treated specially
52         in that they will be assigned to the name where there is no distinct
53         suffix. In addition, a '.' at the start of a name signifies it does
54         not belong to the suffix i.e. ".file" is a name rather than a suffix.
55         Patterns of intermediate '.' characters will otherwise be assigned
56         to the suffix, such that "file....suffix" includes the dots within
57         the suffix itself. See method ext() for a suffix without dots.
58
59         Note that Win32 '\' characters are converted to '/' by default via
60         the FilePath constructor.
61
62 *******************************************************************************/
63
64 class FilePath : PathView
65 {
66         private PathParser      p;              // the parsed path
67         private bool            dir_;           // this represents a dir?
68
69         public alias    set     opAssign;       // path = x;
70         public alias    append  opCatAssign;    // path ~= x;
71
72
73         /***********************************************************************
74
75                 Filter used for screening paths via toList()
76
77         ***********************************************************************/
78
79         public alias bool delegate (FilePath, bool) Filter;
80
81         /***********************************************************************
82
83                 Call-site shortcut to create a FilePath instance. This
84                 enables the same syntax as struct usage, so may expose
85                 a migration path
86
87         ***********************************************************************/
88
89         static FilePath opCall (char[] filepath = null)
90         {
91                 return new FilePath (filepath);
92         }
93
94         /***********************************************************************
95
96                 Create a FilePath from a copy of the provided string.
97
98                 FilePath assumes both path & name are present, and therefore
99                 may split what is otherwise a logically valid path. That is,
100                 the 'name' of a file is typically the path segment following
101                 a rightmost path-separator. The intent is to treat files and
102                 directories in the same manner; as a name with an optional
103                 ancestral structure. It is possible to bias the interpretation
104                 by adding a trailing path-separator to the argument. Doing so
105                 will result in an empty name attribute.
106
107                 With regard to the filepath copy, we found the common case to
108                 be an explicit .dup, whereas aliasing appeared to be rare by
109                 comparison. We also noted a large proportion interacting with
110                 C-oriented OS calls, implying the postfix of a null terminator.
111                 Thus, FilePath combines both as a single operation.
112
113                 Note that Win32 '\' characters are normalized to '/' instead.
114
115         ***********************************************************************/
116
117         this (char[] filepath = null)
118         {
119                 set (filepath, true);
120         }
121        
122         /***********************************************************************
123
124                 Return the complete text of this filepath
125
126         ***********************************************************************/
127
128         final char[] toString ()
129         {
130                 return  p.toString;
131         }
132
133         /***********************************************************************
134
135                 Duplicate this path
136
137         ***********************************************************************/
138
139         final FilePath dup ()
140         {
141                 return FilePath (toString);
142         }
143
144         /***********************************************************************
145
146                 Return the complete text of this filepath as a null
147                 terminated string for use with a C api. Use toString
148                 instead for any D api.
149
150                 Note that the nul is always embedded within the string
151                 maintained by FilePath, so there's no heap overhead when
152                 making a C call
153
154         ***********************************************************************/
155
156         final char[] cString ()
157         {
158                 return p.fp [0 .. p.end_+1];
159         }
160
161         /***********************************************************************
162
163                 Return the root of this path. Roots are constructs such as
164                 "c:"
165
166         ***********************************************************************/
167
168         final char[] root ()
169         {
170                 return p.root;
171         }
172
173         /***********************************************************************
174
175                 Return the file path. Paths may start and end with a "/".
176                 The root path is "/" and an unspecified path is returned as
177                 an empty string. Directory paths may be split such that the
178                 directory name is placed into the 'name' member; directory
179                 paths are treated no differently than file paths
180
181         ***********************************************************************/
182
183         final char[] folder ()
184         {
185                 return p.folder;
186         }
187
188         /***********************************************************************
189
190                 Returns a path representing the parent of this one. This
191                 will typically return the current path component, though
192                 with a special case where the name component is empty. In
193                 such cases, the path is scanned for a prior segment:
194                 ---
195                 normal:  /x/y/z => /x/y
196                 special: /x/y/  => /x
197                 ---
198
199                 Note that this returns a path suitable for splitting into
200                 path and name components (there's no trailing separator).
201
202                 See pop() also, which is generally more useful when working
203                 with FilePath instances
204
205         ***********************************************************************/
206
207         final char[] parent ()
208         {
209                 return p.parent;
210         }
211
212         /***********************************************************************
213
214                 Return the name of this file, or directory.
215
216         ***********************************************************************/
217
218         final char[] name ()
219         {
220                 return p.name;
221         }
222
223         /***********************************************************************
224
225                 Ext is the tail of the filename, rightward of the rightmost
226                 '.' separator e.g. path "foo.bar" has ext "bar". Note that
227                 patterns of adjacent separators are treated specially; for
228                 example, ".." will wind up with no ext at all
229
230         ***********************************************************************/
231
232         final char[] ext ()
233         {
234                 return p.ext;
235         }
236
237         /***********************************************************************
238
239                 Suffix is like ext, but includes the separator e.g. path
240                 "foo.bar" has suffix ".bar"
241
242         ***********************************************************************/
243
244         final char[] suffix ()
245         {
246                 return p.suffix;
247         }
248
249         /***********************************************************************
250
251                 return the root + folder combination
252
253         ***********************************************************************/
254
255         final char[] path ()
256         {
257                 return p.path;
258         }
259
260         /***********************************************************************
261
262                 return the name + suffix combination
263
264         ***********************************************************************/
265
266         final char[] file ()
267         {
268                 return p.file;
269         }
270
271         /***********************************************************************
272
273                 Returns true if all fields are equal.
274
275         ***********************************************************************/
276
277         final override int opEquals (Object o)
278         {
279                 return (this is o) || (o && toString == o.toString);
280         }
281
282         /***********************************************************************
283
284                 Does this FilePath equate to the given text?
285
286         ***********************************************************************/
287
288         final override int opEquals (char[] s)
289         {
290                 return p.opEquals(s);
291         }
292
293         /***********************************************************************
294
295                 Returns true if this FilePath is *not* relative to the
296                 current working directory
297
298         ***********************************************************************/
299
300         final bool isAbsolute ()
301         {
302                 return p.isAbsolute;
303         }
304
305         /***********************************************************************
306
307                 Returns true if this FilePath is empty
308
309         ***********************************************************************/
310
311         final bool isEmpty ()
312         {
313                 return p.isEmpty;
314         }
315
316         /***********************************************************************
317
318                 Returns true if this FilePath has a parent. Note that a
319                 parent is defined by the presence of a path-separator in
320                 the path. This means 'foo' within "\foo" is considered a
321                 child of the root
322
323         ***********************************************************************/
324
325         final bool isChild ()
326         {
327                 return p.isChild;
328         }
329
330         /***********************************************************************
331
332                 Replace all 'from' instances with 'to'
333
334         ***********************************************************************/
335
336         final FilePath replace (char from, char to)
337         {
338                 .replace (path, from, to);
339                 return this;
340         }
341
342         /***********************************************************************
343
344                 Convert path separators to a standard format, using '/' as
345                 the path separator. This is compatible with URI and all of
346                 the contemporary O/S which Tango supports. Known exceptions
347                 include the Windows command-line processor, which considers
348                 '/' characters to be switches instead. Use the native()
349                 method to support that.
350
351                 Note: mutates the current path.
352
353         ***********************************************************************/
354
355         final FilePath standard ()
356         {
357                 .standard (path);
358                 return this;
359         }
360
361         /***********************************************************************
362
363                 Convert to native O/S path separators where that is required,
364                 such as when dealing with the Windows command-line.
365                 
366                 Note: mutates the current path. Use this pattern to obtain a
367                 copy instead: path.dup.native
368
369         ***********************************************************************/
370
371         final FilePath native ()
372         {
373                 .native (path);
374                 return this;
375         }
376
377         /***********************************************************************
378
379                 Concatenate text to this path; no separators are added.
380                 See join() also
381
382         ***********************************************************************/
383
384         final FilePath cat (char[][] others...)
385         {
386                 foreach (other; others)
387                         {
388                         auto len = p.end_ + other.length;
389                         expand (len);
390                         p.fp [p.end_ .. len] = other;
391                         p.fp [len] = 0;
392                         p.end_ = len;
393                         }
394                 return parse;
395         }
396
397         /***********************************************************************
398
399                 Append a folder to this path. A leading separator is added
400                 as required
401
402         ***********************************************************************/
403
404         final FilePath append (char[] path)
405         {
406                 if (file.length)
407                     path = prefixed (path);
408                 return cat (path);
409         }
410
411         /***********************************************************************
412
413                 Prepend a folder to this path. A trailing separator is added
414                 if needed
415
416         ***********************************************************************/
417
418         final FilePath prepend (char[] path)
419         {
420                 adjust (0, p.folder_, p.folder_, padded (path));
421                 return parse;
422         }
423
424         /***********************************************************************
425
426                 Reset the content of this path to that of another and
427                 reparse
428
429         ***********************************************************************/
430
431         FilePath set (FilePath path)
432         {
433                 return set (path.toString, false);
434         }
435
436         /***********************************************************************
437
438                 Reset the content of this path, and reparse. There's an
439                 optional boolean flag to convert the path into standard
440                 form, before parsing (converting '\' into '/')
441
442         ***********************************************************************/
443
444         final FilePath set (char[] path, bool convert = false)
445         {
446                 p.end_ = path.length;
447
448                 expand (p.end_);
449                 if (p.end_)
450                    {
451                    p.fp[0 .. p.end_] = path;
452                    if (convert)
453                        .standard (p.fp [0 .. p.end_]);
454                    }
455
456                 p.fp[p.end_] = '\0';
457                 return parse;
458         }
459
460         /***********************************************************************
461
462                 Sidestep the normal lookup for paths that are known to
463                 be folders. Where folder is true, file-system lookups
464                 will be skipped.
465
466         ***********************************************************************/
467
468         final FilePath isFolder (bool folder)
469         {
470                 dir_ = folder;
471                 return this;
472         }
473
474         /***********************************************************************
475
476                 Replace the root portion of this path
477
478         ***********************************************************************/
479
480         final FilePath root (char[] other)
481         {
482                 auto x = adjust (0, p.folder_, p.folder_, padded (other, ':'));
483                 p.folder_ += x;
484                 p.suffix_ += x;
485                 p.name_ += x;
486                 return this;
487         }
488
489         /***********************************************************************
490
491                 Replace the folder portion of this path. The folder will be
492                 padded with a path-separator as required
493
494         ***********************************************************************/
495
496         final FilePath folder (char[] other)
497         {
498                 auto x = adjust (p.folder_, p.name_, p.name_ - p.folder_, padded (other));
499                 p.suffix_ += x;
500                 p.name_ += x;
501                 return this;
502         }
503
504         /***********************************************************************
505
506                 Replace the name portion of this path
507
508         ***********************************************************************/
509
510         final FilePath name (char[] other)
511         {
512                 auto x = adjust (p.name_, p.suffix_, p.suffix_ - p.name_, other);
513                 p.suffix_ += x;
514                 return this;
515         }
516
517         /***********************************************************************
518
519                 Replace the suffix portion of this path. The suffix will be
520                 prefixed with a file-separator as required
521
522         ***********************************************************************/
523
524         final FilePath suffix (char[] other)
525         {
526                 adjust (p.suffix_, p.end_, p.end_ - p.suffix_, prefixed (other, '.'));
527                 return this;
528         }
529
530         /***********************************************************************
531
532                 Replace the root and folder portions of this path and
533                 reparse. The replacement will be padded with a path
534                 separator as required
535
536         ***********************************************************************/
537
538         final FilePath path (char[] other)
539         {
540                 adjust (0, p.name_, p.name_, padded (other));
541                 return parse;
542         }
543
544         /***********************************************************************
545
546                 Replace the file and suffix portions of this path and
547                 reparse. The replacement will be prefixed with a suffix
548                 separator as required
549
550         ***********************************************************************/
551
552         final FilePath file (char[] other)
553         {
554                 adjust (p.name_, p.end_, p.end_ - p.name_, other);
555                 return parse;
556         }
557
558         /***********************************************************************
559
560                 Pop to the parent of the current filepath (in situ - mutates
561                 this FilePath)
562
563         ***********************************************************************/
564
565         final FilePath pop ()
566         {
567                 auto path = parent();
568                 p.end_ = path.length;
569                 p.fp[p.end_] = '\0';
570                 return parse;
571         }
572
573         /***********************************************************************
574
575                 Join a set of path specs together. A path separator is
576                 potentially inserted between each of the segments.
577
578         ***********************************************************************/
579
580         static char[] join (char[][] paths...)
581         {
582                 return FS.join (paths);
583         }
584
585         /***********************************************************************
586
587                 Return an adjusted path such that non-empty instances do not
588                 have a trailing separator
589
590         ***********************************************************************/
591
592         static char[] stripped (char[] path, char c = FileConst.PathSeparatorChar)
593         {
594                 return FS.stripped (path, c);
595         }
596
597         /***********************************************************************
598
599                 Return an adjusted path such that non-empty instances always
600                 have a trailing separator
601
602         ***********************************************************************/
603
604         static char[] padded (char[] path, char c = FileConst.PathSeparatorChar)
605         {
606                 return FS.padded (path, c);
607         }
608
609         /***********************************************************************
610
611                 Return an adjusted path such that non-empty instances always
612                 have a prefixed separator
613
614         ***********************************************************************/
615
616         static char[] prefixed (char[] s, char c = FileConst.PathSeparatorChar)
617         {
618                 if (s.length && s[0] != c)
619                     s = c ~ s;
620                 return s;
621         }
622
623         /***********************************************************************
624
625                 Parse the path spec, and mutate '\' into '/' as necessary
626
627         ***********************************************************************/
628
629         private final FilePath parse ()
630         {
631                 p.parse (p.fp, p.end_);
632                 return this;
633         }
634
635         /***********************************************************************
636
637                 Potentially make room for more content
638
639         ***********************************************************************/
640
641         private final void expand (uint size)
642         {
643                 ++size;
644                 if (p.fp.length < size)
645                     p.fp.length = (size + 127) & ~127;
646         }
647
648         /***********************************************************************
649
650                 Insert/delete internal content
651
652         ***********************************************************************/
653
654         private final int adjust (int head, int tail, int len, char[] sub)
655         {
656                 len = sub.length - len;
657
658                 // don't destroy self-references!
659                 if (len && sub.ptr >= p.fp.ptr+head+len && sub.ptr < p.fp.ptr+p.fp.length)
660                    {
661                    char[512] tmp = void;
662                    assert (sub.length < tmp.length);
663                    sub = tmp[0..sub.length] = sub;
664                    }
665
666                 // make some room if necessary
667                 expand (len + p.end_);
668
669                 // slide tail around to insert or remove space
670                 memmove (p.fp.ptr+tail+len, p.fp.ptr+tail, p.end_ +1 - tail);
671
672                 // copy replacement
673                 memmove (p.fp.ptr + head, sub.ptr, sub.length);
674
675                 // adjust length
676                 p.end_ += len;
677                 return len;
678         }
679
680
681         /**********************************************************************/
682         /********************** file-system methods ***************************/
683         /**********************************************************************/
684
685
686         /***********************************************************************
687
688                 Create an entire path consisting of this folder along with
689                 all parent folders. The path must not contain '.' or '..'
690                 segments. Related methods include PathUtil.normalize() and
691                 FileSystem.toAbsolute()
692
693                 Note that each segment is created as a folder, including the
694                 trailing segment.
695
696                 Returns: a chaining reference (this)
697
698                 Throws: IOException upon systen errors
699
700                 Throws: IllegalArgumentException if a segment exists but as
701                 a file instead of a folder
702
703         ***********************************************************************/
704
705         final FilePath create ()
706         {       
707                 createPath (this.toString);
708                 return this;
709         }
710
711         /***********************************************************************
712
713                 List the set of filenames within this folder, using
714                 the provided filter to control the list:
715                 ---
716                 bool delegate (FilePath path, bool isFolder) Filter
717                 ---
718
719                 Returning true from the filter includes the given path,
720                 whilst returning false excludes it. Parameter 'isFolder'
721                 indicates whether the path is a file or folder.
722
723                 Note that paths composed of '.' characters are ignored.
724
725         ***********************************************************************/
726
727         final FilePath[] toList (Filter filter = null)
728         {
729                 FilePath[] paths;
730
731                 foreach (info; this)
732                         {
733                         auto p = from (info);
734
735                         // test this entry for inclusion
736                         if (filter is null || filter (p, info.folder<