p4.cpp #45

  • //
  • guest/
  • perforce_software/
  • p4ruby/
  • main/
  • p4.cpp
  • View
  • Commits
  • Open Download .zip Download (35 KB)
/*******************************************************************************

Copyright (c) 2001-2008, Perforce Software, Inc.  All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1.  Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.

2.  Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL PERFORCE SOFTWARE, INC. BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*******************************************************************************/

/*******************************************************************************
 * Name		: p4.cc
 *
 * Author	: Tony Smith <[email protected]> or <[email protected]>
 *
 * Description	: Ruby bindings for the Perforce API. 
 *
 * vim:ts=8:sw=4
 ******************************************************************************/
#include <ruby.h>
#include "undefdups.h"
#include <clientapi.h>
#include <strtable.h>
#include <spec.h>
#include <ident.h>
#include "p4result.h"
#include "specmgr.h"
#include "clientuserruby.h"
#include "p4clientapi.h"
#include "p4mergedata.h"
#include "p4mapmaker.h"
#include "p4error.h"
#include "p4utils.h"
#include "extconf.h"


static Ident ident = { 
	IdentMagic "P4RUBY" "/" ID_OS "/" ID_REL "/" ID_PATCH  " (" ID_API "/" ID_API_PATCH 
		" API)", ID_Y "/" ID_M "/" ID_D 
};


/*******************************************************************************
 * Our Ruby classes.
 ******************************************************************************/
VALUE 	cP4;	// Base P4 Class
VALUE	eP4;	// Exception class
VALUE	cP4MD;	// P4::MergeData class
VALUE	cP4Map;	// P4::Map class
VALUE	cP4Msg; // P4::Message class
VALUE	cP4Prog;	//	P4::Progress class


extern "C"
{

//
// Construction/destruction
//

static void p4_free( P4ClientApi *p4 )
{
    delete p4;
}

static void p4_mark( P4ClientApi *p4 )
{
    p4->GCMark();
}

static VALUE p4_new( VALUE pClass )
{
    VALUE  	argv[ 1 ];
    P4ClientApi	*p4 = new P4ClientApi();
    VALUE	self;

    self = Data_Wrap_Struct( pClass, p4_mark, p4_free, p4 );
    rb_obj_call_init( self, 0, argv );
    return self;
}


//
// Session connect/disconnect
//
static VALUE p4_connect( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    return p4->Connect();
}

static VALUE p4_disconnect( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    return p4->Disconnect();
}

static VALUE p4_connected( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    return p4->Connected();
}

static VALUE p4_server_case_sensitive( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    if( p4->ServerCaseSensitive() )
	return Qtrue;
    return Qfalse;
}

static VALUE p4_server_unicode( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    if( p4->ServerUnicode() )
	return Qtrue;
    return Qfalse;
}

static VALUE p4_run_tagged( VALUE self, VALUE tagged )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );

    if( ! rb_block_given_p() )
	rb_raise( rb_eArgError, "P4#run_tagged requires a block" );

    // The user might have passed an integer, or it might be a boolean,
    // we convert to int for consistency.
    int		flag = 0;
    if( tagged == Qtrue )
	flag = 1;
    else if( tagged == Qfalse )
	flag = 0;
    else
	flag = NUM2INT( tagged ) ? 1 : 0;
    
    int old_value = p4->IsTagged();
    p4->Tagged( flag );

    VALUE ret_val;

    //
    // XXX: This should perhaps be protected with rb_ensure()...
    //
    ret_val = rb_yield( self );

    p4->Tagged( old_value );
    return ret_val;
}

static VALUE p4_get_tagged( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    return p4->IsTagged() ? Qtrue : Qfalse;
}

static VALUE p4_set_tagged( VALUE self, VALUE toggle )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );

    // The user might have passed an integer, or it might be a boolean,
    // we convert to int for consistency.
    int		flag = 0;
    if( toggle == Qtrue )
	flag = 1;
    else if( toggle == Qfalse )
	flag = 0;
    else
	flag = NUM2INT( toggle ) ? 1 : 0;
    
    p4->Tagged( flag );
    return flag ? Qtrue : Qfalse;	// Seems to be ignored...
}

static VALUE p4_get_api_level( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    return INT2NUM( p4->GetApiLevel() );
}

static VALUE p4_set_api_level( VALUE self, VALUE level )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    p4->SetApiLevel( NUM2INT( level ) );
    return self;
}

//
// Getting/Setting Perforce environment
//
static VALUE p4_get_charset( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    StrPtr c = p4->GetCharset();
    return P4Utils::ruby_string( c.Text() );
}

static VALUE p4_set_charset( VALUE self, VALUE c )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    return p4->SetCharset( StringValuePtr( c ) );
}

static VALUE p4_get_p4config( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    StrPtr c = p4->GetConfig();
    return P4Utils::ruby_string( c.Text() );
}

static VALUE p4_get_cwd( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    StrPtr cwd = p4->GetCwd();
    return P4Utils::ruby_string( cwd.Text() );
}

static VALUE p4_set_cwd( VALUE self, VALUE cwd )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    p4->SetCwd( StringValuePtr( cwd ) );
    return Qtrue;
}

static VALUE p4_get_client( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    StrPtr client = p4->GetClient();
    return P4Utils::ruby_string( client.Text() );
}

static VALUE p4_set_client( VALUE self, VALUE client )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    p4->SetClient( StringValuePtr( client ) );
    return Qtrue;
}

static VALUE p4_get_env( VALUE self, VALUE var )
{
    P4ClientApi	*p4;
    const char *val;
    Data_Get_Struct( self, P4ClientApi, p4 );
    val = p4->GetEnv( StringValuePtr( var ) );
    if( !val ) return Qnil;

    return P4Utils::ruby_string( val );
}

static VALUE p4_set_env( VALUE self, VALUE var, VALUE val )
{
    P4ClientApi *p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    return p4->SetEnv( StringValuePtr( var ), StringValuePtr( val ) );
}
static VALUE p4_get_host( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    StrPtr host = p4->GetHost();
    return P4Utils::ruby_string( host.Text() );
}

static VALUE p4_set_host( VALUE self, VALUE host )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    p4->SetHost( StringValuePtr( host ) );
    return Qtrue;
}

static VALUE p4_get_language( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    StrPtr lang = p4->GetLanguage();
    return P4Utils::ruby_string( lang.Text() );
}

static VALUE p4_set_language( VALUE self, VALUE lang )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    p4->SetLanguage( StringValuePtr( lang ) );
    return Qtrue;
}

static VALUE p4_get_maxresults( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    return INT2NUM( p4->GetMaxResults() );
}

