Newer
Older
dub_jkp / source / dub / internal / vibecompat / core / file.d
  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. static import std.stream;
  21. import std.string;
  22. import std.utf;
  23.  
  24.  
  25. /* Add output range support to File
  26. */
  27. struct RangeFile {
  28. std.stream.File file;
  29.  
  30. void put(in ubyte[] bytes) { file.writeExact(bytes.ptr, bytes.length); }
  31. void put(in char[] str) { put(cast(ubyte[])str); }
  32. void put(char ch) { put((&ch)[0 .. 1]); }
  33. void put(dchar ch) { char[4] chars; put(chars[0 .. encode(chars, ch)]); }
  34.  
  35. ubyte[] readAll()
  36. {
  37. file.seek(0, std.stream.SeekPos.End);
  38. auto sz = file.position;
  39. enforce(sz <= size_t.max, "File is too big to read to memory.");
  40. file.seek(0, std.stream.SeekPos.Set);
  41. auto ret = new ubyte[cast(size_t)sz];
  42. file.readExact(ret.ptr, ret.length);
  43. return ret;
  44. }
  45.  
  46. void rawRead(ubyte[] dst) { file.readExact(dst.ptr, dst.length); }
  47. void write(string str) { put(str); }
  48. void close() { file.close(); }
  49. void flush() { file.flush(); }
  50. @property ulong size() { 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. std.stream.FileMode fmode;
  60. final switch(mode){
  61. case FileMode.Read: fmode = std.stream.FileMode.In; break;
  62. case FileMode.ReadWrite: fmode = std.stream.FileMode.Out; break;
  63. case FileMode.CreateTrunc: fmode = std.stream.FileMode.OutNew; break;
  64. case FileMode.Append: fmode = std.stream.FileMode.Append; break;
  65. }
  66. auto ret = new std.stream.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. .copy(from.toNativeString(), to.toNativeString());
  117.  
  118. // try to preserve ownership/permissions in Posix
  119. version (Posix) {
  120. import core.sys.posix.sys.stat;
  121. import core.sys.posix.unistd;
  122. import std.utf;
  123. auto cspath = toUTFz!(const(char)*)(from.toNativeString());
  124. auto cdpath = toUTFz!(const(char)*)(to.toNativeString());
  125. stat_t st;
  126. enforce(stat(cspath, &st) == 0, "Failed to get attributes of source file.");
  127. if (chown(cdpath, st.st_uid, st.st_gid) != 0)
  128. st.st_mode &= ~(S_ISUID | S_ISGID);
  129. chmod(cdpath, st.st_mode);
  130. }
  131. }
  132. /// ditto
  133. void copyFile(string from, string to)
  134. {
  135. copyFile(Path(from), Path(to));
  136. }
  137.  
  138. /**
  139. Creates a symlink.
  140. */
  141. version (Windows)
  142. alias symlinkFile = copyFile; // TODO: symlinks on Windows
  143. else version (Posix)
  144. {
  145. void symlinkFile(Path from, Path to, bool overwrite = false)
  146. {
  147. if (existsFile(to)) {
  148. enforce(overwrite, "Destination file already exists.");
  149. // remove file before copy to allow "overwriting" files that are in
  150. // use on Linux
  151. removeFile(to);
  152. }
  153.  
  154. .symlink(from.toNativeString(), to.toNativeString());
  155. }
  156. }
  157.  
  158. void symlinkFile(string from, string to)
  159. {
  160. symlinkFile(Path(from), Path(to));
  161. }
  162.  
  163. /**
  164. Removes a file
  165. */
  166. void removeFile(Path path)
  167. {
  168. removeFile(path.toNativeString());
  169. }
  170. /// ditto
  171. void removeFile(string path) {
  172. std.file.remove(path);
  173. }
  174.  
  175. /**
  176. Checks if a file exists
  177. */
  178. bool existsFile(Path path) {
  179. return existsFile(path.toNativeString());
  180. }
  181. /// ditto
  182. bool existsFile(string path)
  183. {
  184. return std.file.exists(path);
  185. }
  186.  
  187. /** Stores information about the specified file/directory into 'info'
  188.  
  189. Returns false if the file does not exist.
  190. */
  191. FileInfo getFileInfo(Path path)
  192. {
  193. static if (__VERSION__ >= 2064)
  194. auto ent = std.file.DirEntry(path.toNativeString());
  195. else auto ent = std.file.dirEntry(path.toNativeString());
  196. return makeFileInfo(ent);
  197. }
  198. /// ditto
  199. FileInfo getFileInfo(string path)
  200. {
  201. return getFileInfo(Path(path));
  202. }
  203.  
  204. /**
  205. Creates a new directory.
  206. */
  207. void createDirectory(Path path)
  208. {
  209. mkdir(path.toNativeString());
  210. }
  211. /// ditto
  212. void createDirectory(string path)
  213. {
  214. createDirectory(Path(path));
  215. }
  216.  
  217. /**
  218. Enumerates all files in the specified directory.
  219. */
  220. void listDirectory(Path path, scope bool delegate(FileInfo info) del)
  221. {
  222. foreach( DirEntry ent; dirEntries(path.toNativeString(), SpanMode.shallow) )
  223. if( !del(makeFileInfo(ent)) )
  224. break;
  225. }
  226. /// ditto
  227. void listDirectory(string path, scope bool delegate(FileInfo info) del)
  228. {
  229. listDirectory(Path(path), del);
  230. }
  231. /// ditto
  232. int delegate(scope int delegate(ref FileInfo)) iterateDirectory(Path path)
  233. {
  234. int iterator(scope int delegate(ref FileInfo) del){
  235. int ret = 0;
  236. listDirectory(path, (fi){
  237. ret = del(fi);
  238. return ret == 0;
  239. });
  240. return ret;
  241. }
  242. return &iterator;
  243. }
  244. /// ditto
  245. int delegate(scope int delegate(ref FileInfo)) iterateDirectory(string path)
  246. {
  247. return iterateDirectory(Path(path));
  248. }
  249.  
  250.  
  251. /**
  252. Returns the current working directory.
  253. */
  254. Path getWorkingDirectory()
  255. {
  256. return Path(std.file.getcwd());
  257. }
  258.  
  259.  
  260. /** Contains general information about a file.
  261. */
  262. struct FileInfo {
  263. /// Name of the file (not including the path)
  264. string name;
  265.  
  266. /// Size of the file (zero for directories)
  267. ulong size;
  268.  
  269. /// Time of the last modification
  270. SysTime timeModified;
  271.  
  272. /// Time of creation (not available on all operating systems/file systems)
  273. SysTime timeCreated;
  274.  
  275. /// True if this is a symlink to an actual file
  276. bool isSymlink;
  277.  
  278. /// True if this is a directory or a symlink pointing to a directory
  279. bool isDirectory;
  280. }
  281.  
  282. /**
  283. Specifies how a file is manipulated on disk.
  284. */
  285. enum FileMode {
  286. /// The file is opened read-only.
  287. Read,
  288. /// The file is opened for read-write random access.
  289. ReadWrite,
  290. /// The file is truncated if it exists and created otherwise and the opened for read-write access.
  291. CreateTrunc,
  292. /// The file is opened for appending data to it and created if it does not exist.
  293. Append
  294. }
  295.  
  296. /**
  297. Accesses the contents of a file as a stream.
  298. */
  299.  
  300. private FileInfo makeFileInfo(DirEntry ent)
  301. {
  302. FileInfo ret;
  303. ret.name = baseName(ent.name);
  304. if( ret.name.length == 0 ) ret.name = ent.name;
  305. assert(ret.name.length > 0);
  306. ret.isSymlink = ent.isSymlink;
  307. try {
  308. ret.isDirectory = ent.isDir;
  309. ret.size = ent.size;
  310. ret.timeModified = ent.timeLastModified;
  311. version(Windows) ret.timeCreated = ent.timeCreated;
  312. else ret.timeCreated = ent.timeLastModified;
  313. } catch (Exception e) {
  314. logDiagnostic("Failed to get extended file information for %s: %s", ret.name, e.msg);
  315. }
  316. return ret;
  317. }