1 /** 2 * Betterc: Frequently used primitives suitable for use in the BetterC D subset. 3 * 4 * Copyright: Maxim Freck, 2018. 5 * Authors: Maxim Freck <maxim@freck.pp.ru> 6 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 7 */ 8 module betterc.io.file; 9 10 /******* 11 * Encapsulates a $(D FILE*). 12 */ 13 struct File { 14 private import core.stdc.stdio;//: SEEK_SET, SEEK_CUR, SEEK_END, FILE; 15 private import core.stdc.stdlib: malloc, free; 16 private import betterc.ds.stringz: Stringz; 17 18 /// Offset is relative to the beginning 19 enum seekSet = SEEK_SET; 20 /// Offset is relative to the current position 21 enum seekCur = SEEK_CUR; 22 /// Offset is relative to the end 23 enum seekEnd = SEEK_END; 24 25 26 private struct Payload { 27 Stringz filename; 28 FILE *fd; 29 30 size_t count; 31 @disable this(this); // not copyable 32 } 33 34 private Payload *payload; 35 36 /******* 37 * Creates a temporary file without a name 38 */ 39 public static typeof(this) tmpfile() nothrow @nogc 40 { 41 return File(core.stdc.stdio.tmpfile()); 42 } 43 44 private this(FILE *f) nothrow @nogc 45 { 46 payload = (cast(Payload*)malloc(Payload.sizeof)); 47 payload.filename = Stringz(""); 48 payload.fd = f; 49 payload.count = 1; 50 } 51 52 /******* 53 * Constructor 54 * 55 * Params: 56 * filename = Null terminated file name 57 * mode = Null terminated access mode 58 */ 59 public this(string filename, in string mode) nothrow @nogc 60 { 61 payload = (cast(Payload*)malloc(Payload.sizeof)); 62 payload.filename = Stringz(filename); 63 payload.fd = fopen(*payload.filename, *Stringz(mode)); 64 payload.count = 1; 65 } 66 67 ///ditto 68 this (this) nothrow @nogc 69 { 70 if (payload !is null) 71 payload.count++; 72 } 73 74 ///Ref. counting during structure assignment 75 ref typeof(this) opAssign(ref typeof(this) rhs) return 76 { 77 this.payload = rhs.payload; 78 if (payload !is null) 79 payload.count++; 80 81 return this; 82 } 83 84 /******* 85 * Destructor: frees all memory 86 */ 87 ~this() nothrow @nogc 88 { 89 if (payload !is null && --payload.count == 0) { 90 this.close(); 91 free(payload); 92 } 93 } 94 95 96 /******* 97 * Reuses stream to change its access mode. 98 * 99 * Params: 100 * mode = Null terminated access mode 101 */ 102 public void reopen(in string mode) nothrow @nogc 103 { 104 payload.fd = freopen(*payload.filename, *Stringz(mode), payload.fd); 105 } 106 107 /******* 108 * Returns: underlying FILE* 109 */ 110 public FILE* stream() nothrow @nogc 111 { 112 return payload.fd; 113 } 114 115 ///ditto 116 pragma(inline) 117 FILE* opUnary(string s)() nothrow @nogc if (s == "*") 118 { 119 return payload.fd; 120 } 121 122 /******* 123 * Reads up to count objects into the array buffer 124 * 125 * Params: 126 * ptr = pointer to the first object in the array to be read 127 * size = size of each object in bytes 128 * nmemb = the number of the objects to be read 129 * 130 * Returns: number of objects read successfully 131 */ 132 public size_t read(void* ptr, size_t size, size_t nmemb) nothrow @nogc 133 { 134 return fread(ptr, size, nmemb, payload.fd); 135 } 136 137 ///ditto 138 public size_t read(T)(T[] buf) nothrow @nogc 139 { 140 return read(buf.ptr, T.sizeof, buf.length); 141 } 142 143 144 /******* 145 * Writes up to count binary objects from the given array buffer 146 * 147 * Params: 148 * ptr = pointer to the first object object in the array to be written 149 * size = size of each object in bytes 150 * nmemb = the number of the objects to be read 151 * 152 * Returns: number of objects written successfully 153 */ 154 public size_t write(in void* ptr, size_t size, size_t nmemb) nothrow @nogc 155 { 156 return fwrite(ptr, size, nmemb, payload.fd); 157 } 158 159 ///ditto 160 public size_t write(T)(in T[] buf) nothrow @nogc 161 { 162 return write(buf.ptr, T.sizeof, buf.length); 163 } 164 165 166 167 ref typeof(this) opBinary(char* op, T)(in T[] pos) if (op == "<<") 168 { 169 write(buf.ptr, T.sizeof, buf.length); 170 return this; 171 } 172 173 //--- 174 public size_t read(T)(ref T b) nothrow @nogc 175 { 176 return read(&b, T.sizeof, 1); 177 } 178 179 public size_t write(T)(in T b) nothrow @nogc 180 { 181 return write(&b, T.sizeof, 1); 182 } 183 184 ref typeof(this) opBinary(char* op, T)(in T b) if (op == "<<") 185 { 186 write(&b, T.sizeof, 1); 187 return this; 188 } 189 190 ref typeof(this) opBinary(char* op)(string str) if (op == "<<") 191 { 192 write(str.ptr, 1, str.length); 193 return this; 194 } 195 196 /+++ It's broken at the moment 197 extern(C) public int dprintf(string format, ...) nothrow @nogc 198 { 199 va_list args; 200 va_start(args, format); 201 return vfprintf(payload.fd, *Stringz(format), args); 202 } 203 204 extern(C) public int printf(in char* format, ...) nothrow @nogc 205 { 206 va_list args; 207 va_start(args, format); 208 return vfprintf(payload.fd, format, args); 209 } 210 +++/ 211 212 /******* 213 * Returns: the length of the FILE* 214 */ 215 public size_t length() nothrow @nogc 216 { 217 immutable auto seekSave = ftell(payload.fd); 218 fseek(payload.fd, 0, SEEK_END); 219 auto fileSize = ftell(payload.fd); 220 fseek(payload.fd, seekSave, SEEK_SET); 221 return fileSize; 222 } 223 224 /******* 225 * Returns: true if the end of the stream has been reached, otherwise false 226 */ 227 public bool eof() nothrow @nogc 228 { 229 return feof(payload.fd) > 0; 230 } 231 232 /******* 233 * Sets the file position indicator 234 * 235 * Params: 236 * offset = number of characters to shift the position relative to origin 237 * origin = position to which offset is added 238 * 239 * Returns: true upon success, false otherwise 240 */ 241 public bool seek(size_t offset, int origin) nothrow @nogc 242 { 243 return fseek(payload.fd, cast(int)offset, origin) == 0; 244 } 245 246 /******* 247 * Checks the stream for errors 248 * 249 * Returns: true if the file stream has errors occurred, false otherwise 250 */ 251 public bool error() nothrow @nogc 252 { 253 return ferror(payload.fd) > 0; 254 } 255 256 /******* 257 * Writes any unwritten data from the stream's buffer to the associated output device 258 * 259 * Returns: true on success 260 */ 261 public bool flush() nothrow @nogc 262 { 263 return fflush(payload.fd) == 0; 264 } 265 266 /******* 267 * Closes the file stream 268 * 269 * Returns: true on success 270 */ 271 public void close() nothrow @nogc 272 { 273 if (payload.fd == null) return; 274 275 fclose(payload.fd); 276 payload.fd = null; 277 } 278 }