static VALUE p4_set_maxresults( VALUE self, VALUE val )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    p4->SetMaxResults( NUM2INT( val ) );
    return Qtrue;
}

static VALUE p4_get_maxscanrows( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    return INT2NUM( p4->GetMaxScanRows() );
}

static VALUE p4_set_maxscanrows( VALUE self, VALUE val )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    p4->SetMaxScanRows( NUM2INT( val ) );
    return Qtrue;
}

static VALUE p4_get_maxlocktime( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    return INT2NUM( p4->GetMaxLockTime() );
}

static VALUE p4_set_maxlocktime( VALUE self, VALUE val )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    p4->SetMaxLockTime( NUM2INT( val ) );
    return Qtrue;
}

static VALUE p4_get_password( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    StrPtr passwd = p4->GetPassword();
    return P4Utils::ruby_string( passwd.Text() );
}

static VALUE p4_set_password( VALUE self, VALUE passwd )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    p4->SetPassword( StringValuePtr( passwd ) );
    return Qtrue;
}

static VALUE p4_get_port( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    StrPtr port = p4->GetPort();
    return P4Utils::ruby_string( port.Text() );
}

static VALUE p4_set_port( VALUE self, VALUE port )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    if( p4->Connected() )
	rb_raise( eP4, "Can't change port once you've connected." );

    p4->SetPort( StringValuePtr( port ) );
    return Qtrue;
}

static VALUE p4_get_prog( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    return P4Utils::ruby_string( p4->GetProg().Text() );
}

static VALUE p4_set_prog( VALUE self, VALUE prog )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    p4->SetProg( StringValuePtr( prog ) );
    return Qtrue;
}

static VALUE p4_set_protocol( VALUE self, VALUE var, VALUE val )
{
    P4ClientApi *p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    p4->SetProtocol( StringValuePtr( var ), StringValuePtr( val ) );
    return Qtrue;
}

static VALUE p4_get_ticket_file( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    return P4Utils::ruby_string( p4->GetTicketFile().Text() );
}

static VALUE p4_set_ticket_file( VALUE self, VALUE path )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    p4->SetTicketFile( StringValuePtr( path ) );
    return Qtrue;
}

static VALUE p4_get_user( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    StrPtr user = p4->GetUser();
    return P4Utils::ruby_string( user.Text() );
}

static VALUE p4_set_user( VALUE self, VALUE user )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    p4->SetUser( StringValuePtr( user ) );
    return Qtrue;
}

static VALUE p4_get_version( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    return P4Utils::ruby_string( p4->GetVersion().Text() );
}

static VALUE p4_set_version( VALUE self, VALUE version )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    p4->SetVersion( StringValuePtr( version ) );
    return Qtrue;
}

static VALUE p4_get_track( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    return p4->GetTrack() ? Qtrue : Qfalse;
}

static VALUE p4_set_track( VALUE self, VALUE toggle )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );

    // The user might have passed an integer, or it might be a boolean,
    // we convert to int for consistency.
    int		flag = 0;
    if( toggle == Qtrue )
	flag = 1;
    else if( toggle == Qfalse )
	flag = 0;
    else
	flag = NUM2INT( toggle ) ? 1 : 0;
    
    p4->SetTrack( flag );
    return flag ? Qtrue : Qfalse;	// Seems to be ignored...
}

static VALUE p4_get_streams( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    return p4->IsStreams() ? Qtrue : Qfalse;
}

static VALUE p4_set_streams( VALUE self, VALUE toggle )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );

    // The user might have passed an integer, or it might be a boolean,
    // we convert to int for consistency.
    int		flag = 0;
    if( toggle == Qtrue )
	flag = 1;
    else if( toggle == Qfalse )
	flag = 0;
    else
	flag = NUM2INT( toggle ) ? 1 : 0;
    
    p4->SetStreams( flag );
    return flag ? Qtrue : Qfalse;	// Seems to be ignored...
}

/*******************************************************************************
 * Running commands.  General purpose Run method and method for supplying
 * input to "p4 xxx -i" commands
 ******************************************************************************/

static VALUE p4_run( VALUE self, VALUE args )
{
    int 	i;
    int		argc = 0;
    ID		idFlatten = rb_intern( "flatten" );
    ID		idLength = rb_intern( "length" );
    ID		idTo_S	 = rb_intern( "to_s" );

    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );

    // Flatten the args array, and extract the Perforce command leaving
    // the remaining args in the array.
    VALUE flatArgs = rb_funcall( args, idFlatten, 0 );

    if ( ! NUM2INT( rb_funcall( flatArgs, idLength, 0 ) ) )
	rb_raise( eP4, "P4#run requires an argument" );

    VALUE v = rb_funcall( flatArgs, rb_intern( "shift" ), 0 );
    char *cmd = StringValuePtr( v );
    argc = NUM2INT( rb_funcall( flatArgs, idLength, 0 ) );

    // Allocate storage on the stack so it's automatically reclaimed 
    // when we exit.
    char **p4args = ALLOCA_N( char *, argc + 1 );

    // Copy the args across
    for ( i = 0; i < argc; i++ )
    {
	VALUE	entry = rb_ary_entry( flatArgs, i );
	VALUE	v = rb_funcall( entry, idTo_S, 0 );
	p4args[ i ] = StringValuePtr( v );
    }
    p4args[ i ] = 0;

    // Run the command
    VALUE res =  p4->Run( cmd, argc, p4args );
    return res;
}

static VALUE p4_set_input( VALUE self, VALUE input )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    return p4->SetInput( input );
}

static VALUE p4_get_errors( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    return p4->GetErrors();
}

static VALUE p4_get_messages( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    return p4->GetMessages();
}

static VALUE p4_get_warnings( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    return p4->GetWarnings();
}

static VALUE p4_set_except_level( VALUE self, VALUE level )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    p4->ExceptionLevel( NUM2INT(level) );
    return level;
}

static VALUE p4_get_except_level( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    return INT2NUM( p4->ExceptionLevel() );
}

static VALUE p4_get_server_level( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    int level = p4->GetServerLevel();
    return INT2NUM( level );
}

static VALUE p4_parse_spec( VALUE self, VALUE type, VALUE form )
{
    P4ClientApi	*p4;

    Check_Type( form, T_STRING );
    Check_Type( type, T_STRING );

    Data_Get_Struct( self, P4ClientApi, p4 );
    return p4->ParseSpec( StringValuePtr(type), StringValuePtr(form) );
}

