Newer
Older
dub_jkp / source / dub / internal / vibecompat / core / file.d
@Sönke Ludwig Sönke Ludwig on 10 Mar 2017 7 KB Fix compilation on 2.065.0.
  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 core.stdc.stdio;
  16. import std.datetime;
  17. import std.exception;
  18. import std.file;
  19. import std.path;
  20. import std.stdio;
  21. import std.string;
  22. import std.utf;
  23.  
  24.  
  25. /* Add output range support to File
  26. */
  27. struct RangeFile {
  28. @safe:
  29. std.stdio.File file;
  30.  
  31. void put(in ubyte[] bytes) @trusted { file.rawWrite(bytes); }
  32. void put(in char[] str) { put(cast(const(ubyte)[])str); }
  33. void put(char ch) @trusted { put((&ch)[0 .. 1]); }
  34. void put(dchar ch) { char[4] chars; put(chars[0 .. encode(chars, ch)]); }
  35.  
  36. ubyte[] readAll()
  37. {
  38. auto sz = this.size;
  39. enforce(sz <= size_t.max, "File is too big to read to memory.");
  40. () @trusted { file.seek(0, SEEK_SET); } ();
  41. auto ret = new ubyte[cast(size_t)sz];
  42. rawRead(ret);
  43. return ret;
  44. }
  45.  
  46. void rawRead(ubyte[] dst) @trusted { enforce(file.rawRead(dst).length == dst.length, "Failed to readall bytes from file."); }
  47. void write(string str) { put(str); }
  48. void close() @trusted { file.close(); }
  49. void flush() @trusted { file.flush(); }
  50. @property ulong size() @trusted { return file.size; }
  51. }
  52.  
  53.  
  54. /**
  55. Opens a file stream with the specified mode.
  56. */
  57. RangeFile openFile(Path path, FileMode mode = FileMode.read)
  58. {
  59. string fmode;
  60. final switch(mode){
  61. case FileMode.read: fmode = "rb"; break;
  62. case FileMode.readWrite: fmode = "r+b"; break;
  63. case FileMode.createTrunc: fmode = "wb"; break;
  64. case FileMode.append: fmode = "ab"; break;
  65. }
  66. auto ret = std.stdio.File(path.toNativeString(), fmode);
  67. assert(ret.isOpen);
  68. return RangeFile(ret);
  69. }
  70. /// ditto
  71. RangeFile openFile(string path, FileMode mode = FileMode.read)
  72. {
  73. return openFile(Path(path), mode);
  74. }
  75.  
  76.  
  77. /**
  78. Moves or renames a file.
  79. */
  80. void moveFile(Path from, Path to)
  81. {
  82. moveFile(from.toNativeString(), to.toNativeString());
  83. }
  84. /// ditto
  85. void moveFile(string from, string to)
  86. {
  87. std.file.rename(from, to);
  88. }
  89.  
  90. /**
  91. Copies a file.
  92.  
  93. Note that attributes and time stamps are currently not retained.
  94.  
  95. Params:
  96. from = Path of the source file
  97. to = Path for the destination file
  98. overwrite = If true, any file existing at the destination path will be
  99. overwritten. If this is false, an excpetion will be thrown should
  100. a file already exist at the destination path.
  101.  
  102. Throws:
  103. An Exception if the copy operation fails for some reason.
  104. */
  105. void copyFile(Path from, Path to, bool overwrite = false)
  106. {
  107. enforce(existsFile(from), "Source file does not exist.");
  108.  
  109. if (existsFile(to)) {
  110. enforce(overwrite, "Destination file already exists.");
  111. // remove file before copy to allow "overwriting" files that are in
  112. // use on Linux
  113. removeFile(to);
  114. }
  115.  
  116. static if (is(PreserveAttributes))
  117. {
  118. .copy(from.toNativeString(), to.toNativeString(), PreserveAttributes.yes);
  119. }
  120. else
  121. {
  122. .copy(from.toNativeString(), to.toNativeString());
  123. // try to preserve ownership/permissions in Posix
  124. version (Posix) {
  125. import core.sys.posix.sys.stat;
  126. import core.sys.posix.unistd;
  127. import std.utf;
  128. auto cspath = toUTFz!(const(char)*)(from.toNativeString());
  129. auto cdpath = toUTFz!(const(char)*)(to.toNativeString());
  130. stat_t st;
  131. enforce(stat(cspath, &st) == 0, "Failed to get attributes of source file.");
  132. if (chown(cdpath, st.st_uid, st.st_gid) != 0)
  133. st.st_mode &= ~(S_ISUID | S_ISGID);
  134. chmod(cdpath, st.st_mode);
  135. }
  136. }
  137. }
  138. /// ditto
  139. void copyFile(string from, string to)
  140. {
  141. copyFile(Path(from), Path(to));
  142. }
  143.  
  144. version (Windows) extern(Windows) int CreateHardLinkW(in wchar* to, in wchar* from, void* attr=null);
  145.  
  146. // guess whether 2 files are identical, ignores filename and content
  147. private bool sameFile(Path a, Path b)
  148. {
  149. version (Posix) {
  150. auto st_a = std.file.DirEntry(a.toNativeString).statBuf;
  151. auto st_b = std.file.DirEntry(b.toNativeString).statBuf;
  152. return st_a == st_b;
  153. } else {
  154. static assert(__traits(allMembers, FileInfo)[0] == "name");
  155. return getFileInfo(a).tupleof[1 .. $] == getFileInfo(b).tupleof[1 .. $];
  156. }
  157. }
  158.  
  159. /**
  160. Creates a hardlink.
  161. */
  162. void hardLinkFile(Path from, Path to, bool overwrite = false)
  163. {
  164. if (existsFile(to)) {
  165. enforce(overwrite, "Destination file already exists.");
  166. if (auto fe = collectException!FileException(removeFile(to))) {
  167. if (sameFile(from, to)) return;
  168. throw fe;
  169. }
  170. }
  171.  
  172. version (Windows)
  173. {
  174. alias cstr = toUTFz!(const(wchar)*);
  175. if (CreateHardLinkW(cstr(to.toNativeString), cstr(from.toNativeString)))
  176. return;
  177. }
  178. else
  179. {
  180. import core.sys.posix.unistd : link;
  181. alias cstr = toUTFz!(const(char)*);
  182. if (!link(cstr(from.toNativeString), cstr(to.toNativeString)))
  183. return;
  184. }
  185. // fallback to copy
  186. copyFile(from, to, overwrite);
  187. }
  188.  
  189. /**
  190. Removes a file
  191. */
  192. void removeFile(Path path)
  193. {
  194. removeFile(path.toNativeString());
  195. }
  196. /// ditto
  197. void removeFile(string path) {
  198. std.file.remove(path);
  199. }
  200.  
  201. /**
  202. Checks if a file exists
  203. */
  204. bool existsFile(Path path) {
  205. return existsFile(path.toNativeString());
  206. }
  207. /// ditto
  208. bool existsFile(string path)
  209. {
  210. return std.file.exists(path);
  211. }
  212.  
  213. /** Stores information about the specified file/directory into 'info'
  214.  
  215. Returns false if the file does not exist.
  216. */
  217. FileInfo getFileInfo(Path path)
  218. {
  219. static if (__VERSION__ >= 2064)
  220. auto ent = std.file.DirEntry(path.toNativeString());
  221. else auto ent = std.file.dirEntry(path.toNativeString());
  222. return makeFileInfo(ent);
  223. }
  224. /// ditto
  225. FileInfo getFileInfo(string path)
  226. {
  227. return getFileInfo(Path(path));
  228. }
  229.  
  230. /**
  231. Creates a new directory.
  232. */
  233. void createDirectory(Path path)
  234. {
  235. mkdir(path.toNativeString());
  236. }
  237. /// ditto
  238. void createDirectory(string path)
  239. {
  240. createDirectory(Path(path));
  241. }
  242.  
  243. /**
  244. Enumerates all files in the specified directory.
  245. */
  246. void listDirectory(Path path, scope bool delegate(FileInfo info) del)
  247. {
  248. foreach( DirEntry ent; dirEntries(path.toNativeString(), SpanMode.shallow) )
  249. if( !del(makeFileInfo(ent)) )
  250. break;
  251. }
  252. /// ditto
  253. void listDirectory(string path, scope bool delegate(FileInfo info) del)
  254. {
  255. listDirectory(Path(path), del);
  256. }
  257. /// ditto
  258. int delegate(scope int delegate(ref FileInfo)) iterateDirectory(Path path)
  259. {
  260. int iterator(scope int delegate(ref FileInfo) del){
  261. int ret = 0;
  262. listDirectory(path, (fi){
  263. ret = del(fi);
  264. return ret == 0;
  265. });
  266. return ret;
  267. }
  268. return &iterator;
  269. }
  270. /// ditto
  271. int delegate(scope int delegate(ref FileInfo)) iterateDirectory(string path)
  272. {
  273. return iterateDirectory(Path(path));
  274. }
  275.  
  276.  
  277. /**
  278. Returns the current working directory.
  279. */
  280. Path getWorkingDirectory()
  281. {
  282. return Path(std.file.getcwd());
  283. }
  284.  
  285.  
  286. /** Contains general information about a file.
  287. */
  288. struct FileInfo {
  289. /// Name of the file (not including the path)
  290. string name;
  291.  
  292. /// Size of the file (zero for directories)
  293. ulong size;
  294.  
  295. /// Time of the last modification
  296. SysTime timeModified;
  297.  
  298. /// Time of creation (not available on all operating systems/file systems)
  299. SysTime timeCreated;
  300.  
  301. /// True if this is a symlink to an actual file
  302. bool isSymlink;
  303.  
  304. /// True if this is a directory or a symlink pointing to a directory
  305. bool isDirectory;
  306. }
  307.  
  308. /**
  309. Specifies how a file is manipulated on disk.
  310. */
  311. enum FileMode {
  312. /// The file is opened read-only.
  313. read,
  314. /// The file is opened for read-write random access.
  315. readWrite,
  316. /// The file is truncated if it exists and created otherwise and the opened for read-write access.
  317. createTrunc,
  318. /// The file is opened for appending data to it and created if it does not exist.
  319. append
  320. }
  321.  
  322. /**
  323. Accesses the contents of a file as a stream.
  324. */
  325.  
  326. private FileInfo makeFileInfo(DirEntry ent)
  327. {
  328. FileInfo ret;
  329. ret.name = baseName(ent.name);
  330. if( ret.name.length == 0 ) ret.name = ent.name;
  331. assert(ret.name.length > 0);
  332. ret.isSymlink = ent.isSymlink;
  333. try {
  334. ret.isDirectory = ent.isDir;
  335. ret.size = ent.size;
  336. ret.timeModified = ent.timeLastModified;
  337. version(Windows) ret.timeCreated = ent.timeCreated;
  338. else ret.timeCreated = ent.timeLastModified;
  339. } catch (Exception e) {
  340. logDiagnostic("Failed to get extended file information for %s: %s", ret.name, e.msg);
  341. }
  342. return ret;
  343. }