Qore Programming Language  0.9.16
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 - 2019 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_SELECT
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 the Qore developers to implement an alternative to select() on this platform", fd, mname, FD_SETSIZE);
249  return -1;
250  }
251  hashdecl timeval tv;
252  int rc;
253  while (true) {
254  // to be safe, we set the file descriptor arg after each EINTR (required on Linux for example)
255  fd_set sfs;
256 
257  FD_ZERO(&sfs);
258  FD_SET(fd, &sfs);
259 
260  tv.tv_sec = timeout_ms / 1000;
261  tv.tv_usec = (timeout_ms % 1000) * 1000;
262 
263  rc = read ? ::select(fd + 1, &sfs, 0, 0, &tv) : ::select(fd + 1, 0, &sfs, 0, &tv);
264  if (rc >= 0 || errno != EINTR)
265  break;
266  }
267  if (rc == -1) {
268  rc = 0;
269  xsink->raiseException("FILE-SELECT-ERROR", "select(2) returned an error in call to File::%s()", mname);
270  }
271 
272  return rc;
273 #endif
274  }
275 #endif
276 
277  // assumes lock is held and file is open
278  DLLLOCAL bool isDataAvailableIntern(int timeout_ms, const char* mname, ExceptionSink *xsink) const {
279  return select(timeout_ms, true, mname, xsink);
280  }
281 
282 #ifdef HAVE_TERMIOS_H
283  DLLLOCAL int setTerminalAttributes(int action, QoreTermIOS* ios, ExceptionSink* xsink) const {
284  AutoLocker al(m);
285 
286  if (check_open(xsink))
287  return -1;
288 
289  return ios->set(fd, action, xsink);
290  }
291 
292  DLLLOCAL int getTerminalAttributes(QoreTermIOS* ios, ExceptionSink* xsink) const {
293  AutoLocker al(m);
294 
295  if (check_open(xsink))
296  return -1;
297 
298  return ios->get(fd, xsink);
299  }
300 #endif
301 
302  // unlocked, assumes file is open
303  DLLLOCAL qore_size_t read(void *buf, qore_size_t bs) const {
304  qore_offset_t rc;
305  while (true) {
306  rc = ::read(fd, buf, bs);
307  // try again if we were interrupted by a signal
308  if (rc >= 0 || errno != EINTR)
309  break;
310  }
311 
312  if (rc > 0)
313  do_read_event_unlocked(rc, rc, bs);
314 
315  return rc;
316  }
317 
318  // unlocked, assumes file is open
319  DLLLOCAL qore_size_t write(const void* buf, qore_size_t len, ExceptionSink* xsink = 0) const {
320  qore_offset_t rc;
321  while (true) {
322  rc = ::write(fd, buf, len);
323  // try again if we are interrupted by a signal
324  if (rc >= 0 || errno != EINTR)
325  break;
326  }
327 
328  if (rc > 0)
329  do_write_event_unlocked(rc, rc, len);
330  else if (xsink && rc < 0)
331  xsink->raiseErrnoException("FILE-WRITE-ERROR", errno, "failed writing " QSD " byte%s to File", len, len == 1 ? "" : "s");
332 
333  return rc;
334  }
335 
336  // private function, unlocked
337  DLLLOCAL int readChar() const {
338  unsigned char ch = 0;
339  if (read(&ch, 1) != 1)
340  return -1;
341  return (int)ch;
342  }
343 
344  // private function, unlocked
345  DLLLOCAL int readUnicode(int* n_len = 0) const;
346 
347  DLLLOCAL qore_offset_t readData(void* dest, qore_size_t limit, int timeout_ms, const char* mname, ExceptionSink* xsink) {
348  // wait for data
349  if (timeout_ms >= 0 && !isDataAvailableIntern(timeout_ms, mname, xsink)) {
350  if (!*xsink)
351  xsink->raiseException("FILE-READ-TIMEOUT", "timeout limit exceeded (%d ms) reading file block in ReadOnlyFile::%s()", timeout_ms, mname);
352  return -1;
353  }
354 
355  qore_offset_t rc;
356  while (true) {
357  rc = ::read(fd, dest, limit);
358  // try again if we were interrupted by a signal
359  if (rc >= 0)
360  break;
361  if (errno != EINTR) {
362  xsink->raiseErrnoException("FILE-READ-ERROR", errno, "error reading file in ReadOnlyFile::%s()", mname);
363  return -1;
364  }
365  }
366 
367  return rc;
368  }
369 
370  DLLLOCAL QoreStringNode* readString(qore_offset_t size, int timeout_ms, const char* mname, ExceptionSink* xsink) {
371  return q_read_string(xsink, size, charset, std::bind(&qore_qf_private::readData, this, _1, _2, timeout_ms, mname, _3));
372  }
373 
374  DLLLOCAL char* readBlock(qore_offset_t &size, int timeout_ms, const char* mname, ExceptionSink* xsink) {
375  qore_size_t bs = size > 0 && size < DEFAULT_FILE_BUFSIZE ? size : DEFAULT_FILE_BUFSIZE;
376  qore_size_t br = 0;
377  char* buf = (char* )malloc(sizeof(char) * bs);
378  char* bbuf = 0;
379 
380  while (true) {
381  // wait for data
382  if (timeout_ms >= 0 && !isDataAvailableIntern(timeout_ms, mname, xsink)) {
383  if (!*xsink)
384  xsink->raiseException("FILE-READ-TIMEOUT", "timeout limit exceeded (%d ms) reading file block in ReadOnlyFile::%s()", timeout_ms, mname);
385  br = 0;
386  break;
387  }
388 
389  qore_offset_t rc;
390  while (true) {
391  rc = ::read(fd, buf, bs);
392  // try again if we were interrupted by a signal
393  if (rc >= 0)
394  break;
395  if (errno != EINTR) {
396  xsink->raiseErrnoException("FILE-READ-ERROR", errno, "error reading file after " QSD " bytes read in File::%s()", br, mname);
397  break;
398  }
399  }
400  //printd(5, "readBlock(fd: %d, buf: %p, bs: %d) rc: %d\n", fd, buf, bs, rc);
401  if (rc <= 0)
402  break;
403 
404  // enlarge bbuf (ensure buffer is 1 byte bigger than needed)
405  bbuf = (char* )realloc(bbuf, br + rc + 1);
406  // append buffer to bbuf
407  memcpy(bbuf + br, buf, rc);
408  br += rc;
409 
410  do_read_event_unlocked(rc, br, size);
411 
412  if (size > 0) {
413  if (size - br < bs)
414  bs = size - br;
415  if (br >= (qore_size_t)size)
416  break;
417  }
418  }
419  free(buf);
420  if (*xsink) {
421  if (bbuf)
422  free(bbuf);
423  return nullptr;
424  }
425  size = br;
426  return bbuf;
427  }
428 
429  DLLLOCAL QoreStringNode* readLine(bool incl_eol, ExceptionSink* xsink) {
430  QoreStringNodeHolder str(new QoreStringNode(charset));
431 
432  int rc = readLine(**str, incl_eol);
433 
434  if (rc == -2) {
435  xsink->raiseException("FILE-READLINE-ERROR", "file has not been opened");
436  return 0;
437  }
438 
439  return rc == -1 ? 0 : str.release();
440  }
441 
442  DLLLOCAL int readLine(QoreString& str, bool incl_eol = true) {
443  str.clear();
444 
445  AutoLocker al(m);
446 
447  if (!is_open)
448  return -2;
449 
450  bool tty = (bool)isatty(fd);
451 
452  int ch, rc = -1;
453 
454  while ((ch = readChar()) >= 0) {
455  str.concat((char)ch);
456  if (rc == -1)
457  rc = 0;
458 
459  if (ch == '\r') {
460  // see if next byte is \n' if we're not connected to a terminal device
461  if (!tty) {
462  ch = readChar();
463  if (ch >= 0) {
464  if (ch == '\n') {
465  if (incl_eol)
466  str.concat((char)ch);
467  } else {
468  // reset file to previous byte position
469  lseek(fd, -1, SEEK_CUR);
470  }
471  }
472  }
473  if (!incl_eol)
474  str.terminate(str.strlen() - 1);
475  break;
476  }
477 
478  if (ch == '\n') {
479  if (!incl_eol)
480  str.terminate(str.strlen() - 1);
481  break;
482  }
483  }
484 
485  return rc;
486  }
487 
488  DLLLOCAL int readUntil(char byte, QoreString& str, bool incl_byte = true) {
489  str.clear();
490 
491  AutoLocker al(m);
492 
493  if (!is_open)
494  return -2;
495 
496  int ch, rc = -1;
497 
498  while ((ch = readChar()) >= 0) {
499  char c = ch;
500  str.concat(c);
501  if (rc == -1)
502  rc = 0;
503  if (c == byte) {
504  if (!incl_byte)
505  str.terminate(str.strlen() - 1);
506  break;
507  }
508  }
509 
510  return rc;
511  }
512 
513  DLLLOCAL QoreStringNode* readUntil(const char* bytes, bool incl_bytes, ExceptionSink* xsink) {
514  QoreStringNodeHolder str(new QoreStringNode(charset));
515 
516  int rc = readUntil(bytes, **str, incl_bytes);
517 
518  if (rc == -2) {
519  xsink->raiseException("FILE-READLINE-ERROR", "file has not been opened");
520  return 0;
521  }
522 
523  return rc == -1 ? 0 : str.release();
524  }
525 
526  DLLLOCAL int readLineUnicode(QoreString& str, bool incl_eol = true) {
527  str.clear();
528 
529  AutoLocker al(m);
530 
531  if (!is_open)
532  return -2;
533 
534  bool tty = (bool)isatty(fd);
535 
536  int ch, rc = -1;
537 
538  while ((ch = readUnicode()) >= 0) {
539  // skip BOM
540  if (ch == 0xfeff)
541  continue;
542  else if (ch == 0xfffe && charset == QCS_UTF16 && str.empty()) {
543  charset = QCS_UTF16LE;
544  continue;
545  }
546 
547  str.concatUnicode(ch);
548 
549  if (rc == -1)
550  rc = 0;
551 
552  char c = str[str.size() - 1];
553 
554  if (c == '\r') {
555  // see if next byte is \n' if we're not connected to a terminal device
556  if (!tty) {
557  int len = 0;
558  ch = readUnicode(&len);
559  if (ch >= 0) {
560  if (ch == '\n') {
561  if (incl_eol)
562  str.concatUnicode(ch);
563  }
564  else {
565  // reset file to previous byte position
566  lseek(fd, -len, SEEK_CUR);
567  }
568  }
569  }
570  if (!incl_eol)
571  str.terminate(str.strlen() - 1);
572  break;
573  }
574 
575  if (ch == '\n') {
576  if (!incl_eol)
577  str.terminate(str.strlen() - 1);
578  break;
579  }
580  }
581 
582  return rc;
583  }
584 
585  DLLLOCAL int readUntilUnicode(char byte, QoreString& str, bool incl_byte = true) {
586  str.clear();
587 
588  AutoLocker al(m);
589 
590  if (!is_open)
591  return -2;
592 
593  int ch, rc = -1;
594 
595  while ((ch = readUnicode()) >= 0) {
596  // skip BOM
597  if (ch == 0xfeff)
598  continue;
599  else if (ch == 0xfffe && charset == QCS_UTF16 && str.empty()) {
600  charset = QCS_UTF16LE;
601  continue;
602  }
603 
604  str.concatUnicode(ch);
605 
606  if (rc == -1)
607  rc = 0;
608  if (ch == byte) {
609  if (!incl_byte)
610  str.terminate(str.strlen() - 1);
611  break;
612  }
613  }
614 
615  return rc;
616  }
617  DLLLOCAL int readUntilUnicode(const char* bytes, QoreString& str, bool incl_bytes) {
618  str.clear();
619 
620  AutoLocker al(m);
621 
622  if (!is_open)
623  return -2;
624 
625  // offset in bytes
626  unsigned pos = 0;
627 
628  int ch, rc = -1;
629 
630  while ((ch = readUnicode()) >= 0) {
631  // skip BOM
632  if (ch == 0xfeff)
633  continue;
634  else if (ch == 0xfffe && charset == QCS_UTF16 && str.empty()) {
635  charset = QCS_UTF16LE;
636  continue;
637  }
638 
639  str.concatUnicode(ch);
640 
641  if (rc == -1)
642  rc = 0;
643 
644  if (ch == bytes[pos]) {
645  ++pos;
646  if (!bytes[pos]) {
647  if (!incl_bytes)
648  str.terminate(str.strlen() - pos);
649  break;
650  }
651  } else if (pos) {
652  // bytes=aaac read=aaaac str=aaa pos=3
653  // ^ ^
654  // restart search with characters already added to the string if more than 1 character was matched previously
655  if (pos > 1) {
656  unsigned ps = 1;
657  while (ps < pos) {
658  if (!strncmp(str.getBuffer() + ps, bytes, pos - ps)) {
659  pos -= ps;
660  break;
661  }
662  ++ps;
663  }
664  if (pos == ps)
665  pos = 0;
666  } else {
667  // restart search if failed
668  pos = 0;
669  }
670  }
671  }
672 
673  return rc;
674  }
675 
676  // not the most efficient search algorithm, restarts the search the byte after it fails for multi-byte patterns
677  DLLLOCAL int readUntil(const char* bytes, QoreString& str, bool incl_bytes) {
678  if (!bytes[1])
679  return readUntil(bytes[0], str, incl_bytes);
680 
681  str.clear();
682 
683  AutoLocker al(m);
684 
685  if (!is_open)
686  return -2;
687 
688  // offset in bytes
689  unsigned pos = 0;
690 
691  int ch, rc = -1;
692 
693  while ((ch = readChar()) >= 0) {
694  char c = ch;
695  str.concat(c);
696  if (rc == -1)
697  rc = 0;
698 
699  if (c == bytes[pos]) {
700  ++pos;
701  if (!bytes[pos]) {
702  if (!incl_bytes)
703  str.terminate(str.strlen() - pos);
704  break;
705  }
706  } else if (pos) {
707  // bytes=aaac read=aaaac str=aaa pos=3
708  // ^ ^
709  // restart search with characters already added to the string if more than 1 character was matched previously
710  if (pos > 1) {
711  unsigned ps = 1;
712  while (ps < pos) {
713  if (!strncmp(str.getBuffer() + ps, bytes, pos - ps)) {
714  pos -= ps;
715  break;
716  }
717  ++ps;
718  }
719  if (pos == ps)
720  pos = 0;
721  } else {
722  // restart search if failed
723  pos = 0;
724  }
725  }
726  }
727 
728  return rc;
729  }
730 
731  DLLLOCAL bool isTty() const {
732  AutoLocker al(m);
733 
734  if (!is_open)
735  return false;
736 
737  return (bool)isatty(fd);
738  }
739 
740  DLLLOCAL int detachFd() {
741  AutoLocker al(m);
742 
743  if (!is_open) {
744  return -1;
745  }
746 
747  int rc = fd;
748  // special files (stdout/stderr/stdin) are not closed anyway, so we don't mark the object closed in this case
749  if (!special_file) {
750  close_intern(true);
751  }
752  return rc;
753  }
754 
755  DLLLOCAL qore_size_t getPos() const {
756  AutoLocker al(m);
757 
758  if (!is_open)
759  return -1;
760 
761  return lseek(fd, 0, SEEK_CUR);
762  }
763 
764  DLLLOCAL QoreHashNode* getEvent(int event, int source = QORE_SOURCE_FILE) const {
765  QoreHashNode* h = new QoreHashNode(autoTypeInfo);
766  if (event_arg) {
767  h->setKeyValue("arg", event_arg.refSelf(), nullptr);
768  }
769 
770  h->setKeyValue("event", event, nullptr);
771  h->setKeyValue("source", source, nullptr);
772  h->setKeyValue("id", (int64)this, nullptr);
773 
774  return h;
775  }
776 
777  DLLLOCAL void setEventQueue(ExceptionSink* xsink, Queue* q, QoreValue arg, bool with_data) {
778  AutoLocker al(m);
779  if (event_queue) {
780  if (event_arg) {
781  event_arg.discard(xsink);
782  }
783  event_queue->deref(xsink);
784  }
785  event_queue = q;
786  event_arg = arg;
787  event_data = with_data;
788  }
789 
790  DLLLOCAL void cleanup(ExceptionSink* xsink) {
791  AutoLocker al(m);
792  if (event_queue) {
793  // close the file before the delete message is put on the queue
794  // the file would be closed anyway in the destructor
795  close_intern();
796 
797  event_queue->pushAndTakeRef(getEvent(QORE_EVENT_DELETED));
798 
799  // deref and remove event queue and arg
800  if (event_arg) {
801  event_arg.discard(xsink);
802  event_arg.clear();
803  }
804  event_queue->deref(xsink);
805  event_queue = nullptr;
806  }
807  }
808 
809  DLLLOCAL void do_open_event_unlocked(const char* fn, int flags, int mode, const QoreEncoding* enc) const {
810  if (event_queue) {
811  QoreHashNode* h = getEvent(QORE_EVENT_OPEN_FILE);
812  h->setKeyValue("filename", new QoreStringNode(fn), nullptr);
813  h->setKeyValue("flags", flags, nullptr);
814  h->setKeyValue("mode", mode, nullptr);
815  h->setKeyValue("encoding", new QoreStringNode(enc->getCode()), nullptr);
816  event_queue->pushAndTakeRef(h);
817  }
818  }
819 
820  DLLLOCAL void do_opened_event_unlocked(const char* fn, int flags, int mode, const QoreEncoding* enc) const {
821  if (event_queue) {
822  QoreHashNode* h = getEvent(QORE_EVENT_FILE_OPENED);
823  h->setKeyValue("source", QORE_SOURCE_FILE, nullptr);
824  h->setKeyValue("id", (int64)this, nullptr);
825  h->setKeyValue("filename", new QoreStringNode(fn), nullptr);
826  h->setKeyValue("flags", flags, nullptr);
827  h->setKeyValue("mode", mode, nullptr);
828  h->setKeyValue("encoding", new QoreStringNode(enc->getCode()), nullptr);
829  event_queue->pushAndTakeRef(h);
830  }
831  }
832 
833  DLLLOCAL void do_close_event_unlocked() const {
834  if (event_queue) {
835  event_queue->pushAndTakeRef(getEvent(QORE_EVENT_CHANNEL_CLOSED));
836  }
837  }
838 
839  DLLLOCAL void do_read_event_unlocked(int bytes_read, int total_read, int bufsize) const {
840  // post bytes read on event queue, if any
841  if (event_queue) {
842  QoreHashNode* h = getEvent(QORE_EVENT_DATA_READ);
843  h->setKeyValue("read", bytes_read, nullptr);
844  h->setKeyValue("total_read", total_read, nullptr);
845  h->setKeyValue("total_to_read", bufsize, nullptr);
846  event_queue->pushAndTakeRef(h);
847  }
848  }
849 
850  DLLLOCAL void do_write_event_unlocked(int bytes_written, int total_written, int bufsize) const {
851  // post bytes sent on event queue, if any
852  if (event_queue) {
853  QoreHashNode* h = getEvent(QORE_EVENT_DATA_WRITTEN);
854  h->setKeyValue("written", bytes_written, nullptr);
855  h->setKeyValue("total_written", total_written, nullptr);
856  h->setKeyValue("total_to_write", bufsize, nullptr);
857  event_queue->pushAndTakeRef(h);
858  }
859  }
860 
861  DLLLOCAL QoreListNode* stat(ExceptionSink* xsink) const {
862  AutoLocker al(m);
863 
864  if (check_read_open(xsink))
865  return nullptr;
866 
867  hashdecl stat sbuf;
868  if (fstat(fd, &sbuf)) {
869  xsink->raiseErrnoException("FILE-STAT-ERROR", errno, "fstat() call failed");
870  return nullptr;
871  }
872 
873  return stat_to_list(sbuf);
874  }
875 
876  DLLLOCAL QoreHashNode* hstat(ExceptionSink* xsink) const {
877  AutoLocker al(m);
878 
879  if (check_read_open(xsink))
880  return nullptr;
881 
882  hashdecl stat sbuf;
883  if (fstat(fd, &sbuf)) {
884  xsink->raiseErrnoException("FILE-HSTAT-ERROR", errno, "fstat() call failed");
885  return nullptr;
886  }
887 
888  return stat_to_hash(sbuf);
889  }
890 
891 #ifdef Q_HAVE_STATVFS
892  DLLLOCAL QoreHashNode* statvfs(ExceptionSink* xsink) const {
893  AutoLocker al(m);
894 
895  if (check_read_open(xsink))
896  return nullptr;
897 
898  hashdecl statvfs vfs;
899 #ifdef HAVE_SYS_STATVFS_H
900  if (fstatvfs(fd, &vfs)) {
901  xsink->raiseErrnoException("FILE-STATVFS-ERROR", errno, "fstatvfs() call failed");
902  return nullptr;
903  }
904 #else
905  if (q_fstatvfs(filename.c_str(), &vfs)) {
906  xsink->raiseErrnoException("FILE-STATVFS-ERROR", errno, "fstatvfs() call failed");
907  return nullptr;
908  }
909 #endif
910 
911  return statvfs_to_hash(vfs);
912  }
913 #endif
914 };
915 
916 #endif
SimpleRefHolder< QoreStringNode >
QoreString::clear
DLLEXPORT void clear()
reset string to zero length; memory is not deallocated; string encoding does not change
QoreString::size
DLLEXPORT qore_size_t size() const
returns number of bytes in the string (not including the null pointer)
qore_offset_t
intptr_t qore_offset_t
used for offsets that could be negative
Definition: common.h:76
QoreHashNode::setKeyValue
DLLEXPORT int setKeyValue(const char *key, QoreValue value, ExceptionSink *xsink)
sets the value of "key" to "value"
QoreValue
The main value class in Qore, designed to be passed by value.
Definition: QoreValue.h:262
QoreHashNode
This is the hash or associative list container type in Qore, dynamically allocated only,...
Definition: QoreHashNode.h:50
ExceptionSink::raiseErrnoException
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...
qore_size_t
size_t qore_size_t
used for sizes (same range as a pointer)
Definition: common.h:73
QCS_UTF16LE
const DLLEXPORT QoreEncoding * QCS_UTF16LE
UTF-16LE (only UTF-8 and UTF-16* are multi-byte encodings)
Definition: QoreEncoding.h:253
QCS_UTF16
const DLLEXPORT QoreEncoding * QCS_UTF16
UTF-16 (only UTF-8 and UTF-16* are multi-byte encodings)
Definition: QoreEncoding.h:251
QoreListNode
This is the list container type in Qore, dynamically allocated only, reference counted.
Definition: QoreListNode.h:52
int64
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
AutoLocker
provides a safe and exception-safe way to hold locks in Qore, only to be used on the stack,...
Definition: QoreThreadLock.h:136
QoreString
Qore's string type supported by the QoreEncoding class.
Definition: QoreString.h:81
QoreValue::refSelf
DLLEXPORT QoreValue refSelf() const
references the contained value if type == QV_Node, returns itself
ExceptionSink::raiseException
DLLEXPORT AbstractQoreNode * raiseException(const char *err, const char *fmt,...)
appends a Qore-language exception to the list
QoreValue::discard
DLLEXPORT void discard(ExceptionSink *xsink)
dereferences any contained AbstractQoreNode pointer and sets to 0; does not modify other values
QoreString::terminate
DLLEXPORT void terminate(qore_size_t size)
terminates the string at byte position "size", the string is reallocated if necessary
QoreEncoding::getCode
const DLLEXPORT char * getCode() const
returns the string code (ex: "UTF-8") for the encoding
QoreString::concatUnicode
DLLEXPORT int concatUnicode(unsigned code, ExceptionSink *xsink)
append a character sequence from a unicode code point (returns 0 for OK, -1 for exception)
QoreString::getBuffer
const DLLEXPORT char * getBuffer() const
returns the string's buffer; this data should not be changed
QoreString::concat
DLLEXPORT void concat(const QoreString *str, ExceptionSink *xsink)
concatenates a string and converts encodings if necessary
ExceptionSink
container for holding Qore-language exception information and also for registering a "thread_exit" ca...
Definition: ExceptionSink.h:48
QoreString::strlen
DLLEXPORT qore_size_t strlen() const
returns number of bytes in the string (not including the null pointer)
QoreThreadLock
provides a mutually-exclusive thread lock
Definition: QoreThreadLock.h:49
QoreString::empty
DLLEXPORT bool empty() const
returns true if the string is empty, false if not
QoreEncoding
defines string encoding functions in Qore
Definition: QoreEncoding.h:83
QoreStringNode
Qore's string value type, reference counted, dynamically-allocated only.
Definition: QoreStringNode.h:50