static VALUE p4_format_spec( VALUE self, VALUE type, VALUE hash )
{
    P4ClientApi	*p4;

    Check_Type( type, T_STRING );
    Check_Type( hash, T_HASH );

    Data_Get_Struct( self, P4ClientApi, p4 );
    return p4->FormatSpec( StringValuePtr(type), hash );
}

static VALUE p4_track_output( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    return p4->GetTrackOutput();
}

/*******************************************************************************
 * Self identification
 ******************************************************************************/
static VALUE p4_identify( VALUE self )
{
    StrBuf	s;
    ident.GetMessage( &s );
    return P4Utils::ruby_string( s.Text() );
}

/*******************************************************************************
 * Debugging support
 ******************************************************************************/
static VALUE p4_get_debug( VALUE self)
{
    P4ClientApi *p4;
    Data_Get_Struct( self, P4ClientApi, p4);
    return INT2NUM( p4->GetDebug() );
}

static VALUE p4_set_debug( VALUE self, VALUE debug )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    p4->SetDebug( NUM2INT(debug) );
    return Qtrue;
}

/*******************************************************************************
 * Handler support
 ******************************************************************************/
static VALUE p4_get_handler( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    return p4->GetHandler();
}

static VALUE p4_set_handler( VALUE self, VALUE handler )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    p4->SetHandler( handler );
    return Qtrue;
}

/*******************************************************************************
 * Progress support
 ******************************************************************************/
static VALUE p4_get_progress( VALUE self )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    return p4->GetProgress();
}

static VALUE p4_set_progress( VALUE self, VALUE progress )
{
    P4ClientApi	*p4;
    Data_Get_Struct( self, P4ClientApi, p4 );
    return p4->SetProgress( progress );
}

/*******************************************************************************
 * P4::MergeData methods. Construction/destruction defined elsewhere
 ******************************************************************************/

static VALUE p4md_getyourname( VALUE self )
{
    P4MergeData	*md = 0;
    Data_Get_Struct( self, P4MergeData, md );
    return md->GetYourName();
}

static VALUE p4md_gettheirname( VALUE self )
{
    P4MergeData	*md = 0;
    Data_Get_Struct( self, P4MergeData, md );
    return md->GetTheirName();
}

static VALUE p4md_getbasename( VALUE self )
{
    P4MergeData	*md = 0;
    Data_Get_Struct( self, P4MergeData, md );
    return md->GetBaseName();
}

static VALUE p4md_getyourpath( VALUE self )
{
    P4MergeData	*md = 0;
    Data_Get_Struct( self, P4MergeData, md );
    return md->GetYourPath();
}

static VALUE p4md_gettheirpath( VALUE self )
{
    P4MergeData	*md = 0;
    Data_Get_Struct( self, P4MergeData, md );
    return md->GetTheirPath();
}

static VALUE p4md_getbasepath( VALUE self )
{
    P4MergeData	*md = 0;
    Data_Get_Struct( self, P4MergeData, md );
    return md->GetBasePath();
}

static VALUE p4md_getresultpath( VALUE self )
{
    P4MergeData	*md = 0;
    Data_Get_Struct( self, P4MergeData, md );
    return md->GetResultPath();
}

static VALUE p4md_getmergehint( VALUE self )
{
    P4MergeData	*md = 0;
    Data_Get_Struct( self, P4MergeData, md );
    return md->GetMergeHint();
}

static VALUE p4md_runmerge( VALUE self )
{
    P4MergeData	*md = 0;
    Data_Get_Struct( self, P4MergeData, md );
    return md->RunMergeTool();
}

static VALUE p4md_getcontentresolve( VALUE self )
{
	P4MergeData *md = 0;
	Data_Get_Struct( self, P4MergeData, md );
	return md->GetContentResolveStatus();
}

//
//	Additional methods added for action resolve
//
static VALUE p4md_getactionresolve( VALUE self )
{
	P4MergeData *md = 0;
	Data_Get_Struct( self, P4MergeData, md );
	return md->GetActionResolveStatus();
}

static VALUE p4md_getyoursaction( VALUE self )
{
	P4MergeData *md = 0;
	Data_Get_Struct( self, P4MergeData, md );
	return md->GetYoursAction();
}

static VALUE p4md_gettheiraction( VALUE self )
{
	P4MergeData *md = 0;
	Data_Get_Struct( self, P4MergeData, md );
	return md->GetTheirAction();
}

static VALUE p4md_getmergeaction( VALUE self )
{
	P4MergeData *md = 0;
	Data_Get_Struct( self, P4MergeData, md );
	return md->GetMergeAction();
}

static VALUE p4md_getactiontype( VALUE self )
{
	P4MergeData *md = 0;
	Data_Get_Struct( self, P4MergeData, md );
	return md->GetType();
}

static VALUE p4md_getinfo( VALUE self )
{
	P4MergeData *md = 0;
	Data_Get_Struct( self, P4MergeData, md );
	return md->GetMergeInfo();
}

static VALUE p4md_tos( VALUE self )
{
	P4MergeData *md = 0;
	Data_Get_Struct( self, P4MergeData, md );
	return md->GetString();
}
/******************************************************************************
 * P4::Map class
 ******************************************************************************/
static void p4map_free( P4MapMaker *m )
{
    delete m;
}

static VALUE p4map_new( int argc, VALUE *argv, VALUE pClass )
{
    //VALUE 	pClass;
    VALUE	array;
    VALUE	self;
    P4MapMaker	*m = new P4MapMaker;

    // First arg is the class
    // pClass = argv[ 0 ];
 
    // Now instantiate the new object.
    self = Data_Wrap_Struct( pClass, 0, p4map_free, m );
    rb_obj_call_init( self, 0, argv );

    if( argc )
    {
	array = argv[ 0 ];
	if( !rb_obj_is_kind_of( array, rb_cArray ) )
	    rb_raise( rb_eRuntimeError, "Not an array" );

	StrBuf	t;
	ID	idLen = rb_intern( "length" );
	int	len;
	VALUE	entry;

	// Now iterate over the array, inserting the mappings
	len = NUM2INT( rb_funcall( array, idLen, 0 ) );
	for( int i = 0; i < len; i++ )
	{
	    entry = rb_ary_entry( array, i );
	    m->Insert( entry );
	}
    }
    return self;
}

