Qore Programming Language 1.19.1
Loading...
Searching...
No Matches
qore_qf_private.h
1/* -*- mode: c++; indent-tabs-mode: nil -*- */
2/*
3 qore_qf_private.h
4
5 Qore Programming Language
6
7 Copyright (C) 2003 - 2023 Qore Technologies, s.r.o.
8
9 Permission is hereby granted, free of charge, to any person obtaining a
10 copy of this software and associated documentation files (the "Software"),
11 to deal in the Software without restriction, including without limitation
12 the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 and/or sell copies of the Software, and to permit persons to whom the
14 Software is furnished to do so, subject to the following conditions:
15
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
18
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26
27 Note that the Qore library is released under a choice of three open-source
28 licenses: MIT (as above), LGPL 2+, or GPL 2+; see README-LICENSE for more
29 information.
30*/
31
32#ifndef _QORE_INTERN_QORE_QF_PRIVATE_H
33#define _QORE_INTERN_QORE_QF_PRIVATE_H
34
35#include "qore/intern/QC_Queue.h"
36#ifdef HAVE_TERMIOS_H
37#include "qore/intern/QC_TermIOS.h"
38#endif
39
40#include "qore/intern/StringReaderHelper.h"
41#include "qore/AbstractPollState.h"
42
43#include <cerrno>
44#include <cstdio>
45#include <cstdlib>
46#include <cstring>
47#include <string>
48#include <sys/file.h>
49#include <sys/types.h>
50#include <unistd.h>
51
52#if defined HAVE_POLL
53#include <poll.h>
54#elif defined HAVE_SYS_SELECT_H
55#include <sys/select.h>
56#elif (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
57#define HAVE_SELECT 1
58#else
59#error no async I/O APIs available
60#endif
61
62#ifndef DEFAULT_FILE_BUFSIZE
63#define DEFAULT_FILE_BUFSIZE 16384
64#endif
65
66hashdecl qore_qf_private {
67 int fd = -1;
68 bool is_open = false;
69 bool special_file = false;
70 const QoreEncoding* charset;
71 std::string filename;
72 mutable QoreThreadLock m;
73 Queue* event_queue = nullptr;
75 QoreValue event_arg;
77 bool event_data = false;
78 bool in_non_block = false;
79
80 DLLLOCAL qore_qf_private(const QoreEncoding* cs) : charset(cs) {
81 }
82
83 DLLLOCAL ~qore_qf_private() {
84 close_intern();
85
86 // must be dereferenced and removed before deleting
87 assert(!event_queue);
88 }
89
90 DLLLOCAL int close_intern(bool detach = false) {
91 filename.clear();
92
93 int rc;
94 if (is_open) {
95 if (special_file) {
96 rc = -1;
97 } else if (!detach) {
98 rc = ::close(fd);
99 is_open = false;
100 do_close_event_unlocked();
101 } else {
102 rc = 0;
103 }
104 } else {
105 rc = 0;
106 }
107 return rc;
108 }
109
110 DLLLOCAL int redirect(qore_qf_private& file, ExceptionSink* xsink) {
111 if (&file == this)
112 return 0;
113
114 // lock both files
115 AutoLocker al(m);
116 if (checkNonBlock(xsink)) {
117 return -1;
118 }
119
120 AutoLocker al2(file.m);
121 if (file.checkNonBlock(xsink)) {
122 return -1;
123 }
124
125 // dup2() will close this file descriptor
126 int rc = dup2(file.fd, fd);
127 if (rc == -1) {
128 xsink->raiseErrnoException("FILE-REDIRECT-ERROR", errno, "error in dup2()");
129 return -1;
130 }
131 filename = file.filename;
132
133 return 0;
134 }
135
136 DLLLOCAL int open_intern(const char* fn, int flags, int mode, const QoreEncoding* cs) {
137 close_intern();
138
139 if (!flags)
140 flags = O_RDONLY;
141
142#ifdef _Q_WINDOWS
143 // open files in binary mode by default on Windows
144 if (!(flags & O_TEXT))
145 flags |= O_BINARY;
146#endif
147
148 do_open_event_unlocked(fn, flags, mode, cs);
149
150 fd = ::open(fn, flags, mode);
151 if (fd < 0)
152 return fd;
153
154 do_opened_event_unlocked(fn, flags, mode, cs);
155
156 filename = fn;
157 if (cs)
158 charset = cs;
159 is_open = true;
160 return 0;
161 }
162
163 DLLLOCAL int open(const char* fn, int flags, int mode, const QoreEncoding* cs) {
164 if (!fn || special_file)
165 return -1;
166
167 AutoLocker al(m);
168 return open_intern(fn, flags, mode, cs);
169 }
170
171 // returns -1 for exception
172 DLLLOCAL int checkReadOpen(ExceptionSink* xsink) const {
173 if (is_open) {
174 return 0;
175 }
176
177 xsink->raiseException("FILE-READ-ERROR", "file has not been opened");
178 return -1;
179 }
180
181 // returns -1 for exception
182 DLLLOCAL int checkWriteOpen(ExceptionSink* xsink) const {
183 if (is_open) {
184 return 0;
185 }
186
187 xsink->raiseException("FILE-WRITE-ERROR", "file has not been opened");
188 return -1;
189 }
190
191 // returns -1 for exception
192 DLLLOCAL int checkOpen(ExceptionSink* xsink) const {
193 if (is_open) {
194 return 0;
195 }
196
197 xsink->raiseException("FILE-OPERATION-ERROR", "file has not been opened");
198 return -1;
199 }
200
201 DLLLOCAL bool isOpen() const {
202 return is_open;
203 }
204
205 DLLLOCAL bool isDataAvailable(int timeout_ms, ExceptionSink *xsink) const {
206 AutoLocker al(m);
207 if (checkNonBlock(xsink)) {
208 return false;
209 }
210
211 if (checkReadOpen(xsink))
212 return false;
213
214 return isDataAvailableIntern(timeout_ms, "isDataAvailable", xsink);
215 }
216
217 // fd must be open or -1 is returned and a Qore-language exception is raised
218 /* return values:
219 -1: error
220 0: timeout
221 > 0: I/O can continue
222 */
223 DLLLOCAL int select(int timeout_ms, bool read, const char* mname, ExceptionSink* xsink) const {
224 //printd(5, "select() to: %d read: %d mname: '%s'\n", timeout_ms, read, mname);
225 if (checkOpen(xsink))
226 return -1;
227
228#if defined HAVE_POLL
229 return poll_intern(timeout_ms, read, mname, xsink);
230#elif defined HAVE_SELECT
231 return select_intern(timeout_ms, read, mname, xsink);
232#else
233#error no async I/O operations supported
234#endif
235 }
236
237#if defined HAVE_POLL
238 DLLLOCAL int poll_intern(int timeout_ms, bool read, const char* mname, ExceptionSink* xsink) const {
239 int rc;
240 pollfd fds = {fd, (short)(read ? POLLIN : POLLOUT), 0};
241 while (true) {
242 rc = poll(&fds, 1, timeout_ms);
243 if (rc == -1 && errno == EINTR)
244 continue;
245 break;
246 }
247 if (rc < 0)
248 xsink->raiseException("FILE-SELECT-ERROR", "poll(2) returned an error in call to File::%s()", mname);
249 else if (!rc && ((fds.revents & POLLHUP) || (fds.revents & (POLLERR|POLLNVAL))))
250 rc = -1;
251
252 return rc;
253 }
254#elif defined HAVE_SELECT
255 DLLLOCAL int select_intern(int timeout_ms, bool read, const char* mname, ExceptionSink* xsink) const {
256#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
257 // async I/O ignored on files on windows
258 return 1;
259#else
260 // select is inherently broken since it can only handle descriptors < FD_SETSIZE, which is 1024 on Linux for example
261 if (fd >= FD_SETSIZE) {
262 if (xsink)
263 xsink->raiseException("FILE-SELECT-ERROR", "fd is %d in call to File::%s() which is >= %d; contact "
264 "the Qore developers to implement an alternative to select() on this platform", fd, mname,
265 FD_SETSIZE);
266 return -1;
267 }
268 hashdecl timeval tv;
269 int rc;
270 while (true) {
271 // to be safe, we set the file descriptor arg after each EINTR (required on Linux for example)
272 fd_set sfs;
273
274 FD_ZERO(&sfs);
275 FD_SET(fd, &sfs);
276
277 tv.tv_sec = timeout_ms / 1000;
278 tv.tv_usec = (timeout_ms % 1000) * 1000;
279
280 rc = read ? ::select(fd + 1, &sfs, 0, 0, &tv) : ::select(fd + 1, 0, &sfs, 0, &tv);
281 if (rc >= 0 || errno != EINTR)
282 break;
283 }
284 if (rc == -1) {
285 rc = 0;
286 xsink->raiseException("FILE-SELECT-ERROR", "select(2) returned an error in call to File::%s()", mname);
287 }
288
289 return rc;
290#endif
291 }
292#endif
293
294 // assumes lock is held and file is open
295 DLLLOCAL bool isDataAvailableIntern(int timeout_ms, const char* mname, ExceptionSink *xsink) const {
296 return select(timeout_ms, true, mname, xsink);
297 }
298
299#ifdef HAVE_TERMIOS_H
300 DLLLOCAL int setTerminalAttributes(int action, QoreTermIOS* ios, ExceptionSink* xsink) const {
301 AutoLocker al(m);
302 if (checkNonBlock(xsink)) {
303 return -1;
304 }
305
306 if (checkOpen(xsink))
307 return -1;
308
309 return ios->set(fd, action, xsink);
310 }
311
312 DLLLOCAL int getTerminalAttributes(QoreTermIOS* ios, ExceptionSink* xsink) const {
313 AutoLocker al(m);
314 if (checkNonBlock(xsink)) {
315 return -1;
316 }
317
318 if (checkOpen(xsink))
319 return -1;
320
321 return ios->get(fd, xsink);
322 }
323#endif
324
325 // unlocked, assumes file is open
326 DLLLOCAL ssize_t readCheck(ExceptionSink* xsink, void* buf, size_t bs) const {
327 if (checkNonBlock(xsink)) {
328 return 0;
329 }
330
331 if (checkReadOpen(xsink)) {
332 return 0;
333 }
334
335 return read(buf, bs);
336 }
337
338 // unlocked, assumes file is open
339 DLLLOCAL ssize_t read(void* buf, size_t bs) const {
340 // must be called with the lock held
341 assert(m.trylock());
342
343 ssize_t rc;
344 while (true) {
345 rc = ::read(fd, buf, bs);
346 // try again if we were interrupted by a signal
347 if (rc >= 0 || errno != EINTR)
348 break;
349 }
350
351 if (rc > 0)
352 do_read_event_unlocked(rc, rc, bs);
353
354 return rc;
355 }
356
357 // unlocked, assumes file is open
358 DLLLOCAL ssize_t writeCheck(const void* buf, size_t len, ExceptionSink* xsink) const {
359 if (checkNonBlock(xsink)) {
360 return 0;
361 }
362
363 if (checkWriteOpen(xsink)) {
364 return 0;
365 }
366
367 return write(buf, len, xsink);
368 }
369
370 // unlocked, assumes file is open
371 DLLLOCAL ssize_t write(const void* buf, size_t len, ExceptionSink* xsink = 0) const {
372 // must be called with the lock held
373 assert(m.trylock());
374
375 ssize_t rc;
376 while (true) {
377 rc = ::write(fd, buf, len);
378 // try again if we are interrupted by a signal
379 if (rc >= 0 || errno != EINTR)
380 break;
381 }
382
383 if (rc > 0) {
384 do_write_event_unlocked(rc, rc, len);
385 } else if (xsink && rc < 0) {
386 xsink->raiseErrnoException("FILE-WRITE-ERROR", errno, "failed writing " QSD " byte%s to File", len,
387 len == 1 ? "" : "s");
388 }
389
390 return rc;
391 }
392
393 // private function, unlocked
394 DLLLOCAL int readChar() const {
395 unsigned char ch = 0;
396 if (read(&ch, 1) != 1)
397 return -1;
398 return (int)ch;
399 }
400
401 // private function, unlocked
402 DLLLOCAL int readUnicode(int* n_len = 0) const;
403
404 DLLLOCAL qore_offset_t readData(void* dest, size_t limit, int timeout_ms, const char* mname,
405 ExceptionSink* xsink) {
406 // wait for data
407 if (timeout_ms >= 0 && !isDataAvailableIntern(timeout_ms, mname, xsink)) {
408 if (!*xsink)
409 xsink->raiseException("FILE-READ-TIMEOUT", "timeout limit exceeded (%d ms) reading file block in "
410 "ReadOnlyFile::%s()", timeout_ms, mname);
411 return -1;
412 }
413
414 qore_offset_t rc;
415 while (true) {
416 rc = ::read(fd, dest, limit);
417 //printd(5, "qore_qf_private::readData(%p, %ld, %d, '%s') fd: %d rc: %d\n", dest, limit, timeout_ms,
418 // mname, fd, rc);
419 // try again if we were interrupted by a signal
420 if (rc >= 0) {
421 break;
422 }
423 if (errno != EINTR) {
424 xsink->raiseErrnoException("FILE-READ-ERROR", errno, "error reading file in ReadOnlyFile::%s()",
425 mname);
426 return -1;
427 }
428 }
429
430 return rc;
431 }
432
433 DLLLOCAL QoreStringNode* readString(qore_offset_t size, int timeout_ms, const char* mname, ExceptionSink* xsink) {
434 return q_read_string(xsink, size, charset, std::bind(&qore_qf_private::readData, this, _1, _2, timeout_ms,
435 mname, _3));
436 }
437
438 DLLLOCAL char* readBlock(qore_offset_t &size, int timeout_ms, const char* mname, ExceptionSink* xsink) {
439 size_t bs = size > 0 && size < DEFAULT_FILE_BUFSIZE ? size : DEFAULT_FILE_BUFSIZE;
440 size_t br = 0;
441 char* buf = (char* )malloc(sizeof(char) * bs);
442 char* bbuf = 0;
443
444 while (true) {
445 // wait for data
446 if (timeout_ms >= 0 && !isDataAvailableIntern(timeout_ms, mname, xsink)) {
447 if (!*xsink)
448 xsink->raiseException("FILE-READ-TIMEOUT", "timeout limit exceeded (%d ms) reading file block in "
449 "ReadOnlyFile::%s()", timeout_ms, mname);
450 br = 0;
451 break;
452 }
453
454 qore_offset_t rc;
455 while (true) {
456 rc = ::read(fd, buf, bs);
457 // try again if we were interrupted by a signal
458 if (rc >= 0)
459 break;
460 if (errno != EINTR) {
461 xsink->raiseErrnoException("FILE-READ-ERROR", errno, "error reading file after " QSD " bytes "
462 "read in File::%s()", br, mname);
463 break;
464 }
465 }
466 //printd(5, "readBlock(fd: %d, buf: %p, bs: %d) rc: %d\n", fd, buf, bs, rc);
467 if (rc <= 0)
468 break;
469
470 // enlarge bbuf (ensure buffer is 1 byte bigger than needed)
471 bbuf = (char* )realloc(bbuf, br + rc + 1);
472 // append buffer to bbuf
473 memcpy(bbuf + br, buf, rc);
474 br += rc;
475
476 do_read_event_unlocked(rc, br, size);
477
478 if (size > 0) {
479 if (size - br < bs)
480 bs = size - br;
481 if (br >= (size_t)size)
482 break;
483 }
484 }
485 free(buf);
486 if (*xsink) {
487 if (bbuf)
488 free(bbuf);
489 return nullptr;
490 }
491 size = br;
492 return bbuf;
493 }
494
495 DLLLOCAL QoreStringNode* readLine(bool incl_eol, ExceptionSink* xsink) {
496 QoreStringNodeHolder str(new QoreStringNode(charset));
497
498 int rc = readLine(**str, incl_eol);
499
500 if (rc == -2) {
501 xsink->raiseException("FILE-READLINE-ERROR", "file has not been opened");
502 return 0;
503 }
504
505 return rc == -1 ? 0 : str.release();
506 }
507
508 DLLLOCAL int readLine(QoreString& str, bool incl_eol = true) {
509 str.clear();
510
511 AutoLocker al(m);
512 assert(!in_non_block);
513
514 if (!is_open)
515 return -2;
516
517 bool tty = (bool)isatty(fd);
518
519 int ch, rc = -1;
520
521 while ((ch = readChar()) >= 0) {
522 str.concat((char)ch);
523 if (rc == -1)
524 rc = 0;
525
526 if (ch == '\r') {
527 // see if next byte is \n' if we're not connected to a terminal device
528 if (!tty) {
529 ch = readChar();
530 if (ch >= 0) {
531 if (ch == '\n') {
532 if (incl_eol)
533 str.concat((char)ch);
534 } else {
535 // reset file to previous byte position
536 lseek(fd, -1, SEEK_CUR);
537 }
538 }
539 }
540 if (!incl_eol)
541 str.terminate(str.strlen() - 1);
542 break;
543 }
544
545 if (ch == '\n') {
546 if (!incl_eol)
547 str.terminate(str.strlen() - 1);
548 break;
549 }
550 }
551
552 return rc;
553 }
554
555 DLLLOCAL int readUntil(char byte, QoreString& str, bool incl_byte = true) {
556 str.clear();
557
558 AutoLocker al(m);
559 assert(!in_non_block);
560
561 if (!is_open)
562 return -2;
563
564 int ch, rc = -1;
565
566 while ((ch = readChar()) >= 0) {
567 char c = ch;
568 str.concat(c);
569 if (rc == -1)
570 rc = 0;
571 if (c == byte) {
572 if (!incl_byte)
573 str.terminate(str.strlen() - 1);
574 break;
575 }
576 }
577
578 return rc;
579 }
580
581 DLLLOCAL QoreStringNode* readUntil(const char* bytes, bool incl_bytes, ExceptionSink* xsink) {
582 QoreStringNodeHolder str(new QoreStringNode(charset));
583
584 int rc = readUntil(bytes, **str, incl_bytes);
585
586 if (rc == -2) {
587 xsink->raiseException("FILE-READLINE-ERROR", "file has not been opened");
588 return 0;
589 }
590
591 return rc == -1 ? 0 : str.release();
592 }
593
594 DLLLOCAL int readLineUnicode(QoreString& str, bool incl_eol = true) {
595 str.clear();
596
597 AutoLocker al(m);
598 assert(!in_non_block);
599
600 if (!is_open)
601 return -2;
602
603 bool tty = (bool)isatty(fd);
604
605 int ch, rc = -1;
606
607 while ((ch = readUnicode()) >= 0) {
608 // skip BOM
609 if (ch == 0xfeff)
610 continue;
611 else if (ch == 0xfffe && charset == QCS_UTF16 && str.empty()) {
612 charset = QCS_UTF16LE;
613 continue;
614 }
615
616 str.concatUnicode(ch);
617
618 if (rc == -1)
619 rc = 0;
620
621 char c = str[str.size() - 1];
622
623 if (c == '\r') {
624 // see if next byte is \n' if we're not connected to a terminal device
625 if (!tty) {
626 int len = 0;
627 ch = readUnicode(&len);
628 if (ch >= 0) {
629 if (ch == '\n') {
630 if (incl_eol)
631 str.concatUnicode(ch);
632 }
633 else {
634 // reset file to previous byte position
635 lseek(fd, -len, SEEK_CUR);
636 }
637 }
638 }
639 if (!incl_eol)
640 str.terminate(str.strlen() - 1);
641 break;
642 }
643
644 if (ch == '\n') {
645 if (!incl_eol)
646 str.terminate(str.strlen() - 1);
647 break;
648 }
649 }
650
651 return rc;
652 }
653
654 DLLLOCAL int readUntilUnicode(char byte, QoreString& str, bool incl_byte = true) {
655 str.clear();
656
657 AutoLocker al(m);
658 assert(!in_non_block);
659
660 if (!is_open)
661 return -2;
662
663 int ch, rc = -1;
664
665 while ((ch = readUnicode()) >= 0) {
666 // skip BOM
667 if (ch == 0xfeff)
668 continue;
669 else if (ch == 0xfffe && charset == QCS_UTF16 && str.empty()) {
670 charset = QCS_UTF16LE;
671 continue;
672 }
673
674 str.concatUnicode(ch);
675
676 if (rc == -1)
677 rc = 0;
678 if (ch == byte) {
679 if (!incl_byte)
680 str.terminate(str.strlen() - 1);
681 break;
682 }
683 }
684
685 return rc;
686 }
687 DLLLOCAL int readUntilUnicode(const char* bytes, QoreString& str, bool incl_bytes) {
688 str.clear();
689
690 AutoLocker al(m);
691 assert(!in_non_block);
692
693 if (!is_open)
694 return -2;
695
696 // offset in bytes
697 unsigned pos = 0;
698
699 int ch, rc = -1;
700
701 while ((ch = readUnicode()) >= 0) {
702 // skip BOM
703 if (ch == 0xfeff)
704 continue;
705 else if (ch == 0xfffe && charset == QCS_UTF16 && str.empty()) {
706 charset = QCS_UTF16LE;
707 continue;
708 }
709
710 str.concatUnicode(ch);
711
712 if (rc == -1)
713 rc = 0;
714
715 if (ch == bytes[pos]) {
716 ++pos;
717 if (!bytes[pos]) {
718 if (!incl_bytes)
719 str.terminate(str.strlen() - pos);
720 break;
721 }
722 } else if (pos) {
723 // bytes=aaac read=aaaac str=aaa pos=3
724 // ^ ^
725 // restart search with characters already added to the string if more than 1 character was matched previously
726 if (pos > 1) {
727 unsigned ps = 1;
728 while (ps < pos) {
729 if (!strncmp(str.getBuffer() + ps, bytes, pos - ps)) {
730 pos -= ps;
731 break;
732 }
733 ++ps;
734 }
735 if (pos == ps)
736 pos = 0;
737 } else {
738 // restart search if failed
739 pos = 0;
740 }
741 }
742 }
743
744 return rc;
745 }
746
747 // not the most efficient search algorithm, restarts the search the byte after it fails for multi-byte patterns
748 DLLLOCAL int readUntil(const char* bytes, QoreString& str, bool incl_bytes) {
749 if (!bytes[1])
750 return readUntil(bytes[0], str, incl_bytes);
751
752 str.clear();
753
754 AutoLocker al(m);
755 assert(!in_non_block);
756
757 if (!is_open)
758 return -2;
759
760 // offset in bytes
761 unsigned pos = 0;
762
763 int ch, rc = -1;
764
765 while ((ch = readChar()) >= 0) {
766 char c = ch;
767 str.concat(c);
768 if (rc == -1)
769 rc = 0;
770
771 if (c == bytes[pos]) {
772 ++pos;
773 if (!bytes[pos]) {
774 if (!incl_bytes)
775 str.terminate(str.strlen() - pos);
776 break;
777 }
778 } else if (pos) {
779 // bytes=aaac read=aaaac str=aaa pos=3
780 // ^ ^
781 // restart search with characters already added to the string if more than 1 character was matched previously
782 if (pos > 1) {
783 unsigned ps = 1;
784 while (ps < pos) {
785 if (!strncmp(str.getBuffer() + ps, bytes, pos - ps)) {
786 pos -= ps;
787 break;
788 }
789 ++ps;
790 }
791 if (pos == ps)
792 pos = 0;
793 } else {
794 // restart search if failed
795 pos = 0;
796 }
797 }
798 }
799
800 return rc;
801 }
802
803 DLLLOCAL bool isTty() const {
804 AutoLocker al(m);
805 assert(!in_non_block);
806
807 if (!is_open)
808 return false;
809
810 return (bool)isatty(fd);
811 }
812
813 DLLLOCAL int detachFd() {
814 AutoLocker al(m);
815 assert(!in_non_block);
816
817 if (!is_open) {
818 return -1;
819 }
820
821 int rc = fd;
822 // special files (stdout/stderr/stdin) are not closed anyway, so we don't mark the object closed in this case
823 if (!special_file) {
824 close_intern(true);
825 }
826 return rc;
827 }
828
829 DLLLOCAL size_t getPos() const {
830 AutoLocker al(m);
831 assert(!in_non_block);
832
833 if (!is_open)
834 return -1;
835
836 return lseek(fd, 0, SEEK_CUR);
837 }
838
839 DLLLOCAL QoreHashNode* getEvent(int event, int source = QORE_SOURCE_FILE) const {
840 QoreHashNode* h = new QoreHashNode(autoTypeInfo);
841 if (event_arg) {
842 h->setKeyValue("arg", event_arg.refSelf(), nullptr);
843 }
844
845 h->setKeyValue("event", event, nullptr);
846 h->setKeyValue("source", source, nullptr);
847 h->setKeyValue("id", (int64)this, nullptr);
848
849 return h;
850 }
851
852 DLLLOCAL void setEventQueue(ExceptionSink* xsink, Queue* q, QoreValue arg, bool with_data) {
853 AutoLocker al(m);
854 if (event_queue) {
855 if (event_arg) {
856 event_arg.discard(xsink);
857 }
858 event_queue->deref(xsink);
859 }
860 event_queue = q;
861 event_arg = arg;
862 event_data = with_data;
863 }
864
865 DLLLOCAL void cleanup(ExceptionSink* xsink) {
866 AutoLocker al(m);
867 if (event_queue) {
868 // close the file before the delete message is put on the queue
869 // the file would be closed anyway in the destructor
870 close_intern();
871
872 event_queue->pushAndTakeRef(getEvent(QORE_EVENT_DELETED));
873
874 // deref and remove event queue and arg
875 if (event_arg) {
876 event_arg.discard(xsink);
877 event_arg.clear();
878 }
879 event_queue->deref(xsink);
880 event_queue = nullptr;
881 }
882 }
883
884 DLLLOCAL void do_open_event_unlocked(const char* fn, int flags, int mode, const QoreEncoding* enc) const {
885 if (event_queue) {
886 QoreHashNode* h = getEvent(QORE_EVENT_OPEN_FILE);
887 h->setKeyValue("filename", new QoreStringNode(fn), nullptr);
888 h->setKeyValue("flags", flags, nullptr);
889 h->setKeyValue("mode", mode, nullptr);
890 h->setKeyValue("encoding", new QoreStringNode(enc->getCode()), nullptr);
891 event_queue->pushAndTakeRef(h);
892 }
893 }
894
895 DLLLOCAL void do_opened_event_unlocked(const char* fn, int flags, int mode, const QoreEncoding* enc) const {
896 if (event_queue) {
897 QoreHashNode* h = getEvent(QORE_EVENT_FILE_OPENED);
898 h->setKeyValue("source", QORE_SOURCE_FILE, nullptr);
899 h->setKeyValue("id", (int64)this, nullptr);
900 h->setKeyValue("filename", new QoreStringNode(fn), nullptr);
901 h->setKeyValue("flags", flags, nullptr);
902 h->setKeyValue("mode", mode, nullptr);
903 h->setKeyValue("encoding", new QoreStringNode(enc->getCode()), nullptr);
904 event_queue->pushAndTakeRef(h);
905 }
906 }
907
908 DLLLOCAL void do_close_event_unlocked() const {
909 if (event_queue) {
910 event_queue->pushAndTakeRef(getEvent(QORE_EVENT_CHANNEL_CLOSED));
911 }
912 }
913
914 DLLLOCAL void do_read_event_unlocked(int bytes_read, int total_read, int bufsize) const {
915 // post bytes read on event queue, if any
916 if (event_queue) {
917 QoreHashNode* h = getEvent(QORE_EVENT_DATA_READ);
918 h->setKeyValue("read", bytes_read, nullptr);
919 h->setKeyValue("total_read", total_read, nullptr);
920 h->setKeyValue("total_to_read", bufsize, nullptr);
921 event_queue->pushAndTakeRef(h);
922 }
923 }
924
925 DLLLOCAL void do_write_event_unlocked(int bytes_written, int total_written, int bufsize) const {
926 // post bytes sent on event queue, if any
927 if (event_queue) {
928 QoreHashNode* h = getEvent(QORE_EVENT_DATA_WRITTEN);
929 h->setKeyValue("written", bytes_written, nullptr);
930 h->setKeyValue("total_written", total_written, nullptr);
931 h->setKeyValue("total_to_write", bufsize, nullptr);
932 event_queue->pushAndTakeRef(h);
933 }
934 }
935
936 DLLLOCAL QoreListNode* stat(ExceptionSink* xsink) const {
937 AutoLocker al(m);
938 if (checkNonBlock(xsink)) {
939 return nullptr;
940 }
941
942 if (checkReadOpen(xsink))
943 return nullptr;
944
945 hashdecl stat sbuf;
946 if (fstat(fd, &sbuf)) {
947 xsink->raiseErrnoException("FILE-STAT-ERROR", errno, "fstat() call failed");
948 return nullptr;
949 }
950
951 return stat_to_list(sbuf);
952 }
953
954 DLLLOCAL QoreHashNode* hstat(ExceptionSink* xsink) const {
955 AutoLocker al(m);
956 if (checkNonBlock(xsink)) {
957 return nullptr;
958 }
959
960 if (checkReadOpen(xsink))
961 return nullptr;
962
963 hashdecl stat sbuf;
964 if (fstat(fd, &sbuf)) {
965 xsink->raiseErrnoException("FILE-HSTAT-ERROR", errno, "fstat() call failed");
966 return nullptr;
967 }
968
969 return stat_to_hash(sbuf);
970 }
971
972#ifdef Q_HAVE_STATVFS
973 DLLLOCAL QoreHashNode* statvfs(ExceptionSink* xsink) const {
974 AutoLocker al(m);
975 if (checkNonBlock(xsink)) {
976 return nullptr;
977 }
978
979 if (checkReadOpen(xsink))
980 return nullptr;
981
982 hashdecl statvfs vfs;
983#ifdef HAVE_SYS_STATVFS_H
984 if (fstatvfs(fd, &vfs)) {
985 xsink->raiseErrnoException("FILE-STATVFS-ERROR", errno, "fstatvfs() call failed");
986 return nullptr;
987 }
988#else
989 if (q_fstatvfs(filename.c_str(), &vfs)) {
990 xsink->raiseErrnoException("FILE-STATVFS-ERROR", errno, "fstatvfs() call failed");
991 return nullptr;
992 }
993#endif
994
995 return statvfs_to_hash(vfs);
996 }
997#endif
998
999 DLLLOCAL AbstractPollState* startRead(ExceptionSink* xsink, ssize_t bytes);
1000
1002 DLLLOCAL int setNonBlockingIo(bool non_blocking, ExceptionSink* xsink);
1003
1005 DLLLOCAL int checkNonBlock(ExceptionSink* xsink) const {
1006 // must be called with the lock held
1007 assert(m.trylock());
1008
1009 if (in_non_block) {
1010 xsink->raiseException("FILE-NON-BLOCK-ERROR", "a non-blocking operation is currently in progress");
1011 return -1;
1012 }
1013
1014 return 0;
1015 }
1016
1018 DLLLOCAL int setNonBlock(ExceptionSink* xsink, bool do_io = true) {
1019 // must be called with the lock held
1020 assert(m.trylock());
1021
1022 if (!checkNonBlock(xsink) && (!do_io || !setNonBlockingIo(true, xsink))) {
1023 in_non_block = true;
1024 return 0;
1025 }
1026 return -1;
1027 }
1028
1030 DLLLOCAL void clearNonBlock(ExceptionSink* xsink) {
1031 // must be called with the lock held
1032 assert(m.trylock());
1033
1034 if (in_non_block) {
1035 setNonBlockingIo(false, xsink);
1036 in_non_block = false;
1037 }
1038 }
1039
1040 DLLLOCAL QoreObject* startPollRead(ExceptionSink* xsink, QoreObject* self, const char* path, int64 to_read);
1041};
1042
1043#endif
DLLEXPORT const QoreEncoding * QCS_UTF16
UTF-16 (only UTF-* are multi-byte encodings)
Definition: QoreEncoding.h:248
DLLEXPORT const QoreEncoding * QCS_UTF16LE
UTF-16LE (only UTF-* are multi-byte encodings)
Definition: QoreEncoding.h:250
provides a safe and exception-safe way to hold locks in Qore, only to be used on the stack,...
Definition: QoreThreadLock.h:136
container for holding Qore-language exception information and also for registering a "thread_exit" ca...
Definition: ExceptionSink.h:50
DLLEXPORT AbstractQoreNode * raiseException(const char *err, const char *fmt,...)
appends a Qore-language exception to the list
DLLEXPORT AbstractQoreNode * raiseErrnoException(const char *err, int en, const char *fmt,...)
appends a Qore-language exception to the list and appends the result of strerror(errno) to the descri...
defines string encoding functions in Qore
Definition: QoreEncoding.h:83
DLLEXPORT const char * getCode() const
returns the string code (ex: "UTF-8") for the encoding
This is the hash or associative list container type in Qore, dynamically allocated only,...
Definition: QoreHashNode.h:50
DLLEXPORT int setKeyValue(const char *key, QoreValue value, ExceptionSink *xsink)
sets the value of "key" to "value"
This is the list container type in Qore, dynamically allocated only, reference counted.
Definition: QoreListNode.h:52
the implementation of Qore's object data type, reference counted, dynamically-allocated only
Definition: QoreObject.h:60
DLLEXPORT void clear()
unconditionally set the QoreValue to QoreNothingNode (does not dereference any possible contained Abs...
Qore's string type supported by the QoreEncoding class.
Definition: QoreString.h:93
DLLEXPORT size_t strlen() const
returns number of bytes in the string (not including the null pointer)
DLLEXPORT void clear()
reset string to zero length; memory is not deallocated; string encoding does not change
DLLEXPORT void terminate(size_t size)
terminates the string at byte position "size", the string is reallocated if necessary
DLLEXPORT void concat(const QoreString *str, ExceptionSink *xsink)
concatenates a string and converts encodings if necessary
DLLEXPORT const char * getBuffer() const
returns the string's buffer; this data should not be changed
DLLEXPORT size_t size() const
returns number of bytes in the string (not including the null pointer)
DLLEXPORT bool empty() const
returns true if the string is empty, false if not
DLLEXPORT int concatUnicode(unsigned code, ExceptionSink *xsink)
append a character sequence from a unicode code point (returns 0 for OK, -1 for exception)
Qore's string value type, reference counted, dynamically-allocated only.
Definition: QoreStringNode.h:50
provides a mutually-exclusive thread lock
Definition: QoreThreadLock.h:49
DLLLOCAL int trylock()
attempts to acquire the mutex and returns the status immediately; does not block
Definition: QoreThreadLock.h:101
intptr_t qore_offset_t
used for offsets that could be negative
Definition: common.h:76
long long int64
64bit integer type, cannot use int64_t here since it breaks the API on some 64-bit systems due to equ...
Definition: common.h:260
The main value class in Qore, designed to be passed by value.
Definition: QoreValue.h:276
DLLEXPORT void discard(ExceptionSink *xsink)
dereferences any contained AbstractQoreNode pointer and sets to 0; does not modify other values
DLLEXPORT QoreValue refSelf() const
references the contained value if type == QV_Node, returns itself