Qore Programming Language  1.12.0
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 - 2022 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 
42 #include <cerrno>
43 #include <cstdio>
44 #include <cstdlib>
45 #include <cstring>
46 #include <string>
47 #include <sys/file.h>
48 #include <sys/types.h>
49 #include <unistd.h>
50 
51 #if defined HAVE_POLL
52 #include <poll.h>
53 #elif defined HAVE_SYS_SELECT_H
54 #include <sys/select.h>
55 #elif (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
56 #define HAVE_SELECT 1
57 #else
58 #error no async I/O APIs available
59 #endif
60 
61 #ifndef DEFAULT_FILE_BUFSIZE
62 #define DEFAULT_FILE_BUFSIZE 16384
63 #endif
64 
65 hashdecl qore_qf_private {
66  int fd = -1;
67  bool is_open = false;
68  bool special_file = false;
69  const QoreEncoding* charset;
70  std::string filename;
71  mutable QoreThreadLock m;
72  Queue* event_queue = nullptr;
74  QoreValue event_arg;
76  bool event_data = false;
77 
78  DLLLOCAL qore_qf_private(const QoreEncoding* cs) : charset(cs) {
79  }
80 
81  DLLLOCAL ~qore_qf_private() {
82  close_intern();
83 
84  // must be dereferenced and removed before deleting
85  assert(!event_queue);
86  }
87 
88  DLLLOCAL int close_intern(bool detach = false) {
89  filename.clear();
90 
91  int rc;
92  if (is_open) {
93  if (special_file) {
94  rc = -1;
95  } else if (!detach) {
96  rc = ::close(fd);
97  is_open = false;
98  do_close_event_unlocked();
99  } else {
100  rc = 0;
101  }
102  } else {
103  rc = 0;
104  }
105  return rc;
106  }
107 
108  DLLLOCAL int redirect(qore_qf_private& file, ExceptionSink* xsink) {
109  if (&file == this)
110  return 0;
111 
112  // lock both files
113  AutoLocker al(m);
114  AutoLocker al2(file.m);
115 
116  // dup2() will close this file descriptor
117  int rc = dup2(file.fd, fd);
118  if (rc == -1) {
119  xsink->raiseErrnoException("FILE-REDIRECT-ERROR", errno, "error in dup2()");
120  return -1;
121  }
122  filename = file.filename;
123 
124  return 0;
125  }
126 
127  DLLLOCAL int open_intern(const char* fn, int flags, int mode, const QoreEncoding* cs) {
128  close_intern();
129 
130  if (!flags)
131  flags = O_RDONLY;
132 
133 #ifdef _Q_WINDOWS
134  // open files in binary mode by default on Windows
135  if (!(flags & O_TEXT))
136  flags |= O_BINARY;
137 #endif
138 
139  do_open_event_unlocked(fn, flags, mode, cs);
140 
141  fd = ::open(fn, flags, mode);
142  if (fd < 0)
143  return fd;
144 
145  do_opened_event_unlocked(fn, flags, mode, cs);
146 
147  filename = fn;
148  if (cs)
149  charset = cs;
150  is_open = true;
151  return 0;
152  }
153 
154  DLLLOCAL int open(const char* fn, int flags, int mode, const QoreEncoding* cs) {
155  if (!fn || special_file)
156  return -1;
157 
158  AutoLocker al(m);
159  return open_intern(fn, flags, mode, cs);
160  }
161 
162  // returns -1 for exception
163  DLLLOCAL int check_read_open(ExceptionSink* xsink) const {
164  if (is_open)
165  return 0;
166 
167  xsink->raiseException("FILE-READ-ERROR", "file has not been opened");
168  return -1;
169  }
170 
171  // returns -1 for exception
172  DLLLOCAL int check_write_open(ExceptionSink* xsink) const {
173  if (is_open)
174  return 0;
175 
176  xsink->raiseException("FILE-WRITE-ERROR", "file has not been opened");
177  return -1;
178  }
179 
180  // returns -1 for exception
181  DLLLOCAL int check_open(ExceptionSink* xsink) const {
182  if (is_open)
183  return 0;
184 
185  xsink->raiseException("FILE-OPERATION-ERROR", "file has not been opened");
186  return -1;
187  }
188 
189  DLLLOCAL bool isOpen() const {
190  return is_open;
191  }
192 
193  DLLLOCAL bool isDataAvailable(int timeout_ms, ExceptionSink *xsink) const {
194  AutoLocker al(m);
195 
196  if (check_read_open(xsink))
197  return false;
198 
199  return isDataAvailableIntern(timeout_ms, "isDataAvailable", xsink);
200  }
201 
202  // fd must be open or -1 is returned and a Qore-language exception is raised
203  /* return values:
204  -1: error
205  0: timeout
206  > 0: I/O can continue
207  */
208  DLLLOCAL int select(int timeout_ms, bool read, const char* mname, ExceptionSink* xsink) const {
209  //printd(5, "select() to: %d read: %d mname: '%s'\n", timeout_ms, read, mname);
210  if (check_open(xsink))
211  return -1;
212 
213 #if defined HAVE_POLL
214  return poll_intern(timeout_ms, read, mname, xsink);
215 #elif defined HAVE_SELECT
216  return select_intern(timeout_ms, read, mname, xsink);
217 #else
218 #error no async I/O operations supported
219 #endif
220  }
221 
222 #if defined HAVE_POLL
223  DLLLOCAL int poll_intern(int timeout_ms, bool read, const char* mname, ExceptionSink* xsink) const {
224  int rc;
225  pollfd fds = {fd, (short)(read ? POLLIN : POLLOUT), 0};
226  while (true) {
227  rc = poll(&fds, 1, timeout_ms);
228  if (rc == -1 && errno == EINTR)
229  continue;
230  break;
231  }
232  if (rc < 0)
233  xsink->raiseException("FILE-SELECT-ERROR", "poll(2) returned an error in call to File::%s()", mname);
234  else if (!rc && ((fds.revents & POLLHUP) || (fds.revents & (POLLERR|POLLNVAL))))
235  rc = -1;
236 
237  return rc;
238  }
239 #elif defined HAVE_SELECT
240  DLLLOCAL int select_intern(int timeout_ms, bool read, const char* mname, ExceptionSink* xsink) const {
241 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
242  // async I/O ignored on files on windows
243  return 1;
244 #else
245  // select is inherently broken since it can only handle descriptors < FD_SETSIZE, which is 1024 on Linux for example
246  if (fd >= FD_SETSIZE) {
247  if (xsink)
248  xsink->raiseException("FILE-SELECT-ERROR", "fd is %d in call to File::%s() which is >= %d; contact "
249  "the Qore developers to implement an alternative to select() on this platform", fd, mname,
250  FD_SETSIZE);
251  return -1;
252  }
253  hashdecl timeval tv;
254  int rc;
255  while (true) {
256  // to be safe, we set the file descriptor arg after each EINTR (required on Linux for example)
257  fd_set sfs;
258 
259  FD_ZERO(&sfs);
260  FD_SET(fd, &sfs);
261 
262  tv.tv_sec = timeout_ms / 1000;
263  tv.tv_usec = (timeout_ms % 1000) * 1000;
264 
265  rc = read ? ::select(fd + 1, &sfs, 0, 0, &tv) : ::select(fd + 1, 0, &sfs, 0, &tv);
266  if (rc >= 0 || errno != EINTR)
267  break;
268  }
269  if (rc == -1) {
270  rc = 0;
271  xsink->raiseException("FILE-SELECT-ERROR", "select(2) returned an error in call to File::%s()", mname);
272  }
273 
274  return rc;
275 #endif
276  }
277 #endif
278 
279  // assumes lock is held and file is open
280  DLLLOCAL bool isDataAvailableIntern(int timeout_ms, const char* mname, ExceptionSink *xsink) const {
281  return select(timeout_ms, true, mname, xsink);
282  }
283 
284 #ifdef HAVE_TERMIOS_H
285  DLLLOCAL int setTerminalAttributes(int action, QoreTermIOS* ios, ExceptionSink* xsink) const {
286  AutoLocker al(m);
287 
288  if (check_open(xsink))
289  return -1;
290 
291  return ios->set(fd, action, xsink);
292  }
293 
294  DLLLOCAL int getTerminalAttributes(QoreTermIOS* ios, ExceptionSink* xsink) const {
295  AutoLocker al(m);
296 
297  if (check_open(xsink))
298  return -1;
299 
300  return ios->get(fd, xsink);
301  }
302 #endif
303 
304  // unlocked, assumes file is open
305  DLLLOCAL size_t read(void *buf, size_t bs) const {
306  qore_offset_t rc;
307  while (true) {
308  rc = ::read(fd, buf, bs);
309  // try again if we were interrupted by a signal
310  if (rc >= 0 || errno != EINTR)
311  break;
312  }
313 
314  if (rc > 0)
315  do_read_event_unlocked(rc, rc, bs);
316 
317  return rc;
318  }
319 
320  // unlocked, assumes file is open
321  DLLLOCAL size_t write(const void* buf, size_t len, ExceptionSink* xsink = 0) const {
322  qore_offset_t rc;
323  while (true) {
324  rc = ::write(fd, buf, len);
325  // try again if we are interrupted by a signal
326  if (rc >= 0 || errno != EINTR)
327  break;
328  }
329 
330  if (rc > 0)
331  do_write_event_unlocked(rc, rc, len);
332  else if (xsink && rc < 0)
333  xsink->raiseErrnoException("FILE-WRITE-ERROR", errno, "failed writing " QSD " byte%s to File", len,
334  len == 1 ? "" : "s");
335 
336  return rc;
337  }
338 
339  // private function, unlocked
340  DLLLOCAL int readChar() const {
341  unsigned char ch = 0;
342  if (read(&ch, 1) != 1)
343  return -1;
344  return (int)ch;
345  }
346 
347  // private function, unlocked
348  DLLLOCAL int readUnicode(int* n_len = 0) const;
349 
350  DLLLOCAL qore_offset_t readData(void* dest, size_t limit, int timeout_ms, const char* mname,
351  ExceptionSink* xsink) {
352  // wait for data
353  if (timeout_ms >= 0 && !isDataAvailableIntern(timeout_ms, mname, xsink)) {
354  if (!*xsink)
355  xsink->raiseException("FILE-READ-TIMEOUT", "timeout limit exceeded (%d ms) reading file block in "
356  "ReadOnlyFile::%s()", timeout_ms, mname);
357  return -1;
358  }
359 
360  qore_offset_t rc;
361  while (true) {
362  rc = ::read(fd, dest, limit);
363  //printd(5, "qore_qf_private::readData(%p, %ld, %d, '%s') fd: %d rc: %d\n", dest, limit, timeout_ms,
364  // mname, fd, rc);
365  // try again if we were interrupted by a signal
366  if (rc >= 0) {
367  break;
368  }
369  if (errno != EINTR) {
370  xsink->raiseErrnoException("FILE-READ-ERROR", errno, "error reading file in ReadOnlyFile::%s()",
371  mname);
372  return -1;
373  }
374  }
375 
376  return rc;
377  }
378 
379  DLLLOCAL QoreStringNode* readString(qore_offset_t size, int timeout_ms, const char* mname, ExceptionSink* xsink) {
380  return q_read_string(xsink, size, charset, std::bind(&qore_qf_private::readData, this, _1, _2, timeout_ms,
381  mname, _3));
382  }
383 
384  DLLLOCAL char* readBlock(qore_offset_t &size, int timeout_ms, const char* mname, ExceptionSink* xsink) {
385  size_t bs = size > 0 && size < DEFAULT_FILE_BUFSIZE ? size : DEFAULT_FILE_BUFSIZE;
386  size_t br = 0;
387  char* buf = (char* )malloc(sizeof(char) * bs);
388  char* bbuf = 0;
389 
390  while (true) {
391  // wait for data
392  if (timeout_ms >= 0 && !isDataAvailableIntern(timeout_ms, mname, xsink)) {
393  if (!*xsink)
394  xsink->raiseException("FILE-READ-TIMEOUT", "timeout limit exceeded (%d ms) reading file block in "
395  "ReadOnlyFile::%s()", timeout_ms, mname);
396  br = 0;
397  break;
398  }
399 
400  qore_offset_t rc;
401  while (true) {
402  rc = ::read(fd, buf, bs);
403  // try again if we were interrupted by a signal
404  if (rc >= 0)
405  break;
406  if (errno != EINTR) {
407  xsink->raiseErrnoException("FILE-READ-ERROR", errno, "error reading file after " QSD " bytes "
408  "read in File::%s()", br, mname);
409  break;
410  }
411  }
412  //printd(5, "readBlock(fd: %d, buf: %p, bs: %d) rc: %d\n", fd, buf, bs, rc);
413  if (rc <= 0)
414  break;
415 
416  // enlarge bbuf (ensure buffer is 1 byte bigger than needed)
417  bbuf = (char* )realloc(bbuf, br + rc + 1);
418  // append buffer to bbuf
419  memcpy(bbuf + br, buf, rc);
420  br += rc;
421 
422  do_read_event_unlocked(rc, br, size);
423 
424  if (size > 0) {
425  if (size - br < bs)
426  bs = size - br;
427  if (br >= (size_t)size)
428  break;
429  }
430  }
431  free(buf);
432  if (*xsink) {
433  if (bbuf)
434  free(bbuf);
435  return nullptr;
436  }
437  size = br;
438  return bbuf;
439  }
440 
441  DLLLOCAL QoreStringNode* readLine(bool incl_eol, ExceptionSink* xsink) {
442  QoreStringNodeHolder str(new QoreStringNode(charset));
443 
444  int rc = readLine(**str, incl_eol);
445 
446  if (rc == -2) {
447  xsink->raiseException("FILE-READLINE-ERROR", "file has not been opened");
448  return 0;
449  }
450 
451  return rc == -1 ? 0 : str.release();
452  }
453 
454  DLLLOCAL int readLine(QoreString& str, bool incl_eol = true) {
455  str.clear();
456 
457  AutoLocker al(m);
458 
459  if (!is_open)
460  return -2;
461 
462  bool tty = (bool)isatty(fd);
463 
464  int ch, rc = -1;
465 
466  while ((ch = readChar()) >= 0) {
467  str.concat((char)ch);
468  if (rc == -1)
469  rc = 0;
470 
471  if (ch == '\r') {
472  // see if next byte is \n' if we're not connected to a terminal device
473  if (!tty) {
474  ch = readChar();
475  if (ch >= 0) {
476  if (ch == '\n') {
477  if (incl_eol)
478  str.concat((char)ch);
479  } else {
480  // reset file to previous byte position
481  lseek(fd, -1, SEEK_CUR);
482  }
483  }
484  }
485  if (!incl_eol)
486  str.terminate(str.strlen() - 1);
487  break;
488  }
489 
490  if (ch == '\n') {
491  if (!incl_eol)
492  str.terminate(str.strlen() - 1);
493  break;
494  }
495  }
496 
497  return rc;
498  }
499 
500  DLLLOCAL int readUntil(char byte, QoreString& str, bool incl_byte = true) {
501  str.clear();
502 
503  AutoLocker al(m);
504 
505  if (!is_open)
506  return -2;
507 
508  int ch, rc = -1;
509 
510  while ((ch = readChar()) >= 0) {
511  char c = ch;
512  str.concat(c);
513  if (rc == -1)
514  rc = 0;
515  if (c == byte) {
516  if (!incl_byte)
517  str.terminate(str.strlen() - 1);
518  break;
519  }
520  }
521 
522  return rc;
523  }
524 
525  DLLLOCAL QoreStringNode* readUntil(const char* bytes, bool incl_bytes, ExceptionSink* xsink) {
526  QoreStringNodeHolder str(new QoreStringNode(charset));
527 
528  int rc = readUntil(bytes, **str, incl_bytes);
529 
530  if (rc == -2) {
531  xsink->raiseException("FILE-READLINE-ERROR", "file has not been opened");
532  return 0;
533  }
534 
535  return rc == -1 ? 0 : str.release();
536  }
537 
538  DLLLOCAL int readLineUnicode(QoreString& str, bool incl_eol = true) {
539  str.clear();
540 
541  AutoLocker al(m);
542 
543  if (!is_open)
544  return -2;
545 
546  bool tty = (bool)isatty(fd);
547 
548  int ch, rc = -1;
549 
550  while ((ch = readUnicode()) >= 0) {
551  // skip BOM
552  if (ch == 0xfeff)
553  continue;
554  else if (ch == 0xfffe && charset == QCS_UTF16 && str.empty()) {
555  charset = QCS_UTF16LE;
556  continue;
557  }
558 
559  str.concatUnicode(ch);
560 
561  if (rc == -1)
562  rc = 0;
563 
564  char c = str[str.size() - 1];
565 
566  if (c == '\r') {
567  // see if next byte is \n' if we're not connected to a terminal device
568  if (!tty) {
569  int len = 0;
570  ch = readUnicode(&len);
571  if (ch >= 0) {
572  if (ch == '\n') {
573  if (incl_eol)
574  str.concatUnicode(ch);
575  }
576  else {
577  // reset file to previous byte position
578  lseek(fd, -len, SEEK_CUR);
579  }
580  }
581  }
582  if (!incl_eol)
583  str.terminate(str.strlen() - 1);
584  break;
585  }
586 
587  if (ch == '\n') {
588  if (!incl_eol)
589  str.terminate(str.strlen() - 1);
590  break;
591  }
592  }
593 
594  return rc;
595  }
596 
597  DLLLOCAL int readUntilUnicode(char byte, QoreString& str, bool incl_byte = true) {
598  str.clear();
599 
600  AutoLocker al(m);
601 
602  if (!is_open)
603  return -2;
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  if (ch == byte) {
621  if (!incl_byte)
622  str.terminate(str.strlen() - 1);
623  break;
624  }
625  }
626 
627  return rc;
628  }
629  DLLLOCAL int readUntilUnicode(const char* bytes, QoreString& str, bool incl_bytes) {
630  str.clear();
631 
632  AutoLocker al(m);
633 
634  if (!is_open)
635  return -2;
636 
637  // offset in bytes
638  unsigned pos = 0;
639 
640  int ch, rc = -1;
641 
642  while ((ch = readUnicode()) >= 0) {
643  // skip BOM
644  if (ch == 0xfeff)
645  continue;
646  else if (ch == 0xfffe && charset == QCS_UTF16 && str.empty()) {
647  charset = QCS_UTF16LE;
648  continue;
649  }
650 
651  str.concatUnicode(ch);
652 
653  if (rc == -1)
654  rc = 0;
655 
656  if (ch == bytes[pos]) {
657  ++pos;
658  if (!bytes[pos]) {
659  if (!incl_bytes)
660  str.terminate(str.strlen() - pos);
661  break;
662  }
663  } else if (pos) {
664  // bytes=aaac read=aaaac str=aaa pos=3
665  // ^ ^
666  // restart search with characters already added to the string if more than 1 character was matched previously
667  if (pos > 1) {
668  unsigned ps = 1;
669  while (ps < pos) {
670  if (!strncmp(str.getBuffer() + ps, bytes, pos - ps)) {
671  pos -= ps;
672  break;
673  }
674  ++ps;
675  }
676  if (pos == ps)
677  pos = 0;
678  } else {
679  // restart search if failed
680  pos = 0;
681  }
682  }
683  }
684 
685  return rc;
686  }
687 
688  // not the most efficient search algorithm, restarts the search the byte after it fails for multi-byte patterns
689  DLLLOCAL int readUntil(const char* bytes, QoreString& str, bool incl_bytes) {
690  if (!bytes[1])
691  return readUntil(bytes[0], str, incl_bytes);
692 
693  str.clear();
694 
695  AutoLocker al(m);
696 
697  if (!is_open)
698  return -2;
699 
700  // offset in bytes
701  unsigned pos = 0;
702 
703  int ch, rc = -1;
704 
705  while ((ch = readChar()) >= 0) {
706  char c = ch;
707  str.concat(c);
708  if (rc == -1)
709  rc = 0;
710 
711  if (c == bytes[pos]) {
712  ++pos;
713  if (!bytes[pos]) {
714  if (!incl_bytes)
715  str.terminate(str.strlen() - pos);
716  break;
717  }
718  } else if (pos) {
719  // bytes=aaac read=aaaac str=aaa pos=3
720  // ^ ^
721  // restart search with characters already added to the string if more than 1 character was matched previously
722  if (pos > 1) {
723  unsigned ps = 1;
724  while (ps < pos) {
725  if (!strncmp(str.getBuffer() + ps, bytes, pos - ps)) {
726  pos -= ps;
727  break;
728  }
729  ++ps;
730  }
731  if (pos == ps)
732  pos = 0;
733  } else {
734  // restart search if failed
735  pos = 0;
736  }
737  }
738  }
739 
740  return rc;
741  }
742 
743  DLLLOCAL bool isTty() const {
744  AutoLocker al(m);
745 
746  if (!is_open)
747  return false;
748 
749  return (bool)isatty(fd);
750  }
751 
752  DLLLOCAL int detachFd() {
753  AutoLocker al(m);
754 
755  if (!is_open) {
756  return -1;
757  }
758 
759  int rc = fd;
760  // special files (stdout/stderr/stdin) are not closed anyway, so we don't mark the object closed in this case
761  if (!special_file) {
762  close_intern(true);
763  }
764  return rc;
765  }
766 
767  DLLLOCAL size_t getPos() const {
768  AutoLocker al(m);
769 
770  if (!is_open)
771  return -1;
772 
773  return lseek(fd, 0, SEEK_CUR);
774  }
775 
776  DLLLOCAL QoreHashNode* getEvent(int event, int source = QORE_SOURCE_FILE) const {
777  QoreHashNode* h = new QoreHashNode(autoTypeInfo);
778  if (event_arg) {
779  h->setKeyValue("arg", event_arg.refSelf(), nullptr);
780  }
781 
782  h->setKeyValue("event", event, nullptr);
783  h->setKeyValue("source", source, nullptr);
784  h->setKeyValue("id", (int64)this, nullptr);
785 
786  return h;
787  }
788 
789  DLLLOCAL void setEventQueue(ExceptionSink* xsink, Queue* q, QoreValue arg, bool with_data) {
790  AutoLocker al(m);
791  if (event_queue) {
792  if (event_arg) {
793  event_arg.discard(xsink);
794  }
795  event_queue->deref(xsink);
796  }
797  event_queue = q;
798  event_arg = arg;
799  event_data = with_data;
800  }
801 
802  DLLLOCAL void cleanup(ExceptionSink* xsink) {
803  AutoLocker al(m);
804  if (event_queue) {
805  // close the file before the delete message is put on the queue
806  // the file would be closed anyway in the destructor
807  close_intern();
808 
809  event_queue->pushAndTakeRef(getEvent(QORE_EVENT_DELETED));
810 
811  // deref and remove event queue and arg
812  if (event_arg) {
813  event_arg.discard(xsink);
814  event_arg.clear();
815  }
816  event_queue->deref(xsink);
817  event_queue = nullptr;
818  }
819  }
820 
821  DLLLOCAL void do_open_event_unlocked(const char* fn, int flags, int mode, const QoreEncoding* enc) const {
822  if (event_queue) {
823  QoreHashNode* h = getEvent(QORE_EVENT_OPEN_FILE);
824  h->setKeyValue("filename", new QoreStringNode(fn), nullptr);
825  h->setKeyValue("flags", flags, nullptr);
826  h->setKeyValue("mode", mode, nullptr);
827  h->setKeyValue("encoding", new QoreStringNode(enc->getCode()), nullptr);
828  event_queue->pushAndTakeRef(h);
829  }
830  }
831 
832  DLLLOCAL void do_opened_event_unlocked(const char* fn, int flags, int mode, const QoreEncoding* enc) const {
833  if (event_queue) {
834  QoreHashNode* h = getEvent(QORE_EVENT_FILE_OPENED);
835  h->setKeyValue("source", QORE_SOURCE_FILE, nullptr);
836  h->setKeyValue("id", (int64)this, nullptr);
837  h->setKeyValue("filename", new QoreStringNode(fn), nullptr);
838  h->setKeyValue("flags", flags, nullptr);
839  h->setKeyValue("mode", mode, nullptr);
840  h->setKeyValue("encoding", new QoreStringNode(enc->getCode()), nullptr);
841  event_queue->pushAndTakeRef(h);
842  }
843  }
844 
845  DLLLOCAL void do_close_event_unlocked() const {
846  if (event_queue) {
847  event_queue->pushAndTakeRef(getEvent(QORE_EVENT_CHANNEL_CLOSED));
848  }
849  }
850 
851  DLLLOCAL void do_read_event_unlocked(int bytes_read, int total_read, int bufsize) const {
852  // post bytes read on event queue, if any
853  if (event_queue) {
854  QoreHashNode* h = getEvent(QORE_EVENT_DATA_READ);
855  h->setKeyValue("read", bytes_read, nullptr);
856  h->setKeyValue("total_read", total_read, nullptr);
857  h->setKeyValue("total_to_read", bufsize, nullptr);
858  event_queue->pushAndTakeRef(h);
859  }
860  }
861 
862  DLLLOCAL void do_write_event_unlocked(int bytes_written, int total_written, int bufsize) const {
863  // post bytes sent on event queue, if any
864  if (event_queue) {
865  QoreHashNode* h = getEvent(QORE_EVENT_DATA_WRITTEN);
866  h->setKeyValue("written", bytes_written, nullptr);
867  h->setKeyValue("total_written", total_written, nullptr);
868  h->setKeyValue("total_to_write", bufsize, nullptr);
869  event_queue->pushAndTakeRef(h);
870  }
871  }
872 
873  DLLLOCAL QoreListNode* stat(ExceptionSink* xsink) const {
874  AutoLocker al(m);
875 
876  if (check_read_open(xsink))
877  return nullptr;
878 
879  hashdecl stat sbuf;
880  if (fstat(fd, &sbuf)) {
881  xsink->raiseErrnoException("FILE-STAT-ERROR", errno, "fstat() call failed");
882  return nullptr;
883  }
884 
885  return stat_to_list(sbuf);
886  }
887 
888  DLLLOCAL QoreHashNode* hstat(ExceptionSink* xsink) const {
889  AutoLocker al(m);
890 
891  if (check_read_open(xsink))
892  return nullptr;
893 
894  hashdecl stat sbuf;
895  if (fstat(fd, &sbuf)) {
896  xsink->raiseErrnoException("FILE-HSTAT-ERROR", errno, "fstat() call failed");
897  return nullptr;
898  }
899 
900  return stat_to_hash(sbuf);
901  }
902 
903 #ifdef Q_HAVE_STATVFS
904  DLLLOCAL QoreHashNode* statvfs(ExceptionSink* xsink) const {
905  AutoLocker al(m);
906 
907  if (check_read_open(xsink))
908  return nullptr;
909 
910  hashdecl statvfs vfs;
911 #ifdef HAVE_SYS_STATVFS_H
912  if (fstatvfs(fd, &vfs)) {
913  xsink->raiseErrnoException("FILE-STATVFS-ERROR", errno, "fstatvfs() call failed");
914  return nullptr;
915  }
916 #else
917  if (q_fstatvfs(filename.c_str(), &vfs)) {
918  xsink->raiseErrnoException("FILE-STATVFS-ERROR", errno, "fstatvfs() call failed");
919  return nullptr;
920  }
921 #endif
922 
923  return statvfs_to_hash(vfs);
924  }
925 #endif
926 };
927 
928 #endif
DLLEXPORT const QoreEncoding * QCS_UTF16
UTF-16 (only UTF-8 and UTF-16* are multi-byte encodings)
Definition: QoreEncoding.h:248
DLLEXPORT const QoreEncoding * QCS_UTF16LE
UTF-16LE (only UTF-8 and UTF-16* 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:48
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...
DLLEXPORT AbstractQoreNode * raiseException(const char *err, const char *fmt,...)
appends a Qore-language exception to the list
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
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 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)
DLLEXPORT const char * getBuffer() const
returns the string's buffer; this data should not be changed
Qore's string value type, reference counted, dynamically-allocated only.
Definition: QoreStringNode.h:50
provides a mutually-exclusive thread lock
Definition: QoreThreadLock.h:49
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:275
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