//
// Joins the RHS of the first mapping with the LHS of the second, and
// returns a new P4::Map object made up of the LHS of the first and the
// RHS of the second where the joins match up.
//
static VALUE p4map_join( VALUE pClass, VALUE left, VALUE right )
{
    P4MapMaker *	l = 0;
    P4MapMaker *	r = 0;
    P4MapMaker *	j = 0;
    VALUE 	m;
    VALUE  	argv[ 1 ];

    Data_Get_Struct( left,  P4MapMaker, l );
    Data_Get_Struct( right, P4MapMaker, r );

    j = P4MapMaker::Join( l, r );
    if( !j ) return Qnil;

    m = Data_Wrap_Struct( pClass, 0, p4map_free, j );
    rb_obj_call_init( m, 0, argv );
    return m;
}

//
// Debugging support
//
static VALUE p4map_inspect( VALUE self )
{
    P4MapMaker *	m = 0;
    StrBuf		b;
    StrBuf		tb;

    tb.Alloc( 32 );
    sprintf( tb.Text(), "%p", (void*) self );
    tb.SetLength();

    Data_Get_Struct( self, P4MapMaker, m );

    b << "#<P4::Map:" << tb << "> ";

    m->Inspect( b );
    return P4Utils::ruby_string( b.Text(), b.Length() );
}

//
// Insert a mapping into a P4::Map object. Can be called with either
// one, or two arguments. If one, it's assumed to be a string containing
// either a half-map, or both halves of the mapping.
//
static VALUE p4map_insert( int argc, VALUE *argv, VALUE self )
{
    P4MapMaker *	m = 0;
    StrBuf	t;

    Data_Get_Struct( self, P4MapMaker, m );

    if( argc < 1 || argc > 2 )
	rb_raise( rb_eArgError, "P4::Map#insert takes 1, or 2 arguments" );


    if( argc == 1 )
    {
	// A mapping with only a left hand side.
	m->Insert( *argv );
	return self;
    }

    if( argc == 2 )
    {
	// Separate left- and right-hand strings.
	VALUE left;
	VALUE right;

	left = *argv++;
	right = *argv;

	m->Insert( left, right );
    }
    return self;
}
static VALUE p4map_clear( VALUE self )
{
    P4MapMaker *	m = 0;

    Data_Get_Struct( self, P4MapMaker, m );
    m->Clear();
    return Qtrue;
}

static VALUE p4map_count( VALUE self )
{
    P4MapMaker *	m = 0;

    Data_Get_Struct( self, P4MapMaker, m );
    return INT2NUM( m->Count() );
}

static VALUE p4map_empty( VALUE self )
{
    P4MapMaker *	m = 0;

    Data_Get_Struct( self, P4MapMaker, m );
    return m->Count() ? Qfalse : Qtrue;
}

static VALUE p4map_reverse( VALUE self )
{
    P4MapMaker *	m = 0;
    P4MapMaker *	m2 = 0;
    VALUE		rval;
    VALUE  		argv[ 1 ];

    Data_Get_Struct( self, P4MapMaker, m );
    m2 = new P4MapMaker( *m );
    m2->Reverse();

    rval = Data_Wrap_Struct( cP4Map, 0, p4map_free, m2 );
    rb_obj_call_init( rval, 0, argv );
    return rval;
}

// 
// P4::Map#translate( string, fwd=true )
//
static VALUE p4map_trans( int argc, VALUE *argv, VALUE self )
{
    P4MapMaker *	m = 0;
    int			fwd = 1;
    VALUE		string;

    if( argc < 1 || argc > 2 )
	rb_raise( rb_eArgError, 
		"Invalid arguments to P4::Map#translate. "
		"Pass the string you wish to translate, and an optional "
		"boolean to indicate whether translation should be in "
		"the forward direction." );

    argc--;
    string = *argv++;

    if( argc && *argv == Qfalse )
	fwd = 0;

    Data_Get_Struct( self, P4MapMaker, m );
    return m->Translate( string, fwd );
}

static VALUE p4map_includes( VALUE self, VALUE string )
{
    P4MapMaker *	m = 0;

    Data_Get_Struct( self, P4MapMaker, m );
    if( m->Translate( string, 1 ) != Qnil )
	return Qtrue;
    if( m->Translate( string, 0 ) != Qnil )
	return Qtrue;
    return Qfalse;
}

static VALUE p4map_lhs( VALUE self )
{
    P4MapMaker *	m = 0;

    Data_Get_Struct( self, P4MapMaker, m );
    return m->Lhs();
}

static VALUE p4map_rhs( VALUE self )
{
    P4MapMaker *	m = 0;

    Data_Get_Struct( self, P4MapMaker, m );
    return m->Rhs();
}

static VALUE p4map_to_a( VALUE self )
{
    P4MapMaker *	m = 0;

    Data_Get_Struct( self, P4MapMaker, m );
    return m->ToA();
}

/*******************************************************************************
 * P4::Message methods. Construction/destruction defined elsewhere
******************************************************************************/
static VALUE p4msg_get_severity( VALUE self )
{
    P4Error *	e = 0;

    Data_Get_Struct( self, P4Error, e );
    return e->GetSeverity();
}

static VALUE p4msg_get_generic( VALUE self )
{
    P4Error *	e = 0;

    Data_Get_Struct( self, P4Error, e );
    return e->GetGeneric();
}

static VALUE p4msg_get_text( VALUE self )
{
    P4Error *	e = 0;

    Data_Get_Struct( self, P4Error, e );
    return e->GetText();
}

static VALUE p4msg_get_id( VALUE self )
{
    P4Error *	e = 0;

    Data_Get_Struct( self, P4Error, e );
    return e->GetId();
}
static VALUE p4msg_inspect( VALUE self )
{
    P4Error *	e = 0;

    Data_Get_Struct( self, P4Error, e );
    return e->Inspect();
}


/******************************************************************************
 * Extension initialisation
 ******************************************************************************/

