/*
* Copyright 1995, 1996 Perforce Software. All rights reserved.
*
* This file is part of the Library RCS. See rcstest.c.
*/
/*
* rcsci.c - update RcsArchive with a new revision
*
* Classes defined:
*
* RcsCkin - control block for checkin operation
*
* Public methods:
*
* RcsCkin::RcsCkin() - create/repalce a head/post-head revision
* RcsCkin::~RcsCkin() - free up resources created by RcsCkin()
*
* Internal routines:
*
* ReadFileChunk() - open a file and point a chunk at it
*
* History:
* 2-18-97 (seiwald) - translated to C++.
*/
# define NEED_FILE
# define NEED_TYPES
# include <stdhdrs.h>
# include <error.h>
# include <debug.h>
# include <strbuf.h>
# include <readfile.h>
# include <filesys.h>
# include "rcsdebug.h"
# include "rcsarch.h"
# include "rcsci.h"
# include "rcsdiff.h"
# include "rcsdate.h"
# include "rcsrev.h"
# include <msglbr.h>
/*
* RcsCkinChunk -- a rev chunk, either diff or whole
*/
RcsCkinChunk::RcsCkinChunk()
{
diffFile = 0;
diffFileBinary = 0;
}
/*
* RcsCkinChunk::~RcsCkinChunk() - free up resources
*
* This should only be done after the RcsArchive has been written
* with RcsGenerate, as the RcsArchive now has chunks pointing to
* files opened by RcsCkin().
*/
RcsCkinChunk::~RcsCkinChunk()
{
delete file;
delete diffFile;
delete diffFileBinary;
}
/*
* RcsCkinChunk::Diff() - produce a diff chunk between newFile and revName
*/
void
RcsCkinChunk::Diff(
RcsArchive *archive,
const char *revName,
FileSys *newFile,
int flags,
Error *e )
{
/* Create temp file name */
diffFile = FileSys::CreateGlobalTemp( FST_TEXT );
/* Pipe previous revision through diff and into temp file */
RcsDiff( archive, revName, newFile, diffFile->Name(),
flags|RCS_DIFF_RCSN, e );
if( e->Test() )
return;
/* Make currRev now a diff chunk */
/* First we need to create a new FileSys, so when */
/* we read the file we read it binary. */
diffFileBinary = FileSys::Create( FST_BINARY );
diffFileBinary->Set( diffFile->Name() );
Save( diffFileBinary, e );
if( e->Test() )
return;
if( !file->Size() && archive->GetChunkCnt() != 0 )
e->Set( MsgLbr::Mangled ) << "empty";
/* Make sure it looks like diff output, and not */
/* "binary files differ" */
if( !file->Eof() &&
file->Char() != 'a' &&
file->Char() != 'd' )
{
char buf[ 128 ];
int l = file->Memccpy( buf, '\n', sizeof( buf )-1 );
if( l && buf[ l - 1 ] == '\n' ) --l;
buf[ l ] = 0;
e->Set( MsgLbr::Mangled ) << buf;
}
return;
}
/*
* RcsCkin::RcsCkin() - create a post-head revision/replace the head revision
*
* Takes a file containing the new text and the new revision's name
* and doctors the RcsArchive structure to make that revision the
* new head. This currently supports replacing the head, but doesn't
* allow checkins on branches and it doesn't validate the revision number.
*/
RcsCkin::RcsCkin(
RcsArchive *archive,
FileSys *newFile,
const char *newRev,
RcsChunk *log,
const char *author,
const char *state,
time_t modTime,
Error *e )
{
RcsDate date;
RcsRevPlace place;
/*
* Use RcsRevPlace to figure out this rev's relationship to
* existing revs. The 'place' flag says whether its on the trunk
* or on a branch. The three fields prevRev, currRev, and nextRev
* point to the previous, current, and following revisions in terms
* of RCS lineage.
*/
if( !place.Locate( archive, newRev, e ) )
goto fail;
if( DEBUG_CKIN )
p4debug.printf( "checkin new %s following %s\n",
place.currRev ? place.currRev->revName.text : "none",
place.nextRev ? place.nextRev->revName.text : "none" );
/*
* If there is no current rev entry, create one.
*/
if( !place.currRev )
place.currRev = archive->AddRevision( newRev, 0, place.prevRev, e );
if( e->Test() )
goto fail;
/*
* Fill in the new rev with the new stuff: point its chunk
* at the new file.
*/
if( !*author || !*state )
{
e->Set( MsgLbr::Empty );
goto fail;
}
if( modTime )
date.Set( modTime );
else
date.Now();
place.currRev->date.Save( date.Text(), "date" );
place.currRev->author.Save( author, "author" );
place.currRev->state.Save( state, "state" );
place.currRev->log = *log;
/*
* Create chunk for nextRev.
*
* It will be the diff between the newFile and the nextRev.
*/
if( place.nextRev )
{
nextChunk.Diff( archive, place.nextRev->revName.text,
newFile, RCS_DIFF_REVERSE, e );
place.nextRev->text = nextChunk;
}
/*
* Create chunk for current rev.
*
* If it is a new head, its contents will be the whole file.
* If it follows an existing rev, its contents will be a diff.
*/
if( !place.prevRev )
{
currChunk.Save( newFile, e );
place.currRev->text = currChunk;
}
else
{
currChunk.Diff( archive, place.prevRev->revName.text,
newFile, RCS_DIFF_NORMAL, e );
place.currRev->text = currChunk;
}
if( e->Test() )
goto fail;
/*
* Touch up the head/next pointers along the way.
*/
if( place.prevRev )
{
place.prevRev->next.Save( place.currRev->revName, "next" );
}
else
{
archive->headRev.Save( place.currRev->revName, "head" );
}
if( place.nextRev )
{
place.currRev->next.Save( place.nextRev->revName, "next" );
}
fail:
if( e->Test() )
e->Set( MsgLbr::Checkin ) << newFile->Name() << newRev;
}