fileiobuf.cc #1

  • //
  • guest/
  • perforce_software/
  • p4/
  • sys/
  • fileiobuf.cc
  • View
  • Commits
  • Open Download .zip Download (8 KB)
/*
 * Copyright 1995, 2003 Perforce Software.  All rights reserved.
 *
 * This file is part of Perforce - the FAST SCM System.
 */

/*
 * fileiobuf.cc -- FileIOBuffer methods
 */

# ifdef USE_EBCDIC
# define NEED_EBCDIC
# endif
# include <stdhdrs.h>

# include <error.h>
# include <strbuf.h>

# include "filesys.h"
# include "fileio.h"

void
FileIOBuffer::Open( FileOpenMode mode, Error *e )
{
	// Start w/ binary open

	FileIOBinary::Open( mode, e );

	// Clear send/receive buffers

	snd = rcv = 0;
}

void
FileIOBuffer::FlushBuffer( Error *e )
{
#if defined( USE_EBCDIC ) && defined( NO_EBCDIC_FILES )
    	__etoa_l( iobuf.Text(), iobuf.Length() );
#endif
	FileIOBinary::Write( iobuf.Text(), snd, e );
	snd = 0;
}

void
FileIOBuffer::SetBufferSize( size_t l )
{
	// You can only do this before you open the file.

	if( fd == -1 )
	    iobuf.SetBufferSize( l );
}

void
FileIOBuffer::Close( Error *e )
{
	// Flush buffers

	while( snd && !e->Test())
		FlushBuffer( e );

	// finish with binary close

	FileIOBinary::Close( e );
}

void
FileIOBuffer::Write( const char *buf, int len, Error *e )
{
	// Write logic: copy whole lines that end in \n,
	// translate the \n to a \r, and arrange so that
	// a \n is added thereafter.

	// addnl: saw a \r, must add a \n

	int addnl = 0;

	while( len || addnl )
	{
	    // If iobuf is full, flush

	    if( snd == iobuf.Length() )
	    {
		    FlushBuffer( e );

		    if( e->Test() )
			    return;
	    }

	    // If we owe a \n because we just sent a \r,
	    // add it now (that we know we have space).

	    if( addnl )
		addnl = 0, iobuf.Text()[ snd++ ] = '\n';

	    // buffer what we can

	    char *p;
	    int l = iobuf.Length() - snd;

	    if( l > len )
		l = len;

	    switch( lineType )
	    {
	    case LineTypeRaw:
	    case LineTypeLfcrlf:
		// Straight copy.
		// LFCRLF writes LF.
		memcpy( iobuf.Text() + snd, buf, l );
		break;

	    case LineTypeCr:
		// Copy out to the next \n.  If we hit one, translate
		// it to a \r.

		if( p = (char *)memccpy( iobuf.Text() + snd, buf, '\n', l ) )
		{
		    p[-1] = '\r';
		    l = p - iobuf.Text() - snd;
		}
		break;

	    case LineTypeCrLf:
		// Copy out to the next \n.  If we hit one, translate
		// it to a \r and set addnl so as to write the \n on 
		// the next loop (when we're sure to have space). 

		if( p = (char *)memccpy( iobuf.Text() + snd, buf, '\n', l ) )
		{
		    p[-1] = '\r';
		    l = p - iobuf.Text() - snd;
		    addnl = 1;
		}
		break;
	    }

	    snd += l;
	    buf += l;
	    len -= l;
	}
}

void
FileIOBuffer::FillBuffer( Error *e )
{
	rcv = FileIOBinary::Read( iobuf.Text(), iobuf.Length(), e );
#if defined( USE_EBCDIC ) && defined( NO_EBCDIC_FILES )
	if( rcv > 0 )
	    __atoe_l( iobuf.Text(), rcv );
#endif
}

int
FileIOBuffer::Read( char *buf, int len, Error *e )
{
	// Read logic: read whole lines that end in \r, and
	// arrange so that a following \n translates the \r
	// into a \n and the \n is dropped.

	// soaknl: we saw a \r, skip this \n

	int ilen = len;
	int soaknl = 0;

	while( len || soaknl )
	{
	    // Nothing in the buffer?

	    if( !rcv )
	    {
		ptr = iobuf.Text();
		FillBuffer( e );

		if( e->Test() )
		    return -1;

		if( !rcv )
		    break;
	    }

	    // Skipping \n because we saw a \r?

	    if( soaknl )
	    {
		if( *ptr == '\n' )
		    ++ptr, --rcv, buf[-1] = '\n';
		soaknl = 0;
	    }

	    // Trim avail to what's needed
	    // Fill user buffer; stop at \r

	    char *p;
	    int l = rcv < len ? rcv : len ;

	    switch( lineType )
	    {
	    case LineTypeRaw:
		// Straight copy.
		memcpy( buf, ptr, l );
		break;

	    case LineTypeCr:
		// Copy to the next \r.  If we hit one, translate
		// it to \n.

		if( p = (char *)memccpy( buf, ptr, '\r', l ) )
		{
		    l = p - buf;
		    p[-1] = '\n';
		}
		break;

	    case LineTypeCrLf:
		// Copy to next \r.  If we hit one, arrange so that
		// if we see \n the next time through (when we know
		// there'll be data in the buffer), we translate this
		// \r to a \n and drop the subsequent \n.
		// LFCRLF reads CRLF.

		if( p = (char *)memccpy( buf, ptr, '\r', l ) )
		{
		    l = p - buf;
		    soaknl = 1;
		}
		break;

	    case LineTypeLfcrlf:

		if( p = (char *)memccpy( buf, ptr, '\r', l ) )
		{
		    l = p - buf;
		    p[-1] = '\n';
		    soaknl = 1;
		}
		break;

	    }

	    ptr += l;
	    rcv -= l;
	    buf += l;
	    len -= l;
	}

	return ilen - len;
}