void	Init_P4()
{
    // Ruby instantiation
    eP4 = rb_define_class( "P4Exception", rb_eRuntimeError );

    cP4 = rb_define_class( "P4", rb_cObject );
    rb_define_singleton_method( cP4, "new", RUBY_METHOD_FUNC(p4_new), 0 );
    
    // Protocol options
    rb_define_method( cP4, "api_level", RUBY_METHOD_FUNC(p4_get_api_level), 0);
    rb_define_method( cP4, "api_level=", RUBY_METHOD_FUNC(p4_set_api_level), 1);
    rb_define_method( cP4, "streams?", 	RUBY_METHOD_FUNC(p4_get_streams) , 0 );
    rb_define_method( cP4, "streams=", 	RUBY_METHOD_FUNC(p4_set_streams) , 1 );
    rb_define_method( cP4, "tagged",	RUBY_METHOD_FUNC(p4_run_tagged), 1 );
    rb_define_method( cP4, "tagged?",	RUBY_METHOD_FUNC(p4_get_tagged), 0 );
    rb_define_method( cP4, "tagged=",	RUBY_METHOD_FUNC(p4_set_tagged), 1 );
    rb_define_method( cP4, "track?", 	RUBY_METHOD_FUNC(p4_get_track)   , 0 );
    rb_define_method( cP4, "track=", 	RUBY_METHOD_FUNC(p4_set_track)   , 1 );


    // Perforce client settings.
    //
    rb_define_method( cP4, "charset", 	RUBY_METHOD_FUNC(p4_get_charset) , 0 );
    rb_define_method( cP4, "charset=", 	RUBY_METHOD_FUNC(p4_set_charset) , 1 );
    rb_define_method( cP4, "cwd", 	RUBY_METHOD_FUNC(p4_get_cwd)     , 0 );
    rb_define_method( cP4, "cwd=", 	RUBY_METHOD_FUNC(p4_set_cwd)     , 1 );
    rb_define_method( cP4, "client", 	RUBY_METHOD_FUNC(p4_get_client)  , 0 );
    rb_define_method( cP4, "client=", 	RUBY_METHOD_FUNC(p4_set_client)  , 1 );
    rb_define_method( cP4, "env", 	RUBY_METHOD_FUNC(p4_get_env)     , 1 );
    rb_define_method( cP4, "set_env", 	RUBY_METHOD_FUNC(p4_set_env)     , 2 );
    rb_define_method( cP4, "host", 	RUBY_METHOD_FUNC(p4_get_host)    , 0 );
    rb_define_method( cP4, "host=", 	RUBY_METHOD_FUNC(p4_set_host)    , 1 );
    rb_define_method( cP4, "language",  RUBY_METHOD_FUNC(p4_get_language), 0 );
    rb_define_method( cP4, "language=", RUBY_METHOD_FUNC(p4_set_language), 1 );
    rb_define_method( cP4, "p4config_file",RUBY_METHOD_FUNC(p4_get_p4config),0);
    rb_define_method( cP4, "password", RUBY_METHOD_FUNC(p4_get_password), 0 );
    rb_define_method( cP4, "password=", RUBY_METHOD_FUNC(p4_set_password), 1 );
    rb_define_method( cP4, "port", 	RUBY_METHOD_FUNC(p4_get_port)    , 0 );
    rb_define_method( cP4, "port=", 	RUBY_METHOD_FUNC(p4_set_port)    , 1 );
    rb_define_method( cP4, "prog", 	RUBY_METHOD_FUNC(p4_get_prog)    , 0 );
    rb_define_method( cP4, "prog=", 	RUBY_METHOD_FUNC(p4_set_prog)    , 1 );
    rb_define_method( cP4, "protocol", 	RUBY_METHOD_FUNC(p4_set_protocol), 2 );
    rb_define_method( cP4, "ticket_file", RUBY_METHOD_FUNC(p4_get_ticket_file), 0 );
    rb_define_method( cP4, "ticket_file=", RUBY_METHOD_FUNC(p4_set_ticket_file), 1 );
    rb_define_method( cP4, "user", 	RUBY_METHOD_FUNC(p4_get_user)    , 0 );
    rb_define_method( cP4, "user=", 	RUBY_METHOD_FUNC(p4_set_user)    , 1 );
    rb_define_method( cP4, "version", 	RUBY_METHOD_FUNC(p4_get_version) , 0 );
    rb_define_method( cP4, "version=", 	RUBY_METHOD_FUNC(p4_set_version) , 1 );


    rb_define_method( cP4, "maxresults", RUBY_METHOD_FUNC(p4_get_maxresults),0);
    rb_define_method( cP4, "maxresults=",RUBY_METHOD_FUNC(p4_set_maxresults),1);
    rb_define_method( cP4, "maxscanrows", RUBY_METHOD_FUNC(p4_get_maxscanrows),0);
    rb_define_method( cP4, "maxscanrows=",RUBY_METHOD_FUNC(p4_set_maxscanrows), 1 );
    rb_define_method( cP4, "maxlocktime", RUBY_METHOD_FUNC(p4_get_maxlocktime), 0 );
    rb_define_method( cP4, "maxlocktime=", RUBY_METHOD_FUNC(p4_set_maxlocktime), 1 );

    // Session Connect/Disconnect
    rb_define_method( cP4, "connect", 	RUBY_METHOD_FUNC(p4_connect)     , 0 );
    rb_define_method( cP4, "connected?",RUBY_METHOD_FUNC(p4_connected)   , 0 );
    rb_define_method( cP4, "disconnect", RUBY_METHOD_FUNC(p4_disconnect) , 0 );

    // Running commands - general purpose commands
    rb_define_method( cP4, "run", 	RUBY_METHOD_FUNC(p4_run)         ,-2 );
    rb_define_method( cP4, "input=", 	RUBY_METHOD_FUNC(p4_set_input)   , 1 );
    rb_define_method( cP4, "errors", 	RUBY_METHOD_FUNC(p4_get_errors)  , 0 );
    rb_define_method( cP4, "messages",	RUBY_METHOD_FUNC(p4_get_messages), 0 );
    rb_define_method( cP4, "warnings",	RUBY_METHOD_FUNC(p4_get_warnings), 0 );
    rb_define_method( cP4, "exception_level", RUBY_METHOD_FUNC(p4_get_except_level), 0 );
    rb_define_method( cP4, "exception_level=", RUBY_METHOD_FUNC(p4_set_except_level), 1 );
    rb_define_method( cP4, "server_level", RUBY_METHOD_FUNC(p4_get_server_level), 0 );
    rb_define_method( cP4, "server_case_sensitive?", RUBY_METHOD_FUNC(p4_server_case_sensitive), 0 );
    rb_define_method( cP4, "track_output",	RUBY_METHOD_FUNC(p4_track_output), 0 );

    rb_define_method( cP4, "server_unicode?", RUBY_METHOD_FUNC(p4_server_unicode), 0 );

    // Spec parsing
    rb_define_method( cP4, "parse_spec", RUBY_METHOD_FUNC(p4_parse_spec), 2 );
    rb_define_method( cP4, "format_spec", RUBY_METHOD_FUNC(p4_format_spec), 2 );

    // Identification
    rb_define_const( cP4, "VERSION", P4Utils::ruby_string( ID_REL ) );
    rb_define_const( cP4, "PATCHLEVEL", P4Utils::ruby_string( ID_PATCH ) );
    rb_define_const( cP4, "OS", P4Utils::ruby_string( ID_OS ) );
    rb_define_singleton_method( cP4, "identify", RUBY_METHOD_FUNC(p4_identify), 0 );

    // Debugging support
    rb_define_method( cP4, "debug", RUBY_METHOD_FUNC(p4_get_debug), 0);
    rb_define_method( cP4, "debug=", RUBY_METHOD_FUNC(p4_set_debug), 1 );

    // Support for OutputHandler
    rb_define_method( cP4, "handler", RUBY_METHOD_FUNC(p4_get_handler), 0);
    rb_define_method( cP4, "handler=", RUBY_METHOD_FUNC(p4_set_handler), 1);

    // Support for Progress API
    rb_define_method( cP4, "progress", RUBY_METHOD_FUNC(p4_get_progress), 0);
    rb_define_method( cP4, "progress=", RUBY_METHOD_FUNC(p4_set_progress), 1);

    // P4::MergeData class
    cP4MD = rb_define_class_under( cP4, "MergeData", rb_cObject );

    rb_define_method( cP4MD, "your_name", RUBY_METHOD_FUNC(p4md_getyourname),0);
    rb_define_method( cP4MD, "their_name", RUBY_METHOD_FUNC(p4md_gettheirname),0);
    rb_define_method( cP4MD, "base_name", RUBY_METHOD_FUNC(p4md_getbasename),0);
    rb_define_method( cP4MD, "your_path", RUBY_METHOD_FUNC(p4md_getyourpath),0);
    rb_define_method( cP4MD, "their_path", RUBY_METHOD_FUNC(p4md_gettheirpath),0);
    rb_define_method( cP4MD, "base_path", RUBY_METHOD_FUNC(p4md_getbasepath),0);
    rb_define_method( cP4MD, "result_path", RUBY_METHOD_FUNC(p4md_getresultpath),0);
    rb_define_method( cP4MD, "merge_hint", RUBY_METHOD_FUNC(p4md_getmergehint),0);
    rb_define_method( cP4MD, "run_merge", RUBY_METHOD_FUNC(p4md_runmerge),0);

    rb_define_method(cP4MD, "action_resolve?", RUBY_METHOD_FUNC(p4md_getactionresolve), 0);
    rb_define_method(cP4MD, "content_resolve?", RUBY_METHOD_FUNC(p4md_getcontentresolve), 0);
	rb_define_method(cP4MD, "yours_action", RUBY_METHOD_FUNC(p4md_getyoursaction), 0);
	rb_define_method(cP4MD, "their_action", RUBY_METHOD_FUNC(p4md_gettheiraction), 0);
	rb_define_method(cP4MD, "merge_action", RUBY_METHOD_FUNC(p4md_getmergeaction), 0);
	rb_define_method(cP4MD, "action_type", RUBY_METHOD_FUNC(p4md_getactiontype), 0);
	rb_define_method(cP4MD, "to_s", RUBY_METHOD_FUNC(p4md_tos), 0);
	rb_define_method(cP4MD, "info", RUBY_METHOD_FUNC(p4md_getinfo), 0);

    // P4::Map class
    cP4Map = rb_define_class_under( cP4, "Map", rb_cObject );
    rb_define_singleton_method( cP4Map, "new", RUBY_METHOD_FUNC(p4map_new), -1);
    rb_define_singleton_method( cP4Map, "join", RUBY_METHOD_FUNC(p4map_join), 2 );
    rb_define_method( cP4Map, "insert", RUBY_METHOD_FUNC(p4map_insert),-1);
    rb_define_method( cP4Map, "inspect", RUBY_METHOD_FUNC(p4map_inspect),0);
    rb_define_method( cP4Map, "clear", RUBY_METHOD_FUNC(p4map_clear),0);
    rb_define_method( cP4Map, "count", RUBY_METHOD_FUNC(p4map_count),0);
    rb_define_method( cP4Map, "empty?", RUBY_METHOD_FUNC(p4map_empty),0);
    rb_define_method( cP4Map, "translate", RUBY_METHOD_FUNC(p4map_trans),-1);
    rb_define_method( cP4Map, "reverse", RUBY_METHOD_FUNC(p4map_reverse),0);
    rb_define_method( cP4Map, "includes?", RUBY_METHOD_FUNC(p4map_includes),1);
    rb_define_method( cP4Map, "lhs", RUBY_METHOD_FUNC(p4map_lhs),0);
    rb_define_method( cP4Map, "rhs", RUBY_METHOD_FUNC(p4map_rhs),0);
    rb_define_method( cP4Map, "to_a", RUBY_METHOD_FUNC(p4map_to_a),0);

    // P4::Message class.
    cP4Msg = rb_define_class_under( cP4, "Message", rb_cObject );
    rb_define_method( cP4Msg, "inspect", RUBY_METHOD_FUNC(p4msg_inspect),0);
    rb_define_method( cP4Msg, "msgid", RUBY_METHOD_FUNC(p4msg_get_id), 0);
    rb_define_method( cP4Msg, "severity", RUBY_METHOD_FUNC(p4msg_get_severity), 0);
    rb_define_method( cP4Msg, "generic", RUBY_METHOD_FUNC(p4msg_get_generic), 0);
    rb_define_method( cP4Msg, "to_s", RUBY_METHOD_FUNC(p4msg_get_text), 0);

    //	P4::Progress class.
    cP4Prog = rb_define_class_under( cP4, "Progress", rb_cObject );

};


} // Extern C
# Change User Description Committed
#49 14682 Git Fusion Git Fusion branch management

