unix/SysFile.cpp
Go to the documentation of this file.
1 /*----------------------------------------------------------------------------*/
2 /* */
3 /* Copyright (c) 1995, 2004 IBM Corporation. All rights reserved. */
4 /* Copyright (c) 2005-2009 Rexx Language Association. All rights reserved. */
5 /* */
6 /* This program and the accompanying materials are made available under */
7 /* the terms of the Common Public License v1.0 which accompanies this */
8 /* distribution. A copy is also available at the following address: */
9 /* http://www.ibm.com/developerworks/oss/CPLv1.0.htm */
10 /* */
11 /* Redistribution and use in source and binary forms, with or */
12 /* without modification, are permitted provided that the following */
13 /* conditions are met: */
14 /* */
15 /* Redistributions of source code must retain the above copyright */
16 /* notice, this list of conditions and the following disclaimer. */
17 /* Redistributions in binary form must reproduce the above copyright */
18 /* notice, this list of conditions and the following disclaimer in */
19 /* the documentation and/or other materials provided with the distribution. */
20 /* */
21 /* Neither the name of Rexx Language Association nor the names */
22 /* of its contributors may be used to endorse or promote products */
23 /* derived from this software without specific prior written permission. */
24 /* */
25 /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */
26 /* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */
27 /* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS */
28 /* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT */
29 /* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */
30 /* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED */
31 /* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, */
32 /* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY */
33 /* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING */
34 /* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
35 /* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
36 /* */
37 /*----------------------------------------------------------------------------*/
38 /******************************************************************************/
39 /* REXX Kernel SysFile.cpp */
40 /* */
41 /* Unix implementation of the SysFile class. */
42 /* */
43 /******************************************************************************/
44 
45 #ifdef HAVE_CONFIG_H
46 # include "config.h"
47 #endif
48 
49 #include <string.h>
50 #include <stdlib.h>
51 #include <unistd.h>
52 #include <sys/stat.h>
53 #include <sys/types.h>
54 #include <fcntl.h>
55 #include <sys/ioctl.h>
56 #include <termios.h>
57 #include <stdio.h>
58 #include <errno.h>
59 #include <time.h>
60 #include <fcntl.h>
61 
62 #if defined( HAVE_SYS_FILIO_H )
63 # include <sys/filio.h>
64 #endif
65 
66 #if defined(__APPLE__) && defined(__MACH__)
67 # define lseek64 lseek
68 # define open64 open
69 #endif
70 
71 #include "SysFile.hpp"
72 
73 // This is all the static stuff
74 const int SysFile::stdinHandle = 0;
75 const int SysFile::stdoutHandle = 1;
76 const int SysFile::stderrHandle = 2;
77 
78 #if defined __APPLE__
79 # define open64 open
80 // avoid warning: '(l)stat64' is deprecated: first deprecated in macOS 10.6
81 # define stat64 stat
82 # define lstat64 lstat
83 # define fstat64 fstat
84 #endif
85 
86 /**
87  * Default constructor for a SysFile object.
88  */
90 {
91  fileHandle = -1;
92  errInfo = 0;
93  openedHandle = false;
94  flags = 0;
95  mode = 0;
96  share = 0;
97  filename = NULL;
98  buffered = true;
99  transient = false;
100  device = false;
101  writeable = false;
102  readable = false;
103  isTTY = false;
104  buffer = NULL;
106  bufferPosition = 0;
107  bufferedInput = 0;
108  append = true;
109  filePointer = 0;
110  ungetchar = -1;
111  writeBuffered = false; // no pending write operations
112 }
113 
114 /**
115  * Opens a file. This opens the file for both lowlevel I/O
116  * and also for higher level I/O.
117  *
118  * @param name Name of the stream.
119  * @param openFlags The open flags. This are the same flags used on the open()
120  * function.
121  *
122  * @return true if the file was opened successfully, false otherwise.
123  *
124  * @remarks TTY devices should not be buffered. Stdin, stdout, etc. are set
125  * correctly in their setStdIn() setStdOut(), etc., functions. But
126  * here we need to check for TTY devices and not buffer them.
127  * /dev/pts/n for example.
128  */
129 bool SysFile::open(const char *name, int openFlags, int openMode, int shareMode)
130 {
131  flags = openFlags; // save the initial flag values
132 
133  // we must open this with the NOINHERIT flag added
134  fileHandle = ::open64(name, openFlags, (mode_t)openMode);
135  if ( fileHandle == -1 )
136  {
137  errInfo = errno;
138  return false;
139  }
140 
141  // mark that we opened this handle
142  openedHandle = true;
143 
144  // save a copy of the name
145  filename = strdup(name);
146  ungetchar = -1; // 0xFF indicates no char
147 
148  // is this append mode?
149  if ((flags & RX_O_APPEND) != 0)
150  {
151  // mark this true, and position at the end
152  append = true;
153  lseek64(fileHandle, 0, SEEK_END);
154  }
155 
156  // set eof flag
157  fileeof = false;
158 
160 
161  // set the default buffer size (and allocate the buffer)
162  if ( isTTY )
163  {
164  setBuffering(false, 0);
165  }
166  else
167  {
168  setBuffering(true, 0);
169  }
170 
171  return true;
172 }
173 
174 
175 /**
176  * Open a stream using a provided handle value.
177  *
178  * @param handle The source stream handle.
179  * @param fdopenMode The fdopen() mode flags for the stream.
180  *
181  * @return true if the file opened ok, false otherwise.
182  *
183  * @remarks TTY devices should not be buffered. Stdin, stdout, etc. are set
184  * correctly in their setStdIn() setStdOut(), etc., functions. But
185  * here we need to check for TTY devices and not buffer them.
186  * /dev/pts/n for example.
187  */
188 bool SysFile::open(int handle)
189 {
190  // we didn't open this.
191  openedHandle = false;
192  fileHandle = handle;
193  ungetchar = -1; // 0xFF indicates no char
195 
196  // set the default buffer size (and allocate the buffer)
197  if ( isTTY )
198  {
199  setBuffering(false, 0);
200  }
201  else
202  {
203  setBuffering(true, 0);
204  }
205 
206  return true;
207 }
208 
209 /**
210  * Reset this to an unopened state.
211  */
213 {
214  // make sure we flush anything pending.
215  flush();
216  if (buffer != NULL)
217  {
218  free(buffer);
219  buffer = NULL;
220  }
221  fileHandle = -1;
222 }
223 
224 /**
225  * Controls buffering for this stream.
226  *
227  * @param buffer True or false depending on the desired buffering mode.
228  */
229 void SysFile::setBuffering(bool buffering, size_t length)
230 {
231  if (buffering)
232  {
233  buffered = true;
234  if (length == 0)
235  {
236  length = DEFAULT_BUFFER_SIZE;
237  }
238  buffer = (char *)malloc(length);
239  if (buffer == NULL)
240  {
241  buffered = false;
242  }
243  }
244  else
245  {
246  buffered = false;
247  if (buffer != NULL)
248  {
249  free(buffer);
250  buffer = NULL;
251  }
252  }
253  // reset all of the buffering controls to the defaults
254  bufferPosition = 0;
255  bufferedInput = 0;
256  writeBuffered = false;
257 }
258 
259 
260 /**
261  * Close the stream, and free all associated resources.
262  *
263  * @return true if this closed successfully, false otherwise.
264  */
266 {
267  // don't do anything if not opened
268  if (fileHandle == -1)
269  {
270  return true;
271  }
272 
273  // if we're buffering, make sure the buffers are flushed
274  if (buffered)
275  {
276  flush();
277  }
278  // free out storage areas first
279  if (filename != NULL)
280  {
281  free(const_cast<char *>(filename));
282  filename = NULL;
283  }
284  if (buffer != NULL)
285  {
286  free(buffer);
287  buffer = NULL;
288  }
289  errInfo = 0;
290  // if we opened this handle, we need to close it too.
291  if (openedHandle)
292  {
293  if (::close(fileHandle) == EOF)
294  {
295  fileHandle = -1;
296  errInfo = errno;
297  return false;
298  }
299  }
300 
301  // always clear this on a close
302  fileHandle = -1;
303 
304  return true;
305 }
306 
307 /**
308  * Flush the stream buffers.
309  *
310  * @return True if this worked without error, false otherwise.
311  */
313 {
314  if (buffered)
315  {
316  // do we have data in a write buffer?
317  if (writeBuffered && bufferPosition > 0)
318  {
319  // write this out...but if it fails, we need to bail
320  int written = ::write(fileHandle, buffer, (unsigned int)bufferPosition);
321  // did we have an error?
322  if (written <= 0)
323  {
324  errInfo = errno;
325  return false;
326  }
327  // update the real output position
328  filePointer += written;
329  // and invalidate the buffer
330  bufferPosition = 0;
331  bufferedInput = 0;
332  }
333  }
334  return true;
335 }
336 
337 /**
338  * Read bytes from the stream.
339  *
340  * @param buf The buffer to read into.
341  * @param len The requested number of bytes to read.
342  * @param bytesRead The actual number of bytes read.
343  *
344  * @return True if one or more bytes are read into buf, otherwise false.
345  */
346 bool SysFile::read(char *buf, size_t len, size_t &bytesRead)
347 {
348  // set bytesRead to 0 to be sure we can tell if we are returning any bytes.
349  bytesRead = 0;
350 
351  // asking for nothing? this is pretty easy
352  if (len == 0)
353  {
354  return true;
355  }
356 
357  // if we have an ungetchar, we need to grab that first
358  if (ungetchar != -1)
359  {
360  // add this to our count
361  bytesRead = 1;
362  // copy the character over
363  buf[0] = (char)ungetchar;
364  buf++;
365  len--;
366  ungetchar = -1;
367  // were we only looking for one character (very common in cases where
368  // we've had a char pushed back)
369  if (len == 0)
370  {
371  return true;
372  }
373  }
374 
375  // are we doing buffering?
376  if (buffered)
377  {
378  // do we have pending output in the buffer?
379  if (writeBuffered)
380  {
381  flush();
382  writeBuffered = false;
383  bufferPosition = 0;
384  bufferedInput = 0;
385  }
386 
387 
388  while (len > 0)
389  {
390  // have we exhausted the buffer data?
392  {
393  // read another chunk of data.
394  int blockRead = ::read(fileHandle, buffer, (unsigned int)bufferSize);
395  if (blockRead <= 0)
396  {
397  // not get anything?
398  if (blockRead == 0)
399  {
400  fileeof = true;
401  return bytesRead > 0 ? true : false;
402  }
403  else
404  {
405  // had an error, so raise it
406  errInfo = errno;
407  return false;
408  }
409  }
410  else
411  {
412  // update the positions
413  filePointer += blockRead;
414  bufferedInput = blockRead;
415  bufferPosition = 0;
416  }
417  }
418 
419  // see how much we can copy
420  size_t blocksize = (size_t)(len > bufferedInput - bufferPosition ? bufferedInput - bufferPosition : len);
421  memcpy(buf, buffer + bufferPosition, blocksize);
422  // and adjust all of the positions
423  bufferPosition += blocksize;
424  buf += blocksize;
425  len -= blocksize;
426  bytesRead += blocksize;
427  }
428  }
429  else
430  {
431  while (len > 0)
432  {
433  int blockRead = ::read(fileHandle, buf + bytesRead, (unsigned int)len);
434  if (blockRead <= 0)
435  {
436  // not get anything?
437  if (blockRead == 0)
438  {
439  fileeof = true;
440  // could have had an ungetchar
441  return bytesRead > 0 ? true : false;
442  }
443  else
444  {
445  // had an error, so raise it
446  errInfo = errno;
447  return false;
448  }
449  }
450  // update the length
451  len -= blockRead;
452  bytesRead += blockRead;
453  }
454  }
455  return true;
456 }
457 
458 
459 
460 
461 /**
462  * write data to the stream
463  *
464  * @param data The data buffer to write.
465  * @param len The length to write
466  * @param bytesWritten
467  * The number bytes actually written (return value).
468  *
469  * @return true if the write succeeded, false for any errors.
470  */
471 bool SysFile::write(const char *data, size_t len, size_t &bytesWritten)
472 {
473  // writing zero bytes is a NOP
474  if (len == 0)
475  {
476  return true;
477  }
478  // are we buffering?
479  if (buffered)
480  {
481  // using the buffer for input at the moment?
482  if (!writeBuffered)
483  {
484  // We need to position the file write pointer to the postion of our
485  // last virtual read.
487  // set the absolute position
488  lseek64(fileHandle, offset, SEEK_SET);
489  bufferedInput = 0;
490  bufferPosition = 0;
491  // we're switching modes.
492  writeBuffered = true;
493  }
494 
495  // is this too large to bother copying into the buffer?
496  if (len > bufferSize)
497  {
498  // flush an existing data from the buffer
499  flush();
500  // write this out directly
501  int written = ::write(fileHandle, data, (unsigned int)len);
502  // oh, oh...got a problem
503  if (written <= 0)
504  {
505  // save the error status and bail
506  errInfo = errno;
507  return false;
508  }
509  bytesWritten = written;
510  // update the real output position
511  filePointer += written;
512  return true;
513  }
514 
515  bytesWritten = len;
516  // ok, we have can fit in the buffer, but we might need to do this
517  // in chunks
518  while (len > 0)
519  {
520  // is the buffer full?
521  if (bufferPosition == bufferSize)
522  {
523  // flush the buffer now
524  flush();
525  }
526 
527  // append to the buffer
528  size_t blocksize = (size_t)(len > bufferSize - bufferPosition ? bufferSize - bufferPosition : len);
529  memcpy(buffer + bufferPosition, data, blocksize);
530  // and adjust all of the position pointers
531  bufferPosition += blocksize;
532  data += blocksize;
533  len -= blocksize;
534  }
535  return true;
536  }
537  else
538  {
539  // not a transient stream?
540  if (!transient)
541  {
542  // opened in append mode?
543  if ((flags & O_APPEND) != 0)
544  {
545  // seek to the end of the file, return if there is an error
546  if (lseek64(fileHandle, 0, SEEK_END) < 0)
547  {
548  errInfo = errno;
549  return false;
550  }
551  }
552  // write the data
553  int written = ::write(fileHandle, data, (unsigned int)len);
554  if (written <= 0)
555  {
556  // return error status if there was a problem
557  errInfo = errno;
558  return false;
559  }
560 
561  bytesWritten = written;
562  }
563  else
564  {
565  // write the data
566  int written = ::write(fileHandle, data, (unsigned int)len);
567  if (written <= 0)
568  {
569  // return error status if there was a problem
570  errInfo = errno;
571  return false;
572  }
573 
574  bytesWritten = written;
575  }
576  }
577  return true;
578 }
579 
580 bool SysFile::putChar(char ch)
581 {
582  size_t len;
583  return write(&ch, 1, len);
584 }
585 
586 bool SysFile::ungetc(char ch)
587 {
588  ungetchar = ((int)ch) & 0xff;
589  return true;
590 }
591 
592 bool SysFile::getChar(char &ch)
593 {
594  size_t len;
595 
596  return read(&ch, 1, len);
597 }
598 
599 bool SysFile::puts(const char *data, size_t &len)
600 {
601  return write(data, strlen(data), len);
602 }
603 
604 /**
605  * Write a line to the stream, adding the platform specific
606  * line terminator.
607  *
608  * @param mybuffer Start of the line to write.
609  * @param len The length to write.
610  * @param bytesWritten
611  * The actual number of bytes written, including the line
612  * terminator.
613  *
614  * @return A success/failure indicator.
615  */
616 bool SysFile::putLine(const char *mybuffer, size_t len, size_t &bytesWritten)
617 {
618  // this could be a null line...don't try to write zero bytes
619  if (len > 0)
620  {
621  if (!write(mybuffer, len, bytesWritten))
622  {
623  return false;
624  }
625  }
626  size_t termlen;
627  if (puts(LINE_TERMINATOR, termlen))
628  {
629  bytesWritten += termlen;
630  return true;
631  }
632  return false;
633 }
634 
635 
636 bool SysFile::gets(char *mybuffer, size_t bufferLen, size_t &bytesRead)
637 {
638  size_t i;
639  for (i = 0; i < bufferLen - 1; i++)
640  {
641  size_t len;
642 
643  // if we don't get a character break out of here.
644  if (!this->read(mybuffer + i, 1, len))
645  {
646  break;
647  }
648 
649  // we only look for a newline character. On return, this
650  // line will have the terminator characters at the end, or
651  // if the buffer fills up before we find the terminator,
652  // this will just be null terminated. If this us a multi
653  // character line terminator, both characters will appear
654  // at the end of the line.
655  if (mybuffer[i] == '\n')
656  {
657  // once we hit a new line character, back up and see if the
658  // previous character is a carriage return. If it is, collapse
659  // it to the single line delimiter.
660  if (i >= 1 && mybuffer[i - 1] == '\r')
661  {
662  i--;
663  mybuffer[i] = '\n';
664  }
665  i++; // we need to step the position so that the null terminator doesn't overwrite
666  break;
667  }
668  }
669 
670  // if there is no data read at all, this is an eof failure;
671  if (i == 0)
672  {
673  return false;
674  }
675 
676  // null terminate, set the length, and return
677  mybuffer[i] = '\0';
678  // this is the length minus the terminating null
679  bytesRead = i;
680  // return an error state, but not EOF status.
681  return !error();
682 }
683 
684 /**
685  * Count the number of lines in the stream, starting from the
686  * current file pointer position.
687  *
688  * @param count The returned line count.
689  *
690  * @return The success/failure indicator.
691  */
693 {
694  int64_t counter = 0;
695  size_t bytesRead;
696 
697  while (nextLine(bytesRead))
698  {
699  if (bytesRead == 0)
700  {
701  count = counter;
702  return true;
703  }
704  counter++;
705  }
706 
707  return false;
708 }
709 
710 /**
711  * Count the number of lines between two character positions.
712  *
713  * @param start The starting offset.
714  * @param end The ending offset
715  * @param lastLine The starting offset of the last line before the end position.
716  * @param count The returned offset
717  *
718  * @return The success/failure indicator
719  */
720 bool SysFile::countLines(int64_t start, int64_t end, int64_t &lastLine, int64_t &count)
721 {
722  // go to the target location, if possible
723  if (!seek(start, SEEK_SET, start))
724  {
725  return false;
726  }
727 
728  int64_t counter = 0;
729  size_t bytesRead;
730 
731  while (nextLine(bytesRead))
732  {
733  lastLine = start;
734  // hit an eof? we're done counting, return
735  if (bytesRead == 0)
736  {
737  count = counter;
738  return true;
739  }
740  counter++;
741  start += bytesRead;
742  // have we reached our end point?
743  if (start > end)
744  {
745  count = counter;
746  return true;
747  }
748  }
749 
750  return false;
751 }
752 
753 /**
754  * Move to the beginning of the next line in the stream, returning
755  * a count of the bytes moved forward.
756  *
757  * @param bytesRead The returned byte count for the line (including the terminators).
758  *
759  * @return True if this was processed ok, false for any errors.
760  */
761 bool SysFile::nextLine(size_t &bytesRead)
762 {
763  size_t len = 0;
764 
765  for (;;)
766  {
767  char ch;
768  // if we don't get a character break out of here.
769  if (!getChar(ch))
770  {
771  break;
772  }
773  len++; // count this
774  // found our newline character?
775  if (ch == '\n')
776  {
777  break;
778  }
779  }
780 
781  // this is the length including the line terminators
782  bytesRead = len;
783  // return an error state, but not EOF status.
784  return !error();
785 }
786 
787 bool SysFile::seekForwardLines(int64_t startPosition, int64_t &lineCount, int64_t &endPosition)
788 {
789  // make sure we flush any output data
790  flush();
791 
792  // get a buffer for searching
793  char *mybuffer = (char *)malloc(LINE_POSITIONING_BUFFER);
794  if (mybuffer == NULL)
795  {
796  errInfo = ENOMEM;
797  return false;
798  }
799 
800  for (;;)
801  {
802  int readLength = LINE_POSITIONING_BUFFER;
803 
804  // This is likely due to hitting the end-of-file. We'll just
805  // return our current count and indicate this worked.
806  if (!setPosition(startPosition, startPosition))
807  {
808  free(mybuffer);
809  // set the return position and get outta here
810  endPosition = startPosition;
811  return true;
812  }
813 
814  size_t bytesRead;
815  if (!read(mybuffer, readLength, bytesRead))
816  {
817  free(mybuffer);
818  // if we've hit an eof condition, this is the end
819  if (atEof())
820  {
821  // set the return position and get outta here
822  endPosition = startPosition;
823  return true;
824  }
825  // read error,
826  return false;
827  }
828  // have we hit the eof?
829  if (bytesRead == 0)
830  {
831  free(mybuffer);
832  // set the return position and get outta here
833  endPosition = startPosition;
834  return true;
835  }
836 
837 
838  size_t offset = 0;
839  while (offset < bytesRead)
840  {
841  // we're only interested in \n character, since this will
842  // mark the transition point between lines.
843  if (mybuffer[offset] == '\n')
844  {
845  // reduce the line count by one.
846  lineCount--;
847  // reached the requested point?
848  if (lineCount == 0)
849  {
850  // set the return position and get outta here
851  endPosition = startPosition + offset + 1;
852  free(mybuffer);
853  return true;
854  }
855  }
856  // step to the next character;
857  offset++;
858  }
859  // move the start position...if at the end, we might not
860  // get a full buffer
861  startPosition += bytesRead;
862  }
863 }
864 
865 
866 bool SysFile::setPosition(int64_t location, int64_t &position)
867 {
868  // have a pending write?
869  if (writeBuffered)
870  {
871  // flush any pending data
872  flush();
873  // reset all buffer pointers
874  writeBuffered = false;
875  bufferPosition = 0;
876  bufferedInput = 0;
877  }
878 
879 
880  // is this location within the buffer bounds?
881  if (location >= (filePointer - bufferedInput) && location < filePointer)
882  {
883  // just shift the buffer position;
884  bufferPosition = (size_t)(location - (filePointer - (int64_t)bufferedInput));
885  // just return the same value
886  position = location;
887  }
888  else
889  {
890  // go to the absolute position
891  position = lseek64(fileHandle, location, SEEK_SET);
892  // this return the error indicator?
893  if (position == -1)
894  {
895  errInfo = errno;
896  return false;
897  }
898 
899  // reset all of the buffer information and the current file pointer.
900  bufferPosition = 0;
901  bufferedInput = 0;
902  filePointer = position;
903  }
904  return true;
905 }
906 
907 bool SysFile::seek(int64_t offset, int direction, int64_t &position)
908 {
909  // we need special processing if buffered
910  if (buffered)
911  {
912  switch (direction)
913  {
914  case SEEK_SET:
915  return setPosition(offset, position);
916 
917  case SEEK_CUR:
918  return setPosition(filePointer - bufferedInput + bufferPosition + offset, position);
919 
920  case SEEK_END:
921  int64_t fileSize;
922  if (getSize(fileSize))
923  {
924  return setPosition(fileSize - offset, position);
925  }
926  return false;
927 
928  default:
929  return false;
930  }
931  }
932  else
933  {
934  switch (direction)
935  {
936  case SEEK_SET:
937  position = lseek64(fileHandle, offset, SEEK_SET);
938  break;
939 
940  case SEEK_CUR:
941  position = lseek64(fileHandle, offset, SEEK_CUR);
942  break;
943 
944  case SEEK_END:
945  position = lseek64(fileHandle, offset, SEEK_END);
946  break;
947 
948  default:
949  return false;
950  }
951 
952  // this return the error indicator?
953  if (position == -1)
954  {
955  errInfo = errno;
956  return false;
957  }
958  }
959  return true;
960 }
961 
963 {
964  // we need special processing if we have anything in the
965  // buffer right now
966  if (buffered && !(writeBuffered && bufferPosition == 0))
967  {
968  // just return the current buffer position
970  }
971  else
972  {
973  // get the stream postion
974  position = lseek64(fileHandle, 0, SEEK_CUR);
975  if (position == -1)
976  {
977  return false;
978  }
979  }
980  return true;
981 }
982 
983 /**
984  * Retrieve the size of the stream. If the stream is open,
985  * it returns the stream size. Zero is returned if this
986  * stream is not a regular file.
987  *
988  * @param size The returned size value.
989  *
990  * @return True if the size was retrievable, false otherwise.
991  */
993 {
994  // are we open?
995  if (fileHandle >= 0)
996  {
997  // we might have pending output that might change the size
998  flush();
999  // have a handle, use fstat() to get the info
1000  struct stat64 fileInfo;
1001  if (fstat64(fileHandle, &fileInfo) == 0)
1002  {
1003  // regular file? return the defined size
1004  if ((fileInfo.st_mode & S_IFREG) != 0)
1005  {
1006  size = fileInfo.st_size;
1007  }
1008  else
1009  {
1010  size = 0;
1011  }
1012  return true;
1013  }
1014  }
1015  return false;
1016 }
1017 
1018 /**
1019  * Retrieve the size of a file from the file name. If the
1020  * name is a device, it zero is returned.
1021  *
1022  * @param size The returned size value.
1023  *
1024  * @return True if the size was retrievable, false otherwise.
1025  */
1026 bool SysFile::getSize(const char *name, int64_t &size)
1027 {
1028  // the handle is not active, use the name
1029  struct stat64 fileInfo;
1030  if (stat64(name, &fileInfo) == 0)
1031  {
1032  // regular file? return the defined size
1033  if ((fileInfo.st_mode & S_IFREG) != 0)
1034  {
1035  size = fileInfo.st_size;
1036  }
1037  else
1038  {
1039  size = 0;
1040  }
1041  return true;
1042  }
1043  return false;
1044 }
1045 
1046 /**
1047  * Retrieve the size of the stream. If the stream is open,
1048  * it returns the stream size. Zero is returned if this
1049  * stream is not a regular file.
1050  *
1051  * @param size The returned size value.
1052  *
1053  * @return True if the size was retrievable, false otherwise.
1054  */
1055 bool SysFile::getTimeStamp(const char *&time)
1056 {
1057  time = ""; // default return value
1058  // are we open?
1059  if (fileHandle >= 0)
1060  {
1061  // have a handle, use fstat() to get the info
1062  struct stat64 fileInfo;
1063  if (fstat64(fileHandle, &fileInfo) == 0)
1064  {
1065  // regular file? return the defined size
1066  if ((fileInfo.st_mode & S_IFREG) != 0)
1067  {
1068  time = ctime(&fileInfo.st_mtime);
1069  }
1070  }
1071  }
1072  return false;
1073 }
1074 
1075 /**
1076  * Retrieve the size of a file from the file name. If the
1077  * name is a device, it zero is returned.
1078  *
1079  * @param size The returned size value.
1080  *
1081  * @return True if the size was retrievable, false otherwise.
1082  */
1083 bool SysFile::getTimeStamp(const char *name, const char *&time)
1084 {
1085  time = ""; // default return value
1086  // the handle is not active, use the name
1087  struct stat64 fileInfo;
1088  if (stat64(name, &fileInfo) == 0)
1089  {
1090  // regular file? return the defined size
1091  if ((fileInfo.st_mode & (S_IFREG | S_IFDIR)) != 0)
1092  {
1093  time = ctime(&fileInfo.st_mtime);
1094  }
1095  return true;
1096  }
1097  return false;
1098 }
1099 
1100 
1101 /**
1102  * Determine the stream characteristics after an open.
1103  */
1105 {
1106  transient = false;
1107  device = false;
1108  isTTY = false;
1109  writeable = false;
1110  readable = false;
1111 
1112  if (isatty(fileHandle))
1113  {
1114  transient = true;
1115  device = true;
1116  isTTY = true;
1117  }
1118  // have a handle, use fstat() to get the info
1119  struct stat64 fileInfo;
1120  if (fstat64(fileHandle, &fileInfo) == 0)
1121  {
1122  // character device? set those characteristics
1123  if ((fileInfo.st_mode & S_IFCHR) != 0)
1124  {
1125  device = true;
1126  transient = true;
1127  }
1128 
1129  if ((fileInfo.st_mode & S_IWRITE) != 0)
1130  {
1131  writeable = true;
1132  }
1133 
1134  if ((fileInfo.st_mode & S_IREAD) != 0)
1135  {
1136  readable = true;
1137  }
1138  // tagged as FIFO, then this is also a transient
1139  if ((fileInfo.st_mode & S_IFIFO) != 0)
1140  {
1141  transient = true;
1142  }
1143  }
1144 }
1145 
1146 /**
1147  * Set a SysFile object to be the standard output stream.
1148  */
1150 {
1151  // set the file handle
1153  // we didn't open this.
1154  openedHandle = false;
1155  ungetchar = -1; // -1 indicates no char
1157  setBuffering(false, 0);
1158  readable = true; // force this to readable
1159  // STDIN is buffered by default so make it unbuffered
1160  setbuf(stdin, NULL);
1161 }
1162 
1163 /**
1164  * Set a SysFile object to the standard output stream.
1165  */
1167 {
1168  // set the file handle
1170  // we didn't open this.
1171  openedHandle = false;
1172  ungetchar = -1; // -1 indicates no char
1174  setBuffering(false, 0);
1175  writeable = true; // force this to writeable
1176 
1177  // STDOUT is buffered by default so make it unbuffered
1178  setbuf(stdout, NULL);
1179 }
1180 
1181 /**
1182  * Set a SysFile object to the stderr stream.
1183  */
1185 {
1186  // set the file handle
1188  // we didn't open this.
1189  openedHandle = false;
1190  ungetchar = -1; // -1 indicates no char
1192  setBuffering(false, 0);
1193  writeable = true; // force this to writeable
1194 
1195  // STDERR is not buffered by default so no need to do anything
1196 }
1197 
1198 
1199 /**
1200  * Check to see if a stream still has data.
1201  *
1202  * @return True if data can be read from the stream, false otherwise.
1203  *
1204  * @remarks TTY devices require special handling. But, if stdin is opened
1205  * through a pipe, it won't be marked as a TTY, so we need to check for
1206  * that also.
1207  */
1209 {
1210  // not available for reads? Can't have data
1211  if (!readable)
1212  {
1213  return false;
1214  }
1215 
1216  if (isTTY || (isStdIn() && !hasBufferedInput()))
1217  {
1218  int bytesWaiting;
1219  ioctl(fileHandle, FIONREAD, &bytesWaiting);
1220  if (bytesWaiting)
1221  {
1222  return true;
1223  }
1224  else
1225  {
1226  return false;
1227  }
1228  }
1229 
1230  // we might have something buffered, but also check the
1231  // actual stream.
1232  //return !atEof();
1233  bool b = hasBufferedInput();
1234  if (!b && fileeof)
1235  {
1236  int64_t fileSize;
1237  getSize(fileSize);
1238  return false;
1239  }
1240  return true;
1241 
1242 }
void setStdIn()
bool putChar(char ch)
bool putLine(const char *buffer, size_t len, size_t &bytesWritten)
bool writeable
char * buffer
bool openedHandle
bool atEof()
size_t bufferSize
const char * filename
bool isStdIn()
void setStdErr()
bool gets(char *buffer, size_t len, size_t &bytesRead)
bool nextLine(size_t &bytesRead)
bool getSize(int64_t &size)
bool seek(int64_t offset, int direction, int64_t &position)
bool seekForwardLines(int64_t startPosition, int64_t &lineCount, int64_t &endPosition)
bool hasBufferedInput()
@ LINE_POSITIONING_BUFFER
@ DEFAULT_BUFFER_SIZE
bool flush()
int64_t filePointer
static const int stdinHandle
bool buffered
static const int stderrHandle
int fileHandle
bool close()
bool puts(const char *data, size_t &bytesWritten)
bool getPosition(int64_t &position)
bool fileeof
size_t bufferPosition
void setBuffering(bool buffer, size_t length)
bool getTimeStamp(const char *&time)
static const int stdoutHandle
bool open(const char *name, int openFlags, int openMode, int shareMode)
bool getChar(char &ch)
bool read(char *buf, size_t len, size_t &bytesRead)
bool error()
bool writeBuffered
bool countLines(int64_t &count)
bool write(const char *data, size_t len, size_t &bytesWritten)
bool setPosition(int64_t location, int64_t &position)
bool hasData()
bool readable
size_t bufferedInput
void getStreamTypeInfo()
bool ungetc(char ch)
void setStdOut()
void reset()
#define LINE_TERMINATOR
#define RX_O_APPEND
signed __int64 int64_t