rcsrev.cc #1

  • //
  • guest/
  • perforce_software/
  • p4/
  • 2014_1/
  • rcs/
  • rcsrev.cc
  • View
  • Commits
  • Open Download .zip Download (7 KB)
/*
 * Copyright 1995, 1996 Perforce Software.  All rights reserved.
 *
 * This file is part of the Library RCS.  See rcstest.c.
 */

/*
 * rcsrev.c - RCS revision number mangling
 *
 * Classes defined
 *
 *	RcsRevPlace - a little handle for finding revision insertion points
 *
 * Methods defined:
 *
 * 	RcsArchive::FollowRevision() - compute successor to current rev
 *
 * 	RcsRevPlace::Locate() - find insertion point for a new revision.
 *
 * External routines:
 *
 *	RcsRevCmp() - compare current rev number with desired one.
 *
 * History:
 *	2-18-97 (seiwald) - translated to C++.
 */

# define NEED_TYPES

# include <stdhdrs.h>

# include <error.h>
# include <debug.h>

# include "rcsdebug.h"
# include "rcsarch.h"
# include "rcsrev.h"
# include <msglbr.h>

static const char *const cmps[] = { 
	"EQUAL", "MISMATCH", "DOWN", "UP", "ATBRANCH", "NUMBRANCH", "OUT", "IN"
};

/*
 * RcsRevCmp() - compare two revs
 *
 * Compares an RCS revision number (c) to another "target" revision
 * number (t).  The comparison is based soley on the text of the two
 * numbers (i.e., does not depend on the topology of a particular RCS
 * revision tree instance). See the comments before the definition of
 * enum RevCmp in rcsrev.h for return values and examples.
 */

RevCmp
RcsRevCmp( 
	const char *t,
	const char *c )
{
	int levels = 0;
	int diff = 0;
	RevCmp r;

	if( DEBUG_REVNUM )
	    p4debug.printf("rev cmp %s %s: ", t, c );

	while( !diff && *t && *c )
	{
	    int t1 = 0, c1 = 0;

	    while( *t >= '0' && *t <= '9' )
		t1 = t1 * 10 + *t++ - '0';
	    if( *t == '.' ) ++t;

	cvshack:
	    while( *c >= '0' && *c <= '9' )
		c1 = c1 * 10 + *c++ - '0';
	    if( *c == '.' ) ++c;

	    /* Special RCS hack for x.y.0.z, use levels mod 2 to catch
	     * just the magic CVS 0 branch case. A 0 rev is OK, 3.0.
	     */

	    if( (!c1 && !(levels%2)) && *c >= '0' && *c <= '9' )
		goto cvshack;

	    diff = t1 - c1;
	    levels++;
	}

	r = REV_CMP_MISMATCH;

	if( !diff )
	{
	    if( !*t && !*c )
		r = REV_CMP_EQUAL;
	    else if( !*c )
		if ( levels % 2 )
		    r = REV_CMP_NUM_BRANCH;
		else
		    r = REV_CMP_AT_BRANCH;
	}
	else if( diff < 0 )
	{
	    if( levels <= 2 )
		r = REV_CMP_DOWN_TRUNK;
	    else if( !( levels % 2 ) )
		r = REV_CMP_IN_BRANCH;
	}
	else if( diff > 0 )
	{
	    if( levels <= 2 )
		r = REV_CMP_UP_TRUNK;
	    else if( !( levels % 2 ) )
		r = REV_CMP_OUT_BRANCH;
	}

	if( DEBUG_REVNUM )
	    p4debug.printf( "%s\n", cmps[r]);

	return r;
}


/*
 * RcsRevFollow() - compute successor to current rev
 *
 * This routine follows one step in the path from the current revision
 * number to the target revision number.  It calls RcsRevCmp() to tell it
 * whether to follow down the trunk or out the branch (both using the
 * 'next' pointer), or to follow a new branch.  In the latter case, it
 * searches the current rev's branch list for the proper branch point.
 *
 * Maybe a few examples will help:
 *
 *	current 1.3 target 1.1 -> next
 *	current 1.3 target 1.2.2.3 -> next 
 *	current 1.3.3.1 target 1.3.3.3 -> next
 *
 *	current 1.2 target 1.2.2.3 -> 1.2.2.1
 *	current 1.3.3.1 target 1.3.3.1.4.3 -> 1.3.3.1.4.1
 */

