specdef.cc #1

  • //
  • guest/
  • perforce_software/
  • p4/
  • 2016-1/
  • support/
  • specdef.cc
  • View
  • Commits
  • Open Download .zip Download (7 KB)
/*
 * Copyright 1995, 1996 Perforce Software.  All rights reserved.
 *
 * This file is part of Perforce - the FAST SCM System.
 */

/*
 * specdef.cc -- SpecElem methods
 */

# include <stdhdrs.h>

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

# include "spec.h"
# include <msgdb.h>

const char *const SpecTypes[] = {

	"word",		// SDT_WORD
	"wlist",	// SDT_WORDLIST
	"select",	// SDT_SELECT
	"line",		// SDT_LINE
	"llist",	// SDT_LINELIST
	"date",		// SDT_DATE
	"text",		// SDT_TEXT
	"bulk",		// SDT_BULK
	0,		

} ;

const char *const SpecOpts[] = {

	"optional",	// SDO_OPTIONAL
	"default",	// SDO_DEFAULT
	"required",	// SDO_REQUIRED
	"once",		// SDO_ONCE
	"always",	// SDO_ALWAYS	
	"key",		// SDO_KEY	
	0
} ;

const char *const SpecFmts[] = {
	
	"normal",	// SDF_NORMAL
	"L",		// SDF_LEFT
	"R",		// SDF_RIGHT
	"I",		// SDF_INDENT
	"C",		// SDF_COMMENT
	0
} ;

const char *const SpecOpens[] = {

	"none",		// SDO_NOTOPEN
	"isolate",	// SDO_ISOLATE
	"propagate",	// SDO_PROPAGATE
	0
} ;

const char *
SpecElem::FmtType()
{
	return SpecTypes[ type ];
}

const char *
SpecElem::FmtOpt()
{
	return SpecOpts[ opt ];
}

const char *
SpecElem::FmtFmt()
{
	return SpecFmts[ fmt ];
}

const char *
SpecElem::FmtOpen()
{
	return SpecOpens[ open ];
}

void
SpecElem::SetType( const char *typeName, Error *e )
{
	for( int j = 0; SpecTypes[j]; j++ )
	    if( !strcmp( SpecTypes[j], typeName ) )
	{
	    type = (SpecType)j;
	    return;
	}

	e->Set( MsgDb::FieldTypeBad ) << typeName << tag;
}

void
SpecElem::SetFmt( const char *fmtName, Error *e )
{
	for( int j = 0; SpecFmts[j]; j++ )
	    if( !strcmp( SpecFmts[j], fmtName ) )
	{
	    fmt = (SpecFmt)j;
	    return;
	}

	// Error is optional.
	// Unknown FMT codes allowed.

	if( !e )
	    return;

	e->Set( MsgDb::FieldTypeBad ) << fmtName << tag;
}

void
SpecElem::SetOpt( const char *optName, Error *e )
{
	for( int j = 0; SpecOpts[j]; j++ )
	    if( !strcmp( SpecOpts[j], optName ) )
	{
	    opt = (SpecOpt)j;
	    return;
	}

	e->Set( MsgDb::FieldOptBad ) << optName << tag;
}

void
SpecElem::SetOpen( const char *openName, Error *e )
{
	for( int j = 0; SpecOpens[j]; j++ )
	    if( !strcmp( SpecOpens[j], openName ) )
	{
	    open = (SpecOpen)j;
	    return;
	}

	e->Set( MsgDb::FieldOptBad ) << openName << tag;
}

/*
 * SpecElem::Compare() - compare SpecElems from different specs
 */

int
SpecElem::Compare( const SpecElem &other )
{
	// These can change:
	// fmt, seq, maxLength, preset

	return 
	    tag != other.tag || code != other.code ||
	    type != other.type || opt != other.opt ||
	    nWords != other.nWords || values != other.values ||
	    open != other.open;
}

/*
 * SpecElem::Encode() -- encode a single SpecElem elem
 * SpecElem::Decode() -- decode a single SpecElem elem
 */

void
SpecElem::Encode( StrBuf *s, int c )
{
	// SDO_KEY is internal only, introduced in 2003.1

	*s << tag;

	if( code != c )
	    *s << ";code:" << code;

	if( type != SDT_WORD )
	    *s << ";type:" << SpecTypes[ type ];

	if( opt != SDO_OPTIONAL && opt != SDO_KEY )
	    *s << ";opt:" << SpecOpts[ opt ];

	if( fmt != SDF_NORMAL )
	    *s << ";fmt:" << SpecFmts[ fmt ];

	if( open != SDO_NOTOPEN )
	    *s << ";open:" << SpecOpens[ open ];

	if( IsWords() && nWords != 1 )
	    *s << ";words:" << nWords;

	if( IsWords() && maxWords != 0 )
	    *s << ";maxwords:" << maxWords;

	if( IsRequired() )
	    *s << ";rq";

	if( IsReadOnly() )
	    *s << ";ro";

	if( seq )
	    *s << ";seq:" << seq;

	if( maxLength )
	    *s << ";len:" << maxLength;

	if( HasPresets() )
	    *s << ";pre:" << GetPresets();

	if( values.Length() )
	    *s << ";val:" << values;

	*s << ";;";
}

