1#include <errno.h>
2#include <unistd.h>
3
4#include "include/pw.h"
5#include "include/pwlib/file.h"
6#include "src/pw_interfaces_internal.h"
7
8uint16_t PwInterfaceId_File = 0;
9
10uint16_t PwTypeId_File = 0;
11
12typedef struct {
13 _PwValue name;
14 int fd; // file descriptor
15 bool is_external_fd; // fd is set by `set_fd`
16 bool own_fd; // fd is owned, If not owned, it is not closed by `close`
17 int error; // errno, set by `open`
18
19} _PwFile;
20
21
22static inline bool do_file_read(PwValuePtr file, void* buffer, unsigned buffer_size, unsigned* bytes_read)
23// call Reader::read for File type
24{
25 PwInterface_Reader* reader = (PwInterface_Reader*) pw_get_interface(PwTypeId_File, PwInterfaceId_Reader);
26 return pw_call2(reader, read, file, buffer, buffer_size, bytes_read);
27}
28
29static inline bool do_file_write(PwValuePtr file, void* data, unsigned size, unsigned* bytes_written)
30// call Writer::write for File type
31{
32 PwInterface_Writer* writer = (PwInterface_Writer*) pw_get_interface(PwTypeId_File, PwInterfaceId_Writer);
33 return pw_call2(writer, write, file, data, size, bytes_written);
34}
35
36/****************************************************************
37 * Basic interface
38 */
39
40static bool file_create(PwMethod_Basic_create* mthis, PwValuePtr result, PwCtorArgs* ctor_args)
41{
42 if (!pw_super(mthis, result, ctor_args)) {
43 return false;
44 }
45
46 _PwFile* f = pw_this_data(result);
47 f->fd = -1;
48 f->name = PwNull();
49 return true;
50}
51
52static bool file_destroy(PwMethod_Basic_destroy* mthis, PwValuePtr self, _PwCompoundChain* tail)
53{
54 PwValuePtr value_seen = _pw_on_chain(self, tail);
55 if (value_seen) {
56 return true;
57 }
58 if (!pw_close(self)) {
59 fprintf(stderr, "Failed %s\n", __func__);
60 pw_print_status(stderr, ¤t_task->status);
61 }
62 return pw_super(mthis, self, tail);
63}
64
65static bool file_hash(PwMethod_Basic_hash* mthis, PwValuePtr self, PwHashContext* ctx, _PwCompoundChain* tail)
66{
67 // it's not a hash of entire file content!
68
69 _PwFile* f = pw_this_data(self);
70
71 _pw_hash_uint64(ctx, self->type_id);
72
73 // XXX
74 _pw_call_hash(&f->name, ctx, nullptr);
75 _pw_hash_uint64(ctx, f->fd);
76 _pw_hash_uint64(ctx, f->is_external_fd);
77 return true;
78}
79
80static bool file_dump(PwMethod_Basic_dump* mthis, PwValuePtr self, FILE* fp, int indent, _PwCompoundChain* tail)
81{
82 if (!pw_super(mthis, self, fp, indent, tail)) {
83 return false;
84 }
85
86 _PwFile* f = pw_this_data(self);
87
88 _pw_print_indent(fp, indent + 4);
89 fprintf(fp, "fd: %d", f->fd);
90 if (f->is_external_fd) {
91 char* owned = "";
92 if (f->own_fd) {
93 owned = ", owned";
94 }
95 fprintf(fp, " (external%s)", owned);
96 }
97 fputc('\n', fp);
98
99 _pw_print_indent(fp, indent + 4);
100 if (pw_is_string(&f->name)) {
101 PW_CSTRING(file_name, &f->name);
102 fprintf(fp, "File name: %s", file_name);
103 } else {
104 fprintf(fp, "File name: Null");
105 }
106 fputc('\n', fp);
107 return true;
108}
109
110static PwInterface_Basic file_basic_interface = {
111 .create = { .func = file_create },
112 .destroy = { .func = file_destroy },
113 .hash = { .func = file_hash },
114 .dump = { .func = file_dump }
115};
116
117/****************************************************************
118 * File descriptor interface
119 */
120
121static bool file_get_fd(PwMethod_Fd_get_fd* mthis, PwValuePtr self, int* result)
122{
123 _PwFile* f = pw_this_data(self);
124 *result = f->fd;
125 return true;
126}
127
128static bool file_set_fd(PwMethod_Fd_set_fd* mthis, PwValuePtr self, int fd, bool move)
129{
130 _PwFile* f = pw_this_data(self);
131
132 if (f->fd != -1) {
133 // fd already set
134 pw_set_status(PwStatus(PW_ERROR_FD_ALREADY_SET));
135 return false;
136 }
137 f->fd = fd;
138 f->is_external_fd = true;
139 f->own_fd = move;
140 return true;
141}
142
143static bool file_close(PwMethod_Fd_close* mthis, PwValuePtr self)
144{
145 _PwFile* f = pw_this_data(self);
146 bool ret = true;
147 if (f->fd != -1 && f->own_fd) {
148 int result;
149 do {
150 result = close(f->fd);
151 } while (result == -1 && errno == EINTR);
152 ret = result == 0;
153 if (!ret) {
154 f->error = errno;
155 pw_set_status(PwErrno(errno));
156 return false;
157 }
158 }
159 f->fd = -1;
160 f->error = 0;
161 pw_destroy(&f->name);
162 return ret;
163}
164
165static bool file_set_nonblocking(PwMethod_Fd_set_nonblocking* mthis, PwValuePtr self, bool mode)
166{
167 _PwFile* f = pw_this_data(self);
168
169 if (f->fd == -1) {
170 pw_set_status(PwStatus(PW_ERROR_FILE_CLOSED));
171 return false;
172 }
173 int flags = fcntl(f->fd, F_GETFL, 0);
174 if (mode) {
175 flags |= O_NONBLOCK;
176 } else {
177 flags &= ~O_NONBLOCK;
178 }
179 if (fcntl(f->fd, F_SETFL, flags) == -1) {
180 pw_set_status(PwErrno(errno));
181 return false;
182 }
183 return true;
184}
185
186static PwInterface_Fd file_fd_interface = {
187#define X(name, ...) .name = { .func = file_##name } __VA_OPT__(,)
188 PW_FD_INTERFACE_METHODS
189#undef X
190};
191
192
193/****************************************************************
194 * File interface
195 */
196
197static bool file_open(PwMethod_File_open* mthis, PwValuePtr self, PwValuePtr file_name, int flags, mode_t mode)
198{
199 _PwFile* f = pw_this_data(self);
200
201 if (f->fd != -1) {
202 pw_set_status(PwStatus(PW_ERROR_FILE_ALREADY_OPENED));
203 return false;
204 }
205
206 pw_assert_string(file_name);
207 PW_CSTRING(fname, file_name);
208 do {
209 f->fd = open(fname, flags, mode);
210 } while (f->fd == -1 && errno == EINTR);
211
212 if (f->fd == -1) {
213 f->error = errno;
214 pw_set_status(PwErrno(errno));
215 return false;
216 }
217
218 pw_clone2(&f->name, file_name);
219 f->is_external_fd = false;
220 f->own_fd = true;
221
222 return true;
223}
224
225static bool file_get_name(PwMethod_File_get_name* mthis, PwValuePtr self, PwValuePtr result)
226{
227 _PwFile* f = pw_this_data(self);
228 pw_clone2(result, &f->name);
229 return true;
230}
231
232static bool file_set_name(PwMethod_File_set_name* mthis, PwValuePtr self, PwValuePtr file_name)
233{
234 _PwFile* f = pw_this_data(self);
235
236 if (f->fd != -1 && !f->is_external_fd) {
237 // not an externally set fd
238 pw_set_status(PwStatus(PW_ERROR_CANT_SET_FILENAME));
239 return false;
240 }
241
242 pw_clone2(&f->name, file_name);
243 return true;
244}
245
246static bool file_seek(PwMethod_File_seek* mthis, PwValuePtr self, off_t offset, int whence, off_t* position)
247{
248 _PwFile* f = pw_this_data(self);
249 off_t pos = lseek(f->fd, offset, whence);
250 if (pos == -1) {
251 pw_set_status(PwErrno(errno));
252 return false;
253 }
254 if (position) {
255 *position = pos;
256 }
257 return true;
258}
259
260static bool file_tell(PwMethod_File_tell* mthis, PwValuePtr self, off_t* position)
261{
262 _PwFile* f = pw_this_data(self);
263
264 *position = lseek(f->fd, 0, SEEK_CUR);
265 if (*position == -1) {
266 pw_set_status(PwErrno(errno));
267 return false;
268 }
269 return true;
270}
271
272static PwInterface_File file_interface = {
273#define X(name, ...) .name = { .func = file_##name } __VA_OPT__(,)
274 PW_FILE_INTERFACE_METHODS
275#undef X
276};
277
278
279/****************************************************************
280 * Reader interface
281 */
282
283static bool file_read(PwMethod_Reader_read* mthis, PwValuePtr self, void* buffer, unsigned buffer_size, unsigned* bytes_read)
284{
285 _PwFile* f = pw_this_data(self);
286
287 ssize_t result;
288 do {
289 result = read(f->fd, buffer, buffer_size);
290 } while (result < 0 && errno == EINTR);
291
292 if (result < 0) {
293 *bytes_read = 0;
294 pw_set_status(PwErrno(errno));
295 return false;
296 }
297 *bytes_read = (unsigned) result;
298 return true;
299}
300
301static PwInterface_Reader file_reader_interface = {
302#define X(name, ...) .name = { .func = file_##name } __VA_OPT__(,)
303 PW_READER_INTERFACE_METHODS
304#undef X
305};
306
307
308/****************************************************************
309 * Writer interface
310 */
311
312static bool file_write(PwMethod_Writer_write* mthis, PwValuePtr self, void* data, unsigned size, unsigned* bytes_written)
313{
314 _PwFile* f = pw_this_data(self);
315
316 unsigned written = 0;
317 while (written < size) {
318 ssize_t n;
319 do {
320 n = write(f->fd, ((uint8_t*) data) + written, size - written);
321 } while (n < 0 && errno == EINTR);
322
323 if (n < 0) {
324 if (bytes_written) {
325 *bytes_written = written;
326 }
327 pw_set_status(PwErrno(errno));
328 return false;
329 }
330 written += (unsigned) n;
331 }
332 if (bytes_written) {
333 *bytes_written = written;
334 }
335 return true;
336}
337
338static PwInterface_Writer file_writer_interface = {
339#define X(name, ...) .name = { .func = file_##name } __VA_OPT__(,)
340 PW_WRITER_INTERFACE_METHODS
341#undef X
342};
343
344
345/****************************************************************
346 * Append interface
347 */
348
349static bool file_append_string_data(PwMethod_Append_append_string_data* mthis,
350 PwValuePtr self, uint8_t* start_ptr, uint8_t* end_ptr, uint8_t char_size)
351{
352 if (start_ptr >= end_ptr) {
353 return true;
354 }
355 if (char_size < 2) {
356 // write ASCII and UTF-8 directly to file
357 return do_file_write(self, start_ptr, end_ptr - start_ptr, nullptr);
358 }
359
360 // convert wide chars to UTF-8
361
362 uint8_t buffer[1024];
363 unsigned n = 0;
364
365 while (start_ptr < end_ptr) {
366 char32_t codepoint = _pw_get_char(start_ptr, char_size);
367 n += pw_char32_to_utf8(codepoint, (char*) &buffer[n]);
368 if (n > sizeof(buffer) - 4) {
369 if (!do_file_write(self, buffer, n, nullptr)) {
370 return false;
371 }
372 n = 0;
373 }
374 }
375 if (n) {
376 if (!do_file_write(self, buffer, n, nullptr)) {
377 return false;
378 }
379 }
380 return true;
381}
382
383static bool file_append(PwMethod_Append_append* mthis, PwValuePtr self, PwValuePtr value)
384{
385 if (!pw_validate(value, PwTypeId_String)) {
386 return false;
387 }
388
389 uint8_t* end_ptr;
390 uint8_t* start_ptr = _pw_string_start_end(value, &end_ptr);
391 return pw_this_call(append_string_data, mthis, self, start_ptr, end_ptr, value->char_size);
392}
393
394static PwInterface_Append file_append_interface = {
395#define X(name, ...) .name = { .func = file_##name } __VA_OPT__(,)
396 PW_APPEND_INTERFACE_METHODS
397#undef X
398};
399
400/****************************************************************
401 * Initialization
402 */
403
404[[gnu::constructor]]
405void _pw_init_file()
406{
407 // interface can be already registered
408 // basically. interfaces can be registered by any type in any order
409 if (PwInterfaceId_File) {
410 return;
411 }
412
413 _pw_init_types();
414
415# define X(name, ...) #name __VA_OPT__(,)
416 PwInterfaceId_File = pw_register_interface("File", PW_FILE_INTERFACE_METHODS, nullptr);
417# undef X
418
419 PwTypeId_File = pw_add_type2(
420 "File", _PwFile,
421 PW_PARENTS,
422 PwTypeId_Struct,
423 PW_INTERFACES,
424 PwInterfaceId_Basic, &file_basic_interface,
425 PwInterfaceId_Fd, &file_fd_interface,
426 PwInterfaceId_File, &file_interface,
427 PwInterfaceId_Reader, &file_reader_interface,
428 PwInterfaceId_Writer, &file_writer_interface,
429 PwInterfaceId_Append, &file_append_interface
430 );
431}