#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <string.h>
#include <errno.h>
#include <stdhdrs.h>
#include <strbuf.h>
#include <error.h>
#include "buffile.h"
#include "err.h"
BufFile::BufFile()
{
/*
* Initialize buffer into which the buffered file is read.
*/
buf[ 0 ] = '\0'; // \0 trailing nothing
bufp = buf; // current location is beginning
n = 0; // nothing remaining in buffer
}
void BufFile::SetName( const char *bufname )
{
/*
* Set the name of the buffered file.
*/
name.Set( bufname );
}
void BufFile::SetBfd( const int fd )
{
/*
* Set the file descriptor of the buffered file.
*/
bfd = fd;
}
char *BufFile::GetName()
{
/*
* Get the name of the buffered file.
*/
return name.Text();
}
int BufFile::GetBfd()
{
/*
* Get the file descriptor of the buffered file.
*/
return bfd;
}
bool BufFile::Open( const char *pathname, int flags, Error *e )
{
int fd; // buffered file descriptor
/*
* Set the name of the buffered file.
*/
SetName( pathname );
/*
* Open the buffered file.
*/
if( ( fd = open( pathname, flags ) ) == -1 )
return syserr( "open", GetName(), e );
/*
* Save the buffered file descriptor.
*/
SetBfd( fd );
/*
* No errors encountered.
*/
return true;
}
bool BufFile::DataReady( bool *ready, Error *e )
{
fd_set readfds; // file descriptor of interest
timeval timeout; // timeout value for select() call
int nfds; // file descriptors with data available
if( n )
{
/*
* Data is still buffered; indicate that data is ready.
*/
*ready = true;
/*
* No errors encountered.
*/
return true;
}
/*
* Determine if data is available to be read.
*/
FD_SET( GetBfd(), &readfds );
timeout.tv_sec = 0;
timeout.tv_usec = 0;
if( ( nfds = select( 1, &readfds, 0, 0, &timeout ) ) == -1 )
return syserr( "select", GetName(), e );
if( nfds )
{
/*
* Data is available to be read; indicate that data is ready.
*/
*ready = true;
/*
* No errors encountered.
*/
return true;
}
/*
* Data is not ready.
*/
*ready = false;
/*
* No errors encountered.
*/
return true;
}
bool BufFile::Read( Error *e )
{
/*
* Read the next buffer from the buffered file.
*/
if( ( n = read( GetBfd(), buf, BUFSIZE ) ) == -1 )
{
/*
* Error encountered. If it's not EAGAIN, return the error.
*/
if( errno != EAGAIN )
return syserr( "read", GetName(), e );
/*
* EAGAIN encountered, which means that an O_NONBLOCK read was
* performed. It's up to the caller to determine what to do
* for no data immediately available for reading.
*
* Set the remaining length to reflect that no data was
* immediately available for reading.
*/
n = 0;
}
/*
* Append the trailing \0 and move the pointer to the
* current buffer location to the beginning of the buffer.
*/
buf[ n ] = '\0';
bufp = buf;
return true;
}
bool BufFile::ReadLine( StrBuf *line, bool *eof, Error *e )
{
int len; // length of line up to BUFSIZE
char *lep; // pointer to line ending
bool le; // entire line ending seen
int i;
#ifndef OS_NT
static const char lineend[] = "\n"; // Unix-style line endings
static const int lineend_len = 1;
#else
static const char lineend[] = "\r\n"; // Windows-style line endings
static const int lineend_len = 2;
#endif /* OS_NT */
/*
* Start with empty line.
*/
line->Clear();
/*
* Get line, perhaps requiring multiple reads.
*/
do
{
/*
* Search for line ending.
*/
if( lep = strchr( bufp, lineend[ 0 ] ) )
{
/*
* First character of line ending seen.
*
* Length includes first character of line ending.
*/
len = lep - bufp + 1;
/*
* Append to line from current buffer location
* through first character of line ending.
*/
line->Append( bufp, len );
/*
* Assume entire line ending will be seen.
*/
le = true;
/*
* Ensure entire line ending is present.
*/
for( i = 1; i < lineend_len; i++ )
{
if( bufp - buf + len == BUFSIZE )
{
/*
* At end of buffer; another read is required to
* check for next character in line ending.
*/
if( !Read( e ) )
return false;
/*
* Line being returned is prior to buffer just read.
*/
len = 0;
}
if( bufp[ len ] != lineend[ i ] )
{
/*
* Next character in buffer does not match
* next character in line ending.
*
* Adjust pointer to current buffer location and
* length of remaining buffer for characters
* appended thus far.
*/
bufp += len;
n -= len;
/*
* Entire line ending was not seen; break out of the
* loop and search for the line ending.
*/
le = false;
break;
}
/*
* Append and move past this character of line ending.
*/
line->Append( &bufp[ len ], 1 );
len++;
}
if( !le )
{
/*
* Entire line ending not seen; continue to
* search for the line ending.
*/
continue;
}
/*
* Break out of the loop so as to move to next line and
* return current line.
*/
break;
}
/*
* First character of line ending not seen.
*
* Append to line from current buffer location
* through end of buffer.
*/
line->Append( bufp, n );
/*
* Read next buffer.
*/
if( !Read( e ) )
return false;
}
while( n );
if( n )
{
/*
* Line ending seen.
*/
/*
* Adjust pointer to current buffer location and length
* of remaining buffer for the line being returned.
*/
bufp += len;
n -= len;
/*
* Not at end of file.
*/
*eof = false;
}
else
{
/*
* Line ending not seen and no more characters were read.
*/
/*
* Return EOF if no line being returned.
*/
*eof = !line->Length();
}
/*
* No errors encountered.
*/
return true;
}
bool BufFile::Close( Error *e )
{
/*
* Close the buffered file.
*/
if( close( GetBfd() ) == -1 )
return syserr( "close", GetName(), e );
/*
* No errors encountered.
*/
return true;
}