void
SpecElem::Decode( StrRef *s, Error *e )
{
	// ro and rq from 98.2 specs.

	int isReadOnly = 0;
	int isRequired = 0;

	// w = beginning, y = end, b = char after first ;

	char *w = s->Text();
	char *y = w + s->Length();
	char *b;

	if( b = strchr( w, ';' ) ) *b++ = 0; else b = y;

	// save tag

	tag = w;

	// walk remaining formatter

	while( b != y )
	{
	    char *q;
	    w = b;

	    if( b = strchr( w, ';' ) ) *b++ = 0; else b = y;
	    if( q = strchr( w, ':' ) ) *q++ = 0; else q = b;

	    if( !*w ) break;

	    if( !strcmp( w, "words" ) ) nWords = atoi( q );
	    else if( !strcmp( w, "maxwords" ) ) maxWords = atoi( q );
	    else if( !strcmp( w, "code" ) ) code = atoi( q );
	    else if( !strcmp( w, "type" ) ) SetType( q, e );
	    else if( !strcmp( w, "opt" ) ) SetOpt( q, e );
	    else if( !strcmp( w, "pre" ) ) SetPresets( q );
	    else if( !strcmp( w, "val" ) ) values = q;
	    else if( !strcmp( w, "rq" ) ) isRequired = 1;
	    else if( !strcmp( w, "ro" ) ) isReadOnly = 1;
	    else if( !strcmp( w, "len" ) ) maxLength = atoi( q );
	    else if( !strcmp( w, "seq" ) ) seq = atoi( q );
	    else if( !strcmp( w, "fmt" ) ) SetFmt( q, 0 );
	    else if( !strcmp( w, "open" ) ) SetOpen( q, e );

	    // OK if we don't recognise code!
	}

	/*
	 * opt: was supposed to supplant the ro/rq flags, but as of
	 * 2003.1 it shows up (a) as opt:default for a few built-in
	 * spec strings, (b) as opt:required (wrongly!) when p4 change
	 * re-encodes its ro;rq spec string, and (c) in job specs.
	 *
	 * For cases other than these, we map ro/rq to an opt.  Note
	 * that ro;rq maps to SDO_KEY, just so that IsReadOnly() and
	 * IsRequired() can return true.
	 *
	 * ro rq                IsReadOnly() IsRequired()
	 * 0  0 -> SDO_OPTIONAL     0           0       
	 * 0  1 -> SDO_REQUIRED     0           1
	 * x  x -> SDO_ONCE         1           0
	 * 1  0 -> SDO_ALWAYS       1           0
	 * 1  1 -> SDO_KEY          1           1
	 *
	 * For (b), where pre-2003.1 servers mistakenly encoded ro;rq 
	 * as SDO_REQUIRED, we combine that with ro to make it SDO_KEY.
	 */

	if( opt == SDO_OPTIONAL )
	{
	    if( isRequired && isReadOnly ) opt = SDO_KEY;
	    else if( isRequired ) opt = SDO_REQUIRED;
	    else if( isReadOnly ) opt = SDO_ALWAYS;
	}
	else if( opt == SDO_REQUIRED && isReadOnly )
	{
	    opt = SDO_KEY;
	}

	s->Set( b, y - b );
}

/*
 * SpecElem::CheckValue() - ensure value is one of the SpecElem::values
 */

int
SpecElem::CheckValue( StrBuf &value )
{
	// No values - anything is AOK
	// We don't check lines, yet.

	if( !values.Length() || type != SDT_SELECT )
	    return 1;

	// Split those suckers at / and look for a value

	StrBuf split = values;
	char *p = split.Text();

	for(;;)
	{
	    char *np;
	    StrRef r;

	    // Break at next /

	    if( np = strchr( p, '/' ) )
	    {
		r.Set( p, np - p );
		*np = 0;
	    }
	    else
	    {
		r.Set( p );
	    }

	    // If match (while folding case)
	    // save case-correct value from 'values'

	    if( !value.CCompare( r ) )
	    {
		value.Set( r );
		return 1;
	    }

	    // No more /'s?

	    if( !np )
		return 0;

	    p = np + 1;
	}
}

const StrPtr
SpecElem::GetPreset( const char *name )
{
	// Only SELECT allows for presets of condition/val

	if( type != SDT_SELECT )
	    return name ? StrRef::Null() : presets;

	// Look for a preset with the named condition

	int l = name ? strlen( name ) : 0;
	const char *p = presets.Text();
	const char *e = presets.End();

	for(;;)
	{
	    const char *c = strchr( p, ',' );
	    const char *s = strchr( p, '/' );

	    // foo
	    // foo,more
	    // foo,more/more

	    if( !l && ( !s || c && c < s ) )
	    {
		preset.Set( p, ( c ? c : e ) - p );
		return preset;
	    }

	    // name/foo
	    // name/foo,more

	    if( l == s - p && !strncmp( name, p, l ) && ( !c || c > s ) )
	    {
		preset.Set( s + 1, ( c ? c : e ) - s - 1 );
		return preset;
	    }

	    // try next

	    if( !c )
		return StrRef::Null();

	    p = c + 1;
	}
}

# Change User Description Committed
#1 19472 Liz Lam Initial add of the 2016.1 p4/p4api source code.