Imported from Git
 ghost-of-change-num: 960958
 ghost-of-sha1: 005052ae424bd69f426f7209e741ca1c8c3253c7
 ghost-precedes-sha1: ad052c71a568ef12165e143a6866ad9ceffbb4a1
 parent-branch: None@960958
 push-state: incomplete
#48 14676 tony Rework P4Ruby unit tests so they they are actually units.
Now the
test order is irrelevant as all tests are standalone. Tidied up
the code formatting as well and removed all the tabs from the ruby
sources files. Added a modeline for vim users so consistent
indentation will be used.
#47 14659 tony Add ignore file support to P4Ruby.
This change adds
three new methods:

    P4#ignore_file        - Return current ignore file
    P4#ignore_file=        - Set ignore file name
    P4#ignored?        - Test if a path is ignored

It also removes the check which objected to the use of Ruby 2.0
since we now support it. Now we simply check that you're not
using 2.1 (still a development release).
#46 14655 tony Fix crashing bug in P4Ruby when an exception is raised from within
the block passed to P4#run_resolve. This happened because the
P4::MergeData object was attached to the exception and so survived
longer than the resolve process itself. Ruby's exception handling
then tries to call P4::MergeData#to_s to format the object as a
string, but some of the internal pointers inside the MergeData object
(provided by P4Api) have already been deleted. The to_s method tried
to access those objects and caused the crash.

