root/trunk/bevutils/file.d

Revision 20, 33.7 kB (checked in by teales, 1 year ago)

Bevutils files initial check-in.

Line 
1 // Written in the D programming language.
2
3 /**
4  * Macros:
5  *  WIKI = Phobos/StdFile
6  */
7
8 /*
9  *  Copyright (C) 2001-2004 by Digital Mars, www.digitalmars.com
10  * Written by Walter Bright, Christopher E. Miller, Andre Fornacon
11  *
12  *  This software is provided 'as-is', without any express or implied
13  *  warranty. In no event will the authors be held liable for any damages
14  *  arising from the use of this software.
15  *
16  *  Permission is granted to anyone to use this software for any purpose,
17  *  including commercial applications, and to alter it and redistribute it
18  *  freely, subject to the following restrictions:
19  *
20  *  o  The origin of this software must not be misrepresented; you must not
21  *     claim that you wrote the original software. If you use this software
22  *     in a product, an acknowledgment in the product documentation would be
23  *     appreciated but is not required.
24  *  o  Altered source versions must be plainly marked as such, and must not
25  *     be misrepresented as being the original software.
26  *  o  This notice may not be removed or altered from any source
27  *     distribution.
28  */
29
30 module std.file;
31
32 private import std.c.stdio;
33 private import std.c.stdlib;
34 private import std.path;
35 private import std.string;
36 private import std.regexp;
37 private import std.gc;
38 private import std.stdio;
39
40 /* =========================== Win32 ======================= */
41
42 version (Win32)
43 {
44
45 private import std.c.windows.windows;
46 private import std.utf;
47 private import std.windows.syserror;
48 private import std.windows.charset;
49 private import std.date;
50
51 int useWfuncs = 1;
52
53 static this()
54 {
55     // Win 95, 98, ME do not implement the W functions
56     useWfuncs = (GetVersion() < 0x80000000);
57 }
58
59 /***********************************
60  * Exception thrown for file I/O errors.
61  */
62
63 class FileException : Exception
64 {
65
66     uint errno;         // operating system error code
67
68     this(char[] name)
69     {
70     this(name, "file I/O");
71     }
72
73     this(char[] name, char[] message)
74     {
75     super(name ~ ": " ~ message);
76     }
77
78     this(char[] name, uint errno)
79     {
80     this(name, sysErrorString(errno));
81     this.errno = errno;
82     }
83 }
84
85 /* **********************************
86  * Basic File operations.
87  */
88
89 /********************************************
90  * Read file name[], return array of bytes read.
91  * Throws:
92  *  FileException on error.
93  */
94
95 void[] read(char[] name)
96 {
97     DWORD numread;
98     HANDLE h;
99
100     if (useWfuncs)
101     {
102     wchar* namez = std.utf.toUTF16z(name);
103     h = CreateFileW(namez,GENERIC_READ,FILE_SHARE_READ,null,OPEN_EXISTING,
104         FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,cast(HANDLE)null);
105     }
106     else
107     {
108     char* namez = toMBSz(name);
109     h = CreateFileA(namez,GENERIC_READ,FILE_SHARE_READ,null,OPEN_EXISTING,
110         FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,cast(HANDLE)null);
111     }
112
113     if (h == INVALID_HANDLE_VALUE)
114     goto err1;
115
116     auto size = GetFileSize(h, null);
117     if (size == INVALID_FILE_SIZE)
118     goto err2;
119
120     auto buf = std.gc.malloc(size);
121     if (buf)
122     std.gc.hasNoPointers(buf.ptr);
123
124     if (ReadFile(h,buf.ptr,size,&numread,null) != 1)
125     goto err2;
126
127     if (numread != size)
128     goto err2;
129
130     if (!CloseHandle(h))
131     goto err;
132
133     return buf[0 .. size];
134
135 err2:
136     CloseHandle(h);
137 err:
138     delete buf;
139 err1:
140     throw new FileException(name, GetLastError());
141 }
142
143 /*********************************************
144  * Write buffer[] to file name[].
145  * Throws: FileException on error.
146  */
147
148 void write(char[] name, void[] buffer)
149 {
150     HANDLE h;
151     DWORD numwritten;
152
153     if (useWfuncs)
154     {
155     wchar* namez = std.utf.toUTF16z(name);
156     h = CreateFileW(namez,GENERIC_WRITE,0,null,CREATE_ALWAYS,
157         FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,cast(HANDLE)null);
158     }
159     else
160     {
161     char* namez = toMBSz(name);
162     h = CreateFileA(namez,GENERIC_WRITE,0,null,CREATE_ALWAYS,
163         FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,cast(HANDLE)null);
164     }
165     if (h == INVALID_HANDLE_VALUE)
166     goto err;
167
168     if (WriteFile(h,buffer.ptr,buffer.length,&numwritten,null) != 1)
169     goto err2;
170
171     if (buffer.length != numwritten)
172     goto err2;
173    
174     if (!CloseHandle(h))
175     goto err;
176     return;
177
178 err2:
179     CloseHandle(h);
180 err:
181     throw new FileException(name, GetLastError());
182 }
183
184
185 /*********************************************
186  * Append buffer[] to file name[].
187  * Throws: FileException on error.
188  */
189
190 void append(char[] name, void[] buffer)
191 {
192     HANDLE h;
193     DWORD numwritten;
194
195     if (useWfuncs)
196     {
197     wchar* namez = std.utf.toUTF16z(name);
198     h = CreateFileW(namez,GENERIC_WRITE,0,null,OPEN_ALWAYS,
199         FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,cast(HANDLE)null);
200     }
201     else
202     {
203     char* namez = toMBSz(name);
204     h = CreateFileA(namez,GENERIC_WRITE,0,null,OPEN_ALWAYS,
205         FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,cast(HANDLE)null);
206     }
207     if (h == INVALID_HANDLE_VALUE)
208     goto err;
209
210     SetFilePointer(h, 0, null, FILE_END);
211
212     if (WriteFile(h,buffer.ptr,buffer.length,&numwritten,null) != 1)
213     goto err2;
214
215     if (buffer.length != numwritten)
216     goto err2;
217    
218     if (!CloseHandle(h))
219     goto err;
220     return;
221
222 err2:
223     CloseHandle(h);
224 err:
225     throw new FileException(name, GetLastError());
226 }
227
228
229 /***************************************************
230  * Rename file from[] to to[].
231  * Throws: FileException on error.
232  */
233
234 void rename(char[] from, char[] to)
235 {
236     BOOL result;
237
238     if (useWfuncs)
239     result = MoveFileW(std.utf.toUTF16z(from), std.utf.toUTF16z(to));
240     else
241     result = MoveFileA(toMBSz(from), toMBSz(to));
242     if (!result)
243     throw new FileException(to, GetLastError());
244 }
245
246
247 /***************************************************
248  * Delete file name[].
249  * Throws: FileException on error.
250  */
251
252 void remove(char[] name)
253 {
254     BOOL result;
255
256     if (useWfuncs)
257     result = DeleteFileW(std.utf.toUTF16z(name));
258     else
259     result = DeleteFileA(toMBSz(name));
260     if (!result)
261     throw new FileException(name, GetLastError());
262 }
263
264
265 /***************************************************
266  * Get size of file name[].
267  * Throws: FileException on error.
268  */
269
270 ulong getSize(char[] name)
271 {
272     HANDLE findhndl;
273     uint resulth;
274     uint resultl;
275
276     if (useWfuncs)
277     {
278     WIN32_FIND_DATAW filefindbuf;
279
280     findhndl = FindFirstFileW(std.utf.toUTF16z(name), &filefindbuf);
281     resulth = filefindbuf.nFileSizeHigh;
282     resultl = filefindbuf.nFileSizeLow;
283     }
284     else
285     {
286     WIN32_FIND_DATA filefindbuf;
287
288     findhndl = FindFirstFileA(toMBSz(name), &filefindbuf);
289     resulth = filefindbuf.nFileSizeHigh;
290     resultl = filefindbuf.nFileSizeLow;
291     }
292
293     if (findhndl == cast(HANDLE)-1)
294     {
295     throw new FileException(name, GetLastError());
296     }
297     FindClose(findhndl);
298     return (cast(ulong)resulth << 32) + resultl;
299 }
300
301 /*************************
302  * Get creation/access/modified times of file name[].
303  * Throws: FileException on error.
304  */
305
306 void getTimes(char[] name, out d_time ftc, out d_time fta, out d_time ftm)
307 {
308     HANDLE findhndl;
309
310     if (useWfuncs)
311     {
312     WIN32_FIND_DATAW filefindbuf;
313
314     findhndl = FindFirstFileW(std.utf.toUTF16z(name), &filefindbuf);
315     ftc = std.date.FILETIME2d_time(&filefindbuf.ftCreationTime);
316     fta = std.date.FILETIME2d_time(&filefindbuf.ftLastAccessTime);
317     ftm = std.date.FILETIME2d_time(&filefindbuf.ftLastWriteTime);
318     }
319     else
320     {
321     WIN32_FIND_DATA filefindbuf;
322
323     findhndl = FindFirstFileA(toMBSz(name), &filefindbuf);
324     ftc = std.date.FILETIME2d_time(&filefindbuf.ftCreationTime);
325     fta = std.date.FILETIME2d_time(&filefindbuf.ftLastAccessTime);
326     ftm = std.date.FILETIME2d_time(&filefindbuf.ftLastWriteTime);
327     }
328
329     if (findhndl == cast(HANDLE)-1)
330     {
331     throw new FileException(name, GetLastError());
332     }
333     FindClose(findhndl);
334 }
335
336
337 /***************************************************
338  * Does file name[] (or directory) exist?
339  * Return 1 if it does, 0 if not.
340  */
341
342 int exists(char[] name)
343 {
344     uint result;
345
346     if (useWfuncs)
347     // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/base/getfileattributes.asp
348     result = GetFileAttributesW(std.utf.toUTF16z(name));
349     else
350     result = GetFileAttributesA(toMBSz(name));
351
352     return (result == 0xFFFFFFFF) ? 0 : 1;
353 }
354
355 /***************************************************
356  * Get file name[] attributes.
357  * Throws: FileException on error.
358  */
359
360 uint getAttributes(char[] name)
361 {
362     uint result;
363
364     if (useWfuncs)
365     result = GetFileAttributesW(std.utf.toUTF16z(name));
366     else
367     result = GetFileAttributesA(toMBSz(name));
368     if (result == 0xFFFFFFFF)
369     {
370     throw new FileException(name, GetLastError());
371     }
372     return result;
373 }
374
375 /****************************************************
376  * Is name[] a file?
377  * Throws: FileException if name[] doesn't exist.
378  */
379
380 int isfile(char[] name)
381 {
382     return (getAttributes(name) & FILE_ATTRIBUTE_DIRECTORY) == 0;
383 }
384
385 /****************************************************
386  * Is name[] a directory?
387  * Throws: FileException if name[] doesn't exist.
388  */
389
390 int isdir(char[] name)
391 {
392     return (getAttributes(name) & FILE_ATTRIBUTE_DIRECTORY) != 0;
393 }
394
395 /****************************************************
396  * Change directory to pathname[].
397  * Throws: FileException on error.
398  */
399
400 void chdir(char[] pathname)
401 {   BOOL result;
402
403     if (useWfuncs)
404     result = SetCurrentDirectoryW(std.utf.toUTF16z(pathname));
405     else
406     result = SetCurrentDirectoryA(toMBSz(pathname));
407
408     if (!result)
409     {
410     throw new FileException(pathname, GetLastError());
411     }
412 }
413
414 /****************************************************
415  * Make directory pathname[].
416  * Throws: FileException on error.
417  */
418
419 void mkdir(char[] pathname)
420 {   BOOL result;
421
422     if (useWfuncs)
423     result = CreateDirectoryW(std.utf.toUTF16z(pathname), null);
424     else
425     result = CreateDirectoryA(toMBSz(pathname), null);
426
427     if (!result)
428     {
429     throw new FileException(pathname, GetLastError());
430     }
431 }
432
433 /****************************************************
434  * Remove directory pathname[].
435  * Throws: FileException on error.
436  */
437
438 void rmdir(char[] pathname)
439 {   BOOL result;
440
441     if (useWfuncs)
442     result = RemoveDirectoryW(std.utf.toUTF16z(pathname));
443     else
444     result = RemoveDirectoryA(toMBSz(pathname));
445
446     if (!result)
447     {
448     throw new FileException(pathname, GetLastError());
449     }
450 }
451
452 /****************************************************
453  * Get current directory.
454  * Throws: FileException on error.
455  */
456
457 char[] getcwd()
458 {
459     if (useWfuncs)
460     {
461     wchar c;
462
463     auto len = GetCurrentDirectoryW(0, &c);
464     if (!len)
465         goto Lerr;
466     auto dir = new wchar[len];
467     len = GetCurrentDirectoryW(len, dir.ptr);
468     if (!len)
469         goto Lerr;
470     return std.utf.toUTF8(dir[0 .. len]); // leave off terminating 0
471     }
472     else
473     {
474     char c;
475
476     auto len = GetCurrentDirectoryA(0, &c);
477     if (!len)
478         goto Lerr;
479     auto dir = new char[len];
480     len = GetCurrentDirectoryA(len, dir.ptr);
481     if (!len)
482         goto Lerr;
483     return dir[0 .. len];       // leave off terminating 0
484     }
485
486 Lerr:
487     throw new FileException("getcwd", GetLastError());
488 }
489
490 /***************************************************
491  * Directory Entry
492  */
493
494 struct DirEntry
495 {
496     char[] name;            /// file or directory name
497     ulong size = ~0UL;          /// size of file in bytes
498     d_time creationTime = d_time_nan;   /// time of file creation
499     d_time lastAccessTime = d_time_nan; /// time file was last accessed
500     d_time lastWriteTime = d_time_nan;  /// time file was last written to
501     uint attributes;        // Windows file attributes OR'd together
502
503     void init(char[] path, WIN32_FIND_DATA *fd)
504     {
505     wchar[] wbuf;
506     size_t clength;
507     size_t wlength;
508     size_t n;
509
510     clength = std.string.strlen(fd.cFileName.ptr);
511
512     // Convert cFileName[] to unicode
513     wlength = MultiByteToWideChar(0,0,fd.cFileName.ptr,clength,null,0);
514     if (wlength > wbuf.length)
515         wbuf.length = wlength;
516     n = MultiByteToWideChar(0,0,fd.cFileName.ptr,clength,cast(wchar*)wbuf,wlength);
517     assert(n == wlength);
518     // toUTF8() returns a new buffer
519     name = std.path.join(path, std.utf.toUTF8(wbuf[0 .. wlength]));
520
521     size = (cast(ulong)fd.nFileSizeHigh << 32) | fd.nFileSizeLow;
522     creationTime = std.date.FILETIME2d_time(&fd.ftCreationTime);
523     lastAccessTime = std.date.FILETIME2d_time(&fd.ftLastAccessTime);
524     lastWriteTime = std.date.FILETIME2d_time(&fd.ftLastWriteTime);
525     attributes = fd.dwFileAttributes;
526     }
527
528     void init(char[] path, WIN32_FIND_DATAW *fd)
529     {
530     size_t clength = std.string.wcslen(fd.cFileName.ptr);
531     name = std.path.join(path, std.utf.toUTF8(fd.cFileName[0 .. clength]));
532     size = (cast(ulong)fd.nFileSizeHigh << 32) | fd.nFileSizeLow;
533     creationTime = std.date.FILETIME2d_time(&fd.ftCreationTime);
534     lastAccessTime = std.date.FILETIME2d_time(&fd.ftLastAccessTime);
535     lastWriteTime = std.date.FILETIME2d_time(&fd.ftLastWriteTime);
536     attributes = fd.dwFileAttributes;
537     }
538
539     /****
540      * Return !=0 if DirEntry is a directory.
541      */
542     uint isdir()
543     {
544     return attributes & FILE_ATTRIBUTE_DIRECTORY;
545     }
546
547     /****
548      * Return !=0 if DirEntry is a file.
549      */
550     uint isfile()
551     {
552     return !(attributes & FILE_ATTRIBUTE_DIRECTORY);
553     }
554 }
555
556
557 /***************************************************
558  * Return contents of directory pathname[].
559  * The names in the contents do not include the pathname.
560  * Throws: FileException on error
561  * Example:
562  *  This program lists all the files and subdirectories in its
563  *  path argument.
564  * ----
565  * import std.stdio;
566  * import std.file;
567  *
568  * void main(char[][] args)
569  * {
570  *    auto dirs = std.file.listdir(args[1]);
571  *
572  *    foreach (d; dirs)
573  *  writefln(d);
574  * }
575  * ----
576  */
577
578 char[][] listdir(char[] pathname)
579 {
580     char[][] result;
581    
582     bool listing(char[] filename)
583     {
584     result ~= filename;
585     return true; // continue
586     }
587    
588     listdir(pathname, &listing);
589     return result;
590 }
591
592
593 /*****************************************************
594  * Return all the files in the directory and its subdirectories
595  * that match pattern or regular expression r.
596  * Params:
597  *  pathname = Directory name
598  *  pattern = String with wildcards, such as $(RED "*.d"). The supported
599  *      wildcard strings are described under fnmatch() in
600  *      $(LINK2 std_path.html, std.path).
601  *  r = Regular expression, for more powerful _pattern matching.
602  * Example:
603  *  This program lists all the files with a "d" extension in
604  *  the path passed as the first argument.
605  * ----
606  * import std.stdio;
607  * import std.file;
608  *
609  * void main(char[][] args)
610  * {
611  *    auto d_source_files = std.file.listdir(args[1], "*.d");
612  *
613  *    foreach (d; d_source_files)
614  *  writefln(d);
615  * }
616  * ----
617  * A regular expression version that searches for all files with "d" or
618  * "obj" extensions:
619  * ----
620  * import std.stdio;
621  * import std.file;
622  * import std.regexp;
623  *
624  * void main(char[][] args)
625  * {
626  *    auto d_source_files = std.file.listdir(args[1], RegExp(r"\.(d|obj)$"));
627  *
628  *    foreach (d; d_source_files)
629  *  writefln(d);
630  * }
631  * ----
632  */
633
634 char[][] listthisdir(char[] pathname, char[] pattern) { return listdir(pathname, pattern, false); }
635 char[][] listdir(char[] pathname, char[] pattern) { return listdir(pathname, pattern, true); }
636
637 char[][] listdir(char[] pathname, char[] pattern, bool recurse)
638 {   char[][] result;
639    
640     bool callback(DirEntry* de)
641     {
642     if (de.isdir)
643     {
644         if (recurse)
645             listdir(de.name, &callback);
646     }
647     else
648     {   if (std.path.fnmatch(de.name, pattern))
649         result ~= de.name;
650     }
651     return true; // continue
652     }
653    
654     listdir(pathname, &callback);
655     return result;
656 }
657
658 /** Ditto */
659 char[][] listthisdir(char[] pathname, RegExp r) { return listdir(pathname, r, false); }
660 char[][] listdir(char[] pathname, RegExp r) { return listdir(pathname, r, true); }
661
662 char[][] listdir(char[] pathname, RegExp r, bool recurse)
663 {   char[][] result;
664     bool callback(DirEntry* de)
665     {
666     if (de.isdir)
667     {
668         if (recurse)
669             listdir(de.name, &callback);
670     }
671     else
672     {   
673         if (r.test(de.name))
674         result ~= de.name;
675     }
676     return true; // continue
677     }
678    
679     listdir(pathname, &callback);
680     return result;
681 }
682
683 /******************************************************
684  * For each file and directory name in pathname[],
685  * pass it to the callback delegate.
686  * Params:
687  *  callback =  Delegate that processes each
688  *          filename in turn. Returns true to
689  *          continue, false to stop.
690  * Example:
691  *  This program lists all the files in its
692  *  path argument, including the path.
693  * ----
694  * import std.stdio;
695  * import std.path;
696  * import std.file;
697  *
698  * void main(char[][] args)
699  * {
700  *    auto pathname = args[1];
701  *    char[][] result;
702  *
703  *    bool listing(char[] filename)
704  *    {
705  *      result ~= std.path.join(pathname, filename);
706  *      return true; // continue
707  *    }
708  *
709  *    listdir(pathname, &listing);
710  *
711  *    foreach (name; result)
712  *      writefln("%s", name);
713  * }
714  * ----
715  */
716
717 void listdir(char[] pathname, bool delegate(char[] filename) callback)
718 {
719     bool listing(DirEntry* de)
720     {
721     return callback(std.path.getBaseName(de.name));
722     }
723
724     listdir(pathname, &listing);
725 }
726
727 /******************************************************
728  * For each file and directory DirEntry in pathname[],
729  * pass it to the callback delegate.
730  * Params:
731  *  callback =  Delegate that processes each
732  *          DirEntry in turn. Returns true to
733  *          continue, false to stop.
734  * Example:
735  *  This program lists all the files in its
736  *  path argument and all subdirectories thereof.<