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.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.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 	this (this) nothrow @nogc
68 	{
69 		if (payload !is null)
70 			payload.count++;
71 	}
72 
73 	///Ref. counting during structure assignation
74 	ref typeof(this) opAssign(ref typeof(this) rhs)
75 	{
76 		this.payload = rhs.payload;
77 		if (payload !is null)
78 			payload.count++;
79 
80 		return this;
81 	}
82 
83 	~this() nothrow @nogc
84 	{
85 		if (payload !is null && --payload.count == 0) {
86 			this.close();
87 			free(payload);
88 		}
89 	}
90 
91 
92 	/*******
93 	* Reuses stream to change its access mode.
94 	*
95 	* Params:
96 	*  mode = Null terminated access mode
97 	*/
98 	public void reopen(in string mode) nothrow @nogc
99 	{
100 		payload.fd = freopen(*payload.filename, *Stringz(mode), payload.fd);
101 	}
102 
103 	/*******
104 	* Returns underlying FILE*
105 	*/
106 	public FILE* stream() nothrow @nogc
107 	{
108 		return payload.fd;
109 	}
110 
111 	///ditto
112 	pragma(inline)
113 	FILE* opUnary(string s)() nothrow @nogc if (s == "*")
114 	{
115 		return payload.fd;
116 	}
117 
118 	//---
119 	public size_t read(void* ptr, size_t size, size_t nmemb) nothrow @nogc
120 	{
121 		return fread(ptr, size, nmemb, payload.fd);
122 	}
123 
124 	///ditto
125 	public size_t read(T)(T[] buf) nothrow @nogc
126 	{
127 		//dbg("\nReading to ptr %u %u items of %u size\n", buf.ptr, buf.length, T.sizeof);
128 		return read(buf.ptr, T.sizeof, buf.length);
129 	}
130 
131 
132 	///---
133 	public size_t write(in void* ptr, size_t size, size_t nmemb) nothrow @nogc
134 	{
135 		return fwrite(ptr, size, nmemb, payload.fd);
136 	}
137 
138 	///ditto
139 	public size_t write(T)(in T[] buf) nothrow @nogc
140 	{
141 		return write(buf.ptr, T.sizeof, buf.length);
142 	}
143 
144 
145 
146 	ref typeof(this) opBinary(char* op, T)(in T[] pos) if (op == "<<")
147 	{
148 		write(buf.ptr, T.sizeof, buf.length);
149 		return this;
150 	}
151 
152 	//---
153 	public size_t read(T)(ref T b) nothrow @nogc
154 	{
155 		return read(&b, T.sizeof, 1);
156 	}
157 
158 	public size_t write(T)(in T b) nothrow @nogc
159 	{
160 		return write(&b, T.sizeof, 1);
161 	}
162 
163 	ref typeof(this) opBinary(char* op, T)(in T b) if (op == "<<")
164 	{
165 		write(&b, T.sizeof, 1);
166 		return this;
167 	}
168 
169 	ref typeof(this) opBinary(char* op)(string str) if (op == "<<")
170 	{
171 		write(str.ptr, 1, str.length);
172 		return this;
173 	}
174 
175 	/+++ It's broken at the moment
176 	extern(C) public int dprintf(string format, ...) nothrow @nogc
177 	{
178 		va_list args;
179 		va_start(args, format);
180 		return vfprintf(payload.fd, *Stringz(format), args);
181 	}
182 
183 	extern(C) public int printf(in char* format, ...) nothrow @nogc
184 	{
185 		va_list args;
186 		va_start(args, format);
187 		return vfprintf(payload.fd, format, args);
188 	}
189 	+++/
190 
191 	public size_t length() nothrow @nogc
192 	{
193 		immutable auto seekSave = ftell(payload.fd);
194 		fseek(payload.fd, 0, SEEK_END);
195 		auto fileSize = ftell(payload.fd);
196 		fseek(payload.fd, seekSave, SEEK_SET);
197 		return fileSize;
198 	}
199 
200 	public bool eof() nothrow @nogc
201 	{
202 		return feof(payload.fd) > 0;
203 	}
204 
205 	public bool seek(size_t offset, int origin) nothrow @nogc
206 	{
207 		return fseek(payload.fd, cast(int)offset, origin) == 0;
208 	}
209 
210 	public bool error() nothrow @nogc
211 	{
212 		return ferror(payload.fd) > 0;
213 	}
214 
215 	public bool flush() nothrow @nogc
216 	{
217 		return fflush(payload.fd) == 0;
218 	}
219 
220 	public void close() nothrow @nogc
221 	{
222 		if (payload.fd == null) return;
223 
224 		fclose(payload.fd);
225 		payload.fd = null;
226 	}
227 }