This change ensures that the MergeData object invalidates its pointers
to objects owned by P4Api and passed in with the scope of an individual
resolve as soon as that resolve is completed (successfully or otherwise).
#45 14650 jmistry Progress indicator for P4Ruby.

There is a new class P4::Progress that is set up to be subclassed by a user.
It has the following interface and P4Ruby expects the following class methods to
be defined by the user (even if it's an empty implementation):

    class Progress:
        def init(type)
        end

        def description(description, units )
        end

        def total( total )
        end

        def update( position )
        end

        def done( fail )
        end
    end

Users need to create a subclass of P4::Progress and assign an instance to P4
to enable the progress indicator:

class MyProgress < P4::Progress
    def update(pos):
       # do something with the value here

    # other methods

p4 = P4.new
p4.progress = MyProgress.new

New feature to be documented in the release notes.
#44 14629 jmistry Implemented 'action resolve' in P4Ruby.

This extends the existing P4MergeData class to support action resolve.  Users
will need to test the MergeData object passed to the block to determine if the
resolve is for content (P4::MergeData#content_resolve?) or action
(P4::MergeData#action_resolve?).  The attributes available in
'P4::MergeDatamerge' for an action resolve are:

merge_action : The action chosen for the merge, can be emtpy
yours_action: your action/filetype
their_action: their action/filetype
type: the type of merge. Can be
         "Branch resolve",
         "Filetype resolve",
         "Delete resolve"
         "Filename resolve"
info: a dictionary with additional information that can vary with the resolve
type.

Updated unit test to test action resolve.

User visible change, to be documented in the release notes for 2012.1.
#43 14611 jmistry Support setting values in the registry through P4Ruby.
The command P4.set_env( var, val ) will set a registry variably on platforms
that support this action or through an exception on Unix.
The command P4.set_env( var ) resets a registry variable.

Updated unit test '02_environment.rb' to include registry set/check.

User visible change to be documented in release notes.
#42 14608 jmistry Add encoding to Strings

As part of adding Ruby 1.9 support we need to associate the encoding
for Ruby's strings from the server.  This approach is similar to Sven's
(in changelist 257263), where everything but the 'content' charset was
set to 'utf8'.  The content charset is picked up from P4CHARSET and this
is used to translate any file content.

Also disabled the Ruby 1.9 warning for each compile.

User visible change to be documented in release notes.
#41 14595 jmistry P4.identify now includes API change

P4.identify output now includes C/C++ API change and report product
name as P4RUBY, instead of P4Ruby.

So, the output now looks like this:
    Rev. P4RUBY/DARWIN10U/2011.1.MAIN/279655 (2011.1.MAIN/279655 API) (2011/01/04).
#40 14593 Sven Erik Knop P4Ruby implementation of OutputHandler.
Also, debug is now treated as a normal attribute and can be read.

Test cases still missing.

New functionality, to be documented in release notes.
#39 14592 Sven Erik Knop Enable P4-Ruby to compile and test with Ruby 1.9.

The current solution is far from ideal because it is not possible to compile
and test both Ruby 1.8 and Ruby 1.9 in parallel. The Makefile writes both
artifacts and binaries to the same location.

This means a user/tester/builder needs to choose on Ruby platform or ensure
'make clean' is called first.

Many of the test cases also still fail in Ruby 1.9. We also need to investigate
the Unicode story with Ruby 1.9 and see if the lessons learned from Python 3
can be applied somehow.

Infrastructure change, no functional change yet.
#38 14589 Sven Erik Knop Pulled P4Ruby p10.2 changes back to main.
#37 14586 tony Streams method tidying.
Change P4#streams into P4#streams? as it
returns a boolean, and change the implementation to use
SetVar() instead of SetProtocol() now that Mark's made it work for
us.

Change to undocumented functionality
#36 14584 psoccard Added method to enable streams
#35 14583 psoccard Added support for -Ztrack
#34 14580 tony Added P4::Message#msgid method that returns the UniqueCode
of any error message - much easier than matching it's output
text.

User-visible enhancement documented in p4rubynotes.txt
#33 14579 tony Make new class P4::Message for returning Error objects
to the user. Currently handles errors and warnings, but
could potentially be used for output too (might bloat
people's code though).

Essentially, if you're using a 2010.2 or later client,
or if you've set your api_level to 68 or higher, the
P4#errors and P4#warnings arrays will be populated
with P4::Message objects instead of strings. Users of
older API's, or those who set their api_level to 67
or lower in their scripts will get the old behaviour.

P4::Message objects have the following methods:

  severity() - returns the severity
  generic()  - returns the generic code
  to_s()     - converts the message to a string
  inspect()  - returns a string showing the message details.

User-visible enhancement documented in p4rubynotes.txt
#32 14571 tony Tidy P4Ruby's connection state management.
P4ClientApi was accumulating
a lot of 'int' flags. This change combines them all into a bitmask with some
methods to encapsulate the manipulation of the mask.

I've also removed the code which worked around a very old and obscure
protocol bug in 2000.1/2000.2 servers. Anyone using a server that old
doesn't deserve the modern P4Ruby.

To test P4#server_case_sensitive? I have adapted the test suite so that
the server it runs uses the -C1 flag - so it's case-folding on all
platforms. The reason for this is that it enables the tests to  succeed
on Windows and Mac OS where case-folding is the norm.

Infrastructure change only. No functional change.
#31 14570 tony Add new P4#server_unicode?
method to P4Ruby. This allows you to
test for unicode mode servers once you've executed at least one
Perforce command. That's because the server only sends the
protocol block in response to the first command these days, and
that's when we'll find out if unicode mode is enabled.
#30 14569 tony Add P4::OS and P4::PATCHLEVEL variables so people can work out
details of the P4Ruby installation without having to parse
P4::Identify().

User-visible enhancement documented in p4rubynotes.txt
#29 14561 tony Prevent users from changing the port once they've connected to
the server.

User-visible enhancement documented in p4rubynotes.txt
#28 14560 tony Add a P4#server_case_sensitive?
method that returns true if the server
is base sensitive, and false otherwise. Raises a P4Exception if it's
used before a command has been executed.

User-visible enhancement documented in p4rubynotes.txt
#27 14558 tony Add undoc'd P4#protocol( var, val) method for calling ClientApi::SetProtocol().

I would prefer to have named methods for the important protocol variables,
or to wrap their use in other ways (see tag and specstring), but for
tunables and other ad-hoc settings, sometimes direct access is required.

User-visible change, not documented anywhere.
#26 14553 ateague Integrate change for Map::reverse bug fix where it used to change
the map instead of returning a reversed copy of the map.

Already in the release notes for 8.2.
#25 14548 tony Add new P4#tagged() method, that takes a bool, and a block.
It sets/clears
tagged output (as specified by the boolean), and runs the block. It then
resets the tagged output to its previous value. For example

   p4.tagged( false ) { ctr = p4.run_counter( 'change' ).to_i }

This is in fact the best use-case, since getting a counter value is
harder in tagged mode than in non-tagged mode. Most users
will not need this method.

User-visible enhancement documented in p4rubynotes.txt
#24 14541 tony Copyright notice housekeeping: update all notices to 2008, and
correct start date from 1997 to 2001 when P4Ruby was first released
from the public depot.

No functional change
#23 14540 tony Eliminate P4::Map.load() method, and make P4::Map.new() take
an optional array of mappings instead. Taken from P4Python.

Change to unreleased new feature
#22 14536 tony Correct prototype and returns for P4::Map stuff.
The p4_map_free
function needs to return void, not a VALUE, and the insert
method needs to return something in every code path.

Bug fixes to unreleased functionality
#21 14534 tony Add support for the MapApi class in P4Ruby.
This change adds a new
class, P4::Map, to P4Ruby's canon. The methods on P4::Map are:

    P4::Map.new        - Constructor
    P4::Map.load        - Load a map from an array
    P4::Map.join        - Join two maps to create a third

    P4::Map#clear        - Empty a map
    P4::Map#count        - Return the number of entries
    P4::Map#empty?        - Tests whether a map object is empty
    P4::Map#translate    - Translate a string through a map
    P4::Map#includes?    - Tests whether a path is mapped
    P4::Map#lhs        - Returns the left side as an array
    P4::Map#rhs        - Returns the right side as an array
    P4::Map#to_a        - Returns the map as an array

(Note, the first three are class methods, the rest instance methods)

There is also P4::Map#reverse(), but I'm not documenting that yet as
I'm not sure I understand the semantics of reversing complex maps
well enough.

User-visible enhancement documented in p4rubynotes.txt and slated
for 2008.2 release.
#20 14531 tony Add P4#env() method to allow users to read the P4 object's environment
using the same rules that P4 does. This allows values from P4CONFIG
files to be picked up automatically.

P4#cwd=() now calls Enviro::Config() to reload any config file when
the user changes directory.

User-visible enhancement documented in p4rubynotes.txt
#19 14529 tony Pull 2007.3 p4-ruby changes back to main.

Integration only change
#18 14511 tony Add P4#api_level method to get the current API level.

User-visible new feature to be documented in Scripting Interfaces
Guide.
#17 14508 tony Add 'getters': P4#maxresults, P4#maxscanrows and P4#maxlocktime to
complement the equivalent setters.

User-visible change to be documented in Scripting Interface Guide
#16 14502 tony Rework spec handling somewhat so that:

(a) P4Ruby knows about the default spec types for 2007.2 so it
    doesn't have to connect to the server to parse and format specs,
    and nor does it have to do the ugly hack of running a 'p4 xxx -o'
    and discarding the result just to get the specDef.

(b) If a user's got a custom spec then the spec cache will be updated
    if they fetch an object of that type. So basically, if the
    server's given us a specdef for a class of spec, we use it.
    Otherwise, we fall back on the builtin defaults

This reworks SpecMgr quite a bit - renaming methods so it's clearer
what they do, and making it own the spec cache. We also pass the
SpecMgr object created by P4ClientApi down to ClientUserRuby now,
so that whenever the server sends us a spec, we can update the cache.
#15 14500 tony Add P4#version= and P4#version methods to get/set the version of
the program.

User-visible change to be documented in Scripting Interface Guide
#14 14498 tony Add P4#ticket_file and P4#ticket_file= methods to get and set the
ticket file. Note that P4#ticket_file will only return something if
a value has been previously set using P4#ticket_file=(). There's
no way, in the existing P4 API to get the current ticket file.

User-visible change to be documented in Scripting Interfaces Guide
#13 14496 tony Remove implementation of P4#output.
Missed in previous change
#12 14495 tony Remove P4#output() method used to refetch the output if you've
somehow lost it. Really shouldn't be needed and we don't want
to implement this in the other languages. So...

User-visible change to be de-documented in the Scripting Interfaces
Guide.
#11 14494 tony Add P4#maxlocktime=( int ) method to allow users to specify the
maximum number of milliseconds for which locks may be held.
#10 14493 tony Turn on tagged mode by default, and make parse_forms mode
always on (it only works when tagged mode is on too). Now
users can turn tagged mode off if they want to by calling:

    p4.tagged = false

and turn it back on by calling:

    p4.tagged = true

You can test the current state of affairs using:

    p4.tagged?

User-visible change to be documented in Scripting Interface Guide
#9 14492 tony Rename P4#input to P4#input=, and fix all pre-existing calls.
The
main existing callers are p4.run_password, p4.run_submit and
and p4.run_login. There was also a bug with p4.run_password() which
meant that you couldn't change a user's password if they hadn't
already set one. Fixed that while I was there and added a test
case to check that password setting, use and clearing works.
#8 14489 tony Add P4#server_level method to return the protocol level of the
server. Raises a P4Exception if you attempt to use it before
running a command (since the server2 variable is only sent to
the client after the client runs a command).

New method to be documented in Scripting Interfaces Guide
#7 14488 tony Add P4#language and P4#language= methods to allow users to get/set
their language (mostly applies to Japanese I guess)

Change to unreleased functionality
#6 14487 tony Add P4#connected?
to test whether or not the session (a) has been
connected and (b) has not been dropped.

Functional change to unreleased code.
#5 14486 tony Add P4#config_file method to return the path to the P4CONFIG file
in use.
#4 14485 tony Rename P4#api= to P4#api_level=

User visible change to be documented in Scripting Interfaces Guide
#3 14483 tony Method name rationalisation, part 1.
All the methods with names ending
with '?' that do not return booleans now have the '?' dropped. This
will be used to ensure consistency across the scripting interfaces.

Added P4#prog() -> <string> to partner P4#prog=( <string> )

Removed some redundant aliases that we're dropping support for in
future versions
#2 14482 tony Use Ident from Perforce API to identify P4Ruby instead of home-grown
equivalent. The extra information included in the original output
will be added into new methods later.
#1 14480 tony Add P4Ruby 1.5944 to main as start-point for the first
productized release of P4Ruby