RcsRev *
RcsArchive::FollowRevision( 
	RcsRev *currentRev, 
	const char *targetRev,
	Error *e )
{
	RcsList *branch;
	RcsRev *nextRev = 0;

	switch( RcsRevCmp( targetRev, currentRev->revName.text ) )
	{
	case REV_CMP_EQUAL:
	    return currentRev;

	case REV_CMP_MISMATCH:
	case REV_CMP_IN_BRANCH:
	case REV_CMP_UP_TRUNK:
	    e->Set( MsgLbr::NoRev ) << targetRev;
	    return 0;

	case REV_CMP_DOWN_TRUNK:
	case REV_CMP_OUT_BRANCH:
	    nextRev = NextRevision( currentRev, e );

	    if( !nextRev )
		return 0;

	    break;

	case REV_CMP_NUM_BRANCH:
	case REV_CMP_AT_BRANCH:
	    for( branch = currentRev->branches; branch; branch = branch->qNext )
	        switch( RcsRevCmp( targetRev, branch->revision.text ) )
	    {
	    case REV_CMP_EQUAL:
	    case REV_CMP_NUM_BRANCH:
	    case REV_CMP_AT_BRANCH:
	    case REV_CMP_OUT_BRANCH:
		nextRev = FindRevision( branch->revision.text, e );
		break;

	    case REV_CMP_MISMATCH:
	    case REV_CMP_UP_TRUNK:
	    case REV_CMP_IN_BRANCH:
	    case REV_CMP_DOWN_TRUNK:	/* I hope not... */
		continue;
	    }

	    if( !nextRev )
	    {
		e->Set( MsgLbr::NoBranch ) << targetRev;
		return 0;
	    }
	    break;
	}

	/* A sanity check for loops */

	switch( RcsRevCmp( nextRev->revName.text, currentRev->revName.text ) )
	{
	case REV_CMP_EQUAL:
	case REV_CMP_MISMATCH:
	case REV_CMP_IN_BRANCH:
	case REV_CMP_UP_TRUNK:
	    e->Set( MsgLbr::Loop ) << nextRev->revName.text;
	    return 0;

	default:
	    return nextRev;
	}
}

/*
 * RcsRevPlace::Locate() - find insertion point for a new revision.
 *
 * Returns (possibly) three points: to the previous rev, the current rev,
 * and the next rev, in terms of RCS lineage as computed by RcsRevCmp().
 * Returns a flag indicating whether the new revision will be on the
 * trunk or the branch, so that checkin knows which way to compute the
 * diffs.
 *
 * Any of prevRev, currRev, and nextRev may be null.  A null prevRev means 
 * currRev will be the head; a null currRev means it will be new; a null 
 * nextRev means the new rev will be at the tip of a branch (or the base of 
 * the trunk).
 */

int
RcsRevPlace::Locate(
	RcsArchive	*archive,
	const char 	*targetRev,
	Error	*e ) 
{
	RcsList *branch;
	RcsRev *rev = 0;

	/* Clear prevRev, nextRev */

	place = RCS_REV_PLACE_TRUNK;
	prevRev = currRev = nextRev = 0;
	thisPtr = &archive->headRev;

	/* start at head revision. */

	if( archive->headRev.text )
	    rev = archive->FindRevision( archive->headRev.text, e );

	if( !rev )
	    return 1;

	/* Follow down the trunk */

	for(;;)
	    switch( RcsRevCmp( targetRev, rev->revName.text ) )
	{
	case REV_CMP_EQUAL:
	    currRev = rev;

	    if( !rev->next.text )
		return 1;

	    if( !( rev = archive->NextRevision( rev, e ) ) )
		return 0;

	    /* fall through */

	case REV_CMP_IN_BRANCH:
	case REV_CMP_UP_TRUNK:
	    nextRev = rev;
	    return 1;

	case REV_CMP_MISMATCH:
	    return 0;

	case REV_CMP_OUT_BRANCH:
	case REV_CMP_DOWN_TRUNK:
	    prevRev = rev;

	    if( !rev->next.text )
		return 1;

	    thisPtr = &rev->next;

	    if( !( rev = archive->NextRevision( rev, e ) ) )
		return 0;
	    
	    continue;

	case REV_CMP_NUM_BRANCH:
	case REV_CMP_AT_BRANCH:
	    prevRev = rev;
	    place = RCS_REV_PLACE_BRANCH;

	    for( branch = rev->branches; branch; branch = branch->qNext )
	        switch( RcsRevCmp( targetRev, branch->revision.text ) )
	    {
	    case REV_CMP_EQUAL:
	    case REV_CMP_NUM_BRANCH:
	    case REV_CMP_AT_BRANCH:
	    case REV_CMP_OUT_BRANCH:
		thisPtr = &branch->revision;

		rev = archive->FindRevision( branch->revision.text, e );

		if( !rev )
		    return 0;

		goto next;

	    case REV_CMP_MISMATCH:
	    case REV_CMP_UP_TRUNK:
	    case REV_CMP_IN_BRANCH:
	    case REV_CMP_DOWN_TRUNK:	/* I hope not... */
		continue;
	    }

	    e->Set( MsgLbr::NoBrRev ) << targetRev;
	    return 0;

	next:;
	}
}

void
RcsRevTest( 
	RcsArchive	*archive,
	const char 	*targetRev,
	Error		*e )
{
	RcsRevPlace	p[1];
	int n;

	n = p->Locate( archive, targetRev, e );

	if( !n )
	    return;

	p4debug.printf( "%s : from %s to %s to %s\n",
		targetRev,
		p->prevRev ? p->prevRev->revName.text : "head",
		p->currRev ? p->currRev->revName.text : "new",
		p->nextRev ? p->nextRev->revName.text : "tail" );
}

# Change User Description Committed
#2 15902 Matt Attaway A second renaming that I will not obliterate as a badge of shame
#1 15901 Matt Attaway Clean up code to fit modern Workshop naming standards
//guest/perforce_software/p4/2014.1/rcs/rcsrev.cc
#1 12188 Matt Attaway Move 'main' p4 into a release specific directory in prep for new releases
//guest/perforce_software/p4/rcs/rcsrev.cc
#1 9129 Matt Attaway Initial commit of the 2014.1 p4/p4api source code