1 /**
2 	File handling.
3 
4 	Copyright: © 2012 RejectedSoftware e.K.
5 	License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
6 	Authors: Sönke Ludwig
7 */
8 module dub.internal.vibecompat.core.file;
9 
10 public import dub.internal.vibecompat.inet.url;
11 
12 import dub.internal.vibecompat.core.log;
13 
14 import std.conv;
15 import std.c.stdio;
16 import std.datetime;
17 import std.exception;
18 import std.file;
19 import std.path;
20 static import std.stream;
21 import std.string;
22 import std.utf;
23 
24 
25 version(Posix){
26 	private extern(C) int mkstemps(char* templ, int suffixlen);
27 }
28 
29 
30 /* Add output range support to File
31 */
32 struct RangeFile {
33 	std.stream.File file;
34 
35 	void put(in ubyte[] bytes) { file.writeExact(bytes.ptr, bytes.length); }
36 	void put(in char[] str) { put(cast(ubyte[])str); }
37 	void put(char ch) { put((&ch)[0 .. 1]); }
38 	void put(dchar ch) { char[4] chars; put(chars[0 .. encode(chars, ch)]); }
39 	
40 	ubyte[] readAll()
41 	{
42 		file.seek(0, std.stream.SeekPos.End);
43 		auto sz = file.position;
44 		enforce(sz <= size_t.max, "File is too big to read to memory.");
45 		file.seek(0, std.stream.SeekPos.Set);
46 		auto ret = new ubyte[cast(size_t)sz];
47 		file.readExact(ret.ptr, ret.length);
48 		return ret;
49 	}
50 
51 	void rawRead(ubyte[] dst) { file.readExact(dst.ptr, dst.length); }
52 	void write(string str) { put(str); }
53 	void close() { file.close(); }
54 	void flush() { file.flush(); }
55 	@property ulong size() { return file.size; }
56 }
57 
58 
59 /**
60 	Opens a file stream with the specified mode.
61 */
62 RangeFile openFile(Path path, FileMode mode = FileMode.Read)
63 {
64 	std.stream.FileMode fmode;
65 	final switch(mode){
66 		case FileMode.Read: fmode = std.stream.FileMode.In; break;
67 		case FileMode.ReadWrite: fmode = std.stream.FileMode.Out; break;
68 		case FileMode.CreateTrunc: fmode = std.stream.FileMode.OutNew; break;
69 		case FileMode.Append: fmode = std.stream.FileMode.Append; break;
70 	}
71 	auto ret = new std.stream.File(path.toNativeString(), fmode);
72 	assert(ret.isOpen());
73 	return RangeFile(ret);
74 }
75 /// ditto
76 RangeFile openFile(string path, FileMode mode = FileMode.Read)
77 {
78 	return openFile(Path(path), mode);
79 }
80 
81 
82 /**
83 	Moves or renames a file.
84 */
85 void moveFile(Path from, Path to)
86 {
87 	moveFile(from.toNativeString(), to.toNativeString());
88 }
89 /// ditto
90 void moveFile(string from, string to)
91 {
92 	std.file.rename(from, to);
93 }
94 
95 /**
96 	Copies a file.
97 
98 	Note that attributes and time stamps are currently not retained.
99 
100 	Params:
101 		from = Path of the source file
102 		to = Path for the destination file
103 		overwrite = If true, any file existing at the destination path will be
104 			overwritten. If this is false, an excpetion will be thrown should
105 			a file already exist at the destination path.
106 
107 	Throws:
108 		An Exception if the copy operation fails for some reason.
109 */
110 void copyFile(Path from, Path to, bool overwrite = false)
111 {
112 	enforce(overwrite || !existsFile(to), "Destination file already exists.");
113 	.copy(from.toNativeString(), to.toNativeString());
114 }
115 /// ditto
116 void copyFile(string from, string to)
117 {
118 	copyFile(Path(from), Path(to));
119 }
120 
121 /**
122 	Removes a file
123 */
124 void removeFile(Path path)
125 {
126 	removeFile(path.toNativeString());
127 }
128 /// ditto
129 void removeFile(string path) {
130 	std.file.remove(path);
131 }
132 
133 /**
134 	Checks if a file exists
135 */
136 bool existsFile(Path path) {
137 	return existsFile(path.toNativeString());
138 }
139 /// ditto
140 bool existsFile(string path)
141 {
142 	return std.file.exists(path);
143 }
144 
145 /** Stores information about the specified file/directory into 'info'
146 
147 	Returns false if the file does not exist.
148 */
149 FileInfo getFileInfo(Path path)
150 {
151 	auto ent = std.file.dirEntry(path.toNativeString());
152 	return makeFileInfo(ent);
153 }
154 /// ditto
155 FileInfo getFileInfo(string path)
156 {
157 	return getFileInfo(Path(path));
158 }
159 
160 /**
161 	Creates a new directory.
162 */
163 void createDirectory(Path path)
164 {
165 	mkdir(path.toNativeString());
166 }
167 /// ditto
168 void createDirectory(string path)
169 {
170 	createDirectory(Path(path));
171 }
172 
173 /**
174 	Enumerates all files in the specified directory.
175 */
176 void listDirectory(Path path, scope bool delegate(FileInfo info) del)
177 {
178 	foreach( DirEntry ent; dirEntries(path.toNativeString(), SpanMode.shallow) )
179 		if( !del(makeFileInfo(ent)) )
180 			break;
181 }
182 /// ditto
183 void listDirectory(string path, scope bool delegate(FileInfo info) del)
184 {
185 	listDirectory(Path(path), del);
186 }
187 /// ditto
188 int delegate(scope int delegate(ref FileInfo)) iterateDirectory(Path path)
189 {
190 	int iterator(scope int delegate(ref FileInfo) del){
191 		int ret = 0;
192 		listDirectory(path, (fi){
193 			ret = del(fi);
194 			return ret == 0;
195 		});
196 		return ret;
197 	}
198 	return &iterator;
199 }
200 /// ditto
201 int delegate(scope int delegate(ref FileInfo)) iterateDirectory(string path)
202 {
203 	return iterateDirectory(Path(path));
204 }
205 
206 
207 /**
208 	Returns the current working directory.
209 */
210 Path getWorkingDirectory()
211 {
212 	return Path(std.file.getcwd());
213 }
214 
215 
216 /** Contains general information about a file.
217 */
218 struct FileInfo {
219 	/// Name of the file (not including the path)
220 	string name;
221 
222 	/// Size of the file (zero for directories)
223 	ulong size;
224 
225 	/// Time of the last modification
226 	SysTime timeModified;
227 
228 	/// Time of creation (not available on all operating systems/file systems)
229 	SysTime timeCreated;
230 
231 	/// True if this is a symlink to an actual file
232 	bool isSymlink;
233 
234 	/// True if this is a directory or a symlink pointing to a directory
235 	bool isDirectory;
236 }
237 
238 /**
239 	Specifies how a file is manipulated on disk.
240 */
241 enum FileMode {
242 	/// The file is opened read-only.
243 	Read,
244 	/// The file is opened for read-write random access.
245 	ReadWrite,
246 	/// The file is truncated if it exists and created otherwise and the opened for read-write access.
247 	CreateTrunc,
248 	/// The file is opened for appending data to it and created if it does not exist.
249 	Append
250 }
251 
252 /**
253 	Accesses the contents of a file as a stream.
254 */
255 
256 private FileInfo makeFileInfo(DirEntry ent)
257 {
258 	FileInfo ret;
259 	ret.name = baseName(ent.name);
260 	if( ret.name.length == 0 ) ret.name = ent.name;
261 	assert(ret.name.length > 0);
262 	ret.size = ent.size;
263 	ret.timeModified = ent.timeLastModified;
264 	version(Windows) ret.timeCreated = ent.timeCreated;
265 	else ret.timeCreated = ent.timeLastModified;
266 	ret.isSymlink = ent.isSymlink;
267 	ret.isDirectory = ent.isDir;
268 	return ret;
269 }
270