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