/*
* Copyright 1995, 1996 Perforce Software. All rights reserved.
*
* This file is part of the Library RCS. See rcstest.c.
*/
/*
* rcsmeta.c - print out RCS metadata, given an RcsArchive structure
*
* Classes defined:
*
* RcsGenMeta - control block for generating metadata
*
* Public methods:
*
* RcsGenMeta::RcsGenMeta -- set up for metadata generation
* RcsGenMeta::Generate - write metadata to stdio file
*
* Private structures:
*
* RcsRevMeta - metadata info about a single rev
*
* History:
* 2-18-97 (seiwald) - translated to C++.
*/
# define NEED_TYPES
# include <stdhdrs.h>
# include <strbuf.h>
# include <error.h>
# include <errorlog.h>
# include <filesys.h>
# include "readfile.h"
# include "rcsarch.h"
# include "rcsrev.h"
# include "rcsgen.h"
# include "rcsdate.h"
# include "rcsmeta.h"
/* CHEAT! from dmtypes.h! */
enum DmtActionType {
DAT_ADD = 0,
DAT_EDIT = 1,
DAT_DELETE = 2,
DAT_BRANCH = 3,
DAT_INTEG = 4
} ;
/*
* Generate output of the form:
*
* rev:
* "rev" //depot/branch/path rev ishead action
* change-date change-code change-user
* //depot/IMPORT/arch-path arch-rev
*
* label:
* "label" label //label/branch/path //depot/branch/path rev change-date
*
* change:
* "change" change-date change-code change-user @text@
*
*/
struct RcsRevMeta
{
const char *branch;
const char *revName;
int revNum;
int isHead;
int action;
} ;
RcsGenMeta::RcsGenMeta(
FileSys *wf,
RcsArchive *ar,
char *archName )
{
this->wf = wf;
this->ar = ar;
this->archName = archName;
/* In the attic? */
char *a = archName;
while( ( a = strchr( a, 'A' ) ) && strncmp( a, "Attic/", 6 ) )
++a;
if( isDead = ( a != 0 ) )
{
strncpy( path, archName, a - archName );
strcpy( path + ( a - archName ), a + 6 );
}
else
{
strcpy( path, archName );
}
}
void
RcsGenMeta::Generate(
Error *e )
{
RcsRevMeta rm;
/* (Recursively) dump out the trunk */
rm.branch = "main";
rm.revName = ar->headRev.text;
rm.isHead = 1;
rm.revNum = 1;
rm.action = DAT_ADD;
Trunk( &rm, e );
AssertLog.Report( e );
}
void
RcsGenMeta::Trunk(
RcsRevMeta *rm,
Error *e )
{
RcsRev *rev;
/* Find the rev */
rev = ar->FindRevision( rm->revName, e );
if( e->Test() )
return;
/* Figure action */
rm->action = DAT_EDIT;
/* Recurse, to count down to the trunk. */
if( rev->next.text )
{
RcsRevMeta nrm[1];
nrm->branch = rm->branch;
nrm->isHead = 0;
nrm->revName = rev->next.text;
nrm->revNum = 1;
nrm->action = DAT_EDIT; /* reset later */
Trunk( nrm, e );
rm->revNum += nrm->revNum;
if( e->Test() )
return;
/* If nrm shows the previous rev was deleted, we'll make this */
/* rev an add. */
if( nrm->action == DAT_DELETE )
rm->action = DAT_ADD;
}
else
{
rm->action = DAT_ADD;
}
/* Dump out this rev. */
Rev( rm, rev );
/* Now, dump out branches */
Branches( rm, rev, e );
}
void
RcsGenMeta::Branch(
RcsRevMeta *rm,
Error *e )
{
RcsRev *rev;
/* Find the rev */
rev = ar->FindRevision( rm->revName, e );
if( e->Test() )
return;
/* Action always EDIT, since BRANCH is handled by RcsMetaBranches */
/* actually, always EDIT */
rm->isHead = !rev->next.text;
rm->action = DAT_EDIT;
/* Dump out this rev. */
Rev( rm, rev );
/* Now, dump out branches */
Branches( rm, rev, e );
if( e->Test() )
return;
/* Recurse to next */
if( rev->next.text )
{
RcsRevMeta nrm[1];
nrm->branch = rm->branch;
nrm->revName = rev->next.text;
nrm->revNum = rm->revNum + 1;
/* isHead computed later */
/* action computed later */
Branch( nrm, e );
}
}
void
RcsGenMeta::Branches(
RcsRevMeta *rm,
RcsRev *rev,
Error *e )
{
RcsList *s;
for( s = rev->branches; s; s = s->qNext )
{
RcsList *t;
RcsRevMeta nrm[1];
char *r = s->revision.text;
/* Find the symbol for this branch */
for( t = ar->symbolList; t; t = t->qNext )
if( RcsRevCmp( r, t->revision.text ) == REV_CMP_NUM_BRANCH )
break;
if( !t )
{
fprintf( stderr, "%s/%s has no branch\n", archName, r );
continue;
}
/* Generate branch point -- sames as orig */
nrm->branch = t->string.text;
nrm->revName = rm->revName;
nrm->revNum = 1;
nrm->isHead = 0;
nrm->action = DAT_BRANCH;
Rev( nrm, rev );
/* Now generate remaining revs 2+ in branch */
nrm->branch = t->string.text;
nrm->revName = s->revision.text;
nrm->revNum = 2;
nrm->isHead = 0; /* dummy */
nrm->action = DAT_EDIT; /* dummy */
Branch( nrm, e );
if( e->Test() )
return;
/* Generate integration record & reverse integ record. */
GenString( "integ" );
File3( "depot", t->string.text, path ); /* to */
File3( "depot", rm->branch, path ); /* from */
GenNum( 0 ); /* startFromRev */
GenNum( rm->revNum ); /* endFromRev */
GenNum( 1 ); /* toRev */
GenNum( 2 ); /* BRANCH */
GenNum( 1 ); /* commited */
GenNum( 1 ); /* resolved */
GenNum( 0 ); /* changeCode */
GenNL();
GenString( "integ" );
File3( "depot", rm->branch, path ); /* from */
File3( "depot", t->string.text, path ); /* to */
GenNum( 0 ); /* startFromRev */
GenNum( 1 ); /* endFromRev */
GenNum( rm->revNum ); /* toRev */
GenNum( 3 ); /* BRANCH_R */
GenNum( 1 ); /* commited */
GenNum( 1 ); /* resolved */
GenNum( 0 ); /* changeCode */
GenNL();
}
}
void
RcsGenMeta::Rev(
RcsRevMeta *rm,
RcsRev *rev )
{
/* Dump out a revision */
RcsList *s;
int changeDate;
unsigned int changeCode;
RcsDate date;
/* Turn action of "dead" rev's into delete. */
if( !strcmp( rev->state.text, "dead" ) || isDead && rm->isHead )
rm->action = DAT_DELETE;
/* Hash the change text. */
{
RcsSize len;
changeCode = 0;
rev->log.file->Seek( rev->log.offset );
for( len = rev->log.length; len && !rev->log.file->Eof(); --len )
{
changeCode = ( changeCode * 2903657531u )
^ rev->log.file->Char() ;
rev->log.file->Next();
}
}
/* And get the date */
date.Set( rev->date.text );
changeDate = date.Parse();
/* Dump change */
GenString( "change" );
GenNum( changeDate );
GenNum( changeCode );
GenString( rev->author.text );
GenChunk( &rev->log, 0 );
GenNL();
/* Dump rev */
GenString( "rev" );
File3( "depot", rm->branch, path );
GenNum( rm->revNum );
GenNum( rm->isHead );
GenNum( rm->action );
GenNum( changeDate );
File3( "depot", "IMPORT", archName );
GenString( rev->revName.text );
GenNum( changeCode );
GenString( rev->author.text );
GenNL();
/* Dump labels if there are any. */
for( s = ar->symbolList; s; s = s->qNext )
{
char label[ 64 ]; /* XXX DmtLDomain */
switch( RcsRevCmp( rev->revName.text, s->revision.text ) )
{
case REV_CMP_EQUAL:
/* Truncate label at 63 chars */
strncpy( label, s->string.text, sizeof( label ) );
label[sizeof(label)-1] = 0;
/* Symbol for this rev - write label entry */
GenString( "label" );
GenString( label );
File3( label, rm->branch, path );
File3( "depot", rm->branch, path );
GenNum( rm->revNum );
GenNum( changeDate );
GenNL();
break;
}
}
}
void
RcsGenMeta::File3(
const char *domain,
const char *branch,
const char *path )
{
char buf[1024];
sprintf( buf, "//%s/%s/%s", domain, branch, path );
GenString( buf );
}