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 	if (existsFile(to)) {
113 		enforce(overwrite, "Destination file already exists.");
114 		// remove file before copy to allow "overwriting" files that are in
115 		// use on Linux
116 		removeFile(to);
117 	}
118 
119 	.copy(from.toNativeString(), to.toNativeString());
120 
121 	// try to preserve ownership/permissions in Posix
122 	version (Posix) {
123 		import core.sys.posix.sys.stat;
124 		import core.sys.posix.unistd;
125 		import std.utf;
126 		auto cspath = toUTFz!(const(char)*)(from.toNativeString());
127 		auto cdpath = toUTFz!(const(char)*)(to.toNativeString());
128 		stat_t st;
129 		enforce(stat(cspath, &st) == 0, "Failed to get attributes of source file.");
130 		if (chown(cdpath, st.st_uid, st.st_gid) != 0)
131 			st.st_mode &= ~(S_ISUID | S_ISGID);
132 		chmod(cdpath, st.st_mode);
133 	}
134 }
135 /// ditto
136 void copyFile(string from, string to)
137 {
138 	copyFile(Path(from), Path(to));
139 }
140 
141 /**
142 	Removes a file
143 */
144 void removeFile(Path path)
145 {
146 	removeFile(path.toNativeString());
147 }
148 /// ditto
149 void removeFile(string path) {
150 	std.file.remove(path);
151 }
152 
153 /**
154 	Checks if a file exists
155 */
156 bool existsFile(Path path) {
157 	return existsFile(path.toNativeString());
158 }
159 /// ditto
160 bool existsFile(string path)
161 {
162 	return std.file.exists(path);
163 }
164 
165 /** Stores information about the specified file/directory into 'info'
166 
167 	Returns false if the file does not exist.
168 */
169 FileInfo getFileInfo(Path path)
170 {
171 	static if (__VERSION__ >= 2064)
172 		auto ent = std.file.DirEntry(path.toNativeString());
173 	else auto ent = std.file.dirEntry(path.toNativeString());
174 	return makeFileInfo(ent);
175 }
176 /// ditto
177 FileInfo getFileInfo(string path)
178 {
179 	return getFileInfo(Path(path));
180 }
181 
182 /**
183 	Creates a new directory.
184 */
185 void createDirectory(Path path)
186 {
187 	mkdir(path.toNativeString());
188 }
189 /// ditto
190 void createDirectory(string path)
191 {
192 	createDirectory(Path(path));
193 }
194 
195 /**
196 	Enumerates all files in the specified directory.
197 */
198 void listDirectory(Path path, scope bool delegate(FileInfo info) del)
199 {
200 	foreach( DirEntry ent; dirEntries(path.toNativeString(), SpanMode.shallow) )
201 		if( !del(makeFileInfo(ent)) )
202 			break;
203 }
204 /// ditto
205 void listDirectory(string path, scope bool delegate(FileInfo info) del)
206 {
207 	listDirectory(Path(path), del);
208 }
209 /// ditto
210 int delegate(scope int delegate(ref FileInfo)) iterateDirectory(Path path)
211 {
212 	int iterator(scope int delegate(ref FileInfo) del){
213 		int ret = 0;
214 		listDirectory(path, (fi){
215 			ret = del(fi);
216 			return ret == 0;
217 		});
218 		return ret;
219 	}
220 	return &iterator;
221 }
222 /// ditto
223 int delegate(scope int delegate(ref FileInfo)) iterateDirectory(string path)
224 {
225 	return iterateDirectory(Path(path));
226 }
227 
228 
229 /**
230 	Returns the current working directory.
231 */
232 Path getWorkingDirectory()
233 {
234 	return Path(std.file.getcwd());
235 }
236 
237 
238 /** Contains general information about a file.
239 */
240 struct FileInfo {
241 	/// Name of the file (not including the path)
242 	string name;
243 
244 	/// Size of the file (zero for directories)
245 	ulong size;
246 
247 	/// Time of the last modification
248 	SysTime timeModified;
249 
250 	/// Time of creation (not available on all operating systems/file systems)
251 	SysTime timeCreated;
252 
253 	/// True if this is a symlink to an actual file
254 	bool isSymlink;
255 
256 	/// True if this is a directory or a symlink pointing to a directory
257 	bool isDirectory;
258 }
259 
260 /**
261 	Specifies how a file is manipulated on disk.
262 */
263 enum FileMode {
264 	/// The file is opened read-only.
265 	Read,
266 	/// The file is opened for read-write random access.
267 	ReadWrite,
268 	/// The file is truncated if it exists and created otherwise and the opened for read-write access.
269 	CreateTrunc,
270 	/// The file is opened for appending data to it and created if it does not exist.
271 	Append
272 }
273 
274 /**
275 	Accesses the contents of a file as a stream.
276 */
277 
278 private FileInfo makeFileInfo(DirEntry ent)
279 {
280 	FileInfo ret;
281 	ret.name = baseName(ent.name);
282 	if( ret.name.length == 0 ) ret.name = ent.name;
283 	assert(ret.name.length > 0);
284 	ret.isSymlink = ent.isSymlink;
285 	try {
286 		ret.isDirectory = ent.isDir;
287 		ret.size = ent.size;
288 		ret.timeModified = ent.timeLastModified;
289 		version(Windows) ret.timeCreated = ent.timeCreated;
290 		else ret.timeCreated = ent.timeLastModified;
291 	} catch (Exception e) {
292 		logDiagnostic("Failed to get extended file information for %s: %s", ret.name, e.msg);
293 	}
294 	return ret;
295 }
296