void
FileIOBuffer::Seek( offL_t pos, Error *e )
{
	if( mode == FOM_WRITE && snd > 0 )
	    FlushBuffer( e );

	if( !e->Test() )
	    FileIOBinary::Seek( pos, e );

	// Clear send/receive buffers

	snd = rcv = 0;
}

offL_t
FileIOBuffer::Tell()
{
	if( mode == FOM_READ )
	    return tellpos - rcv;
	return tellpos + snd;
}

/*
 * This is an optimized version of FileSys::ReadLine to take advantage
 * of the buffering we have.
 *
 * The macro STRICT_LINEENDING sets up the code to strictly follow
 * the specified FileSys line ending, but that does not match the
 * reality of the prior existing FileSys::ReadLine.  I'm submitting
 * with this macro option in case we decide to use this strict case
 * in the future instead of having to rediscover how to do that. - JAA
 *
 * See the test program readlinetest in the tests directory.
 *
 * Return values...
 * 0 if end of file
 * 1 if complete line read
 * -1 partial line read - size excedded before line ended
 */

int
FileIOBuffer::ReadLine( StrBuf *buf, Error *e )
{
	buf->Clear();
	int size = iobuf.Length();

	// Read logic: read whole lines that end in \r, and
	// arrange so that a following \n translates the \r
	// into a \n and the \n is dropped.

	// soaknl: we saw a \r, skip this \n

	int soaknl = 0;
	int linedone = 0;

	while( ( !linedone && buf->Length() < size ) || soaknl )
	{
	    // Nothing in the buffer?

	    if( !rcv )
	    {
		ptr = iobuf.Text();
		FillBuffer( e );

		if( e->Test() || !rcv )
		{
		    if( linedone || buf->Length() > 0 )
			break;	// this really exits 1
		    return 0;
		}
	    }

	    // Skipping \n because we saw a \r?

	    if( soaknl )
	    {
		if( *ptr == '\n' )
		    ++ptr, --rcv;
		soaknl = 0;
	    }

	    // Trim avail to what's needed
	    // Fill user buffer; stop at \r

	    if( linedone || buf->Length() >= size )
		break;

	    char *p;
	    int l = rcv < size ? rcv : size;

	    switch( lineType )
	    {
	    case LineTypeRaw:
		// Straight copy.
		p = (char *)memchr( ptr, '\n', l );
		if( p )
		{
		    l = p - ptr;
		    buf->Extend( ptr, l );
		    l++;
		    linedone = 1;
		}
		else
		    buf->Extend( ptr, l );
		break;

	    case LineTypeCr:
		// Copy to the next \r.

#ifndef STRICT_LINEENDING
		// or the next \n
		if( p = (char *)memchr( ptr, '\n', l ) )
		{
		    l = p - ptr;
		    if( p = (char *)memchr( ptr, '\r', l ) )
			l = p - ptr;
		    buf->Extend( ptr, l );
		    l++;
		    linedone = 1;
		}
		else
#endif
		if( p = (char *)memchr( ptr, '\r', l ) )
		{
		    l = p - ptr;
		    buf->Extend( ptr, l );
		    l++;
		    linedone = 1;
		}
		else
		    buf->Extend( ptr, l );
		break;

	    case LineTypeCrLf:
#ifdef STRICT_LINEENDING
		// Copy to next \r.  If we hit one, arrange so that
		// if we see \n the next time through (when we know
		// there'll be data in the buffer) drop it.

		if( p = (char *)memchr( ptr, '\r', l ) )
		{
		    l = p - ptr;
		    buf->Extend( ptr, l );
		    l++;
		    soaknl = linedone = 1;
		}
		else
		    buf->Extend( ptr, l );
		break;
#else
		// fall through...
#endif

	    case LineTypeLfcrlf:

		if( p = (char *)memchr( ptr, '\n', l ) )
		{
		    l = p - ptr;
		    if( p > ptr && p[-1] == '\r' )
			buf->Extend( ptr, l-1 );
		    else
			buf->Extend( ptr, l );
		    l++;
		    linedone = 1;
		}
		else if( ptr[l-1] == '\r' )
		{
		    buf->Extend( ptr, l-1 );
		    soaknl = linedone = 1;
		}
		else
		    buf->Extend( ptr, l );
		break;

	    }

	    ptr += l;
	    rcv -= l;
	}

	buf->Terminate();

	return linedone ? 1 : -1;
}
# Change User Description Committed
#2 12188 Matt Attaway Move 'main' p4 into a release specific directory in prep for new releases
#1 9129 Matt Attaway Initial commit of the 2014.1 p4/p4api source code