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 }