/******************************************************************************* Copyright (c) 1997-2004, 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 CONTR IBUTORS "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 or * * Description : Ruby bindings for the Perforce API. * ******************************************************************************/ #include #if defined ( OS_NT ) && defined( SetPort ) // Stupid "#define SetPort SetPortA" in winspool.h # undef SetPort #endif #include #include #include "p4rb_version.h" #include "p4result.h" #include "clientuserruby.h" #include "p4clientapi.h" #include "extconf.h" /******************************************************************************* * Utility class for self identification ******************************************************************************/ class Ident : public StrBuf { public: Ident( const char *fmt ) { this->fmt = fmt; } void AddVar( const char *name, const char *value ) { char buf[ 256 ]; sprintf( buf, fmt, name, value ); Append( buf ); } VALUE ToValue() { return rb_str_new2( Text() ); } private: const char *fmt; }; /******************************************************************************* * Our Ruby classes. ******************************************************************************/ VALUE cP4; // Base P4 Class VALUE eP4; // Exception 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(); } // // Protocol settings // static VALUE p4_set_tagged( VALUE self ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); p4->Tagged(); return Qtrue; } static VALUE p4_set_parse_forms( VALUE self ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); p4->ParseForms(); return Qtrue; } // // Getting/Setting Perforce environment // static VALUE p4_get_cwd( VALUE self ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); StrPtr cwd = p4->GetCwd(); return rb_str_new2( cwd.Text() ); } static VALUE p4_set_cwd( VALUE self, VALUE cwd ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); p4->SetCwd( STR2CSTR( cwd ) ); return Qtrue; } static VALUE p4_get_client( VALUE self ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); StrPtr client = p4->GetClient(); return rb_str_new2( client.Text() ); } static VALUE p4_set_client( VALUE self, VALUE client ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); p4->SetClient( STR2CSTR( client ) ); return Qtrue; } static VALUE p4_get_host( VALUE self ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); StrPtr host = p4->GetHost(); return rb_str_new2( host.Text() ); } static VALUE p4_set_host( VALUE self, VALUE host ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); p4->SetHost( STR2CSTR( host ) ); return Qtrue; } static VALUE p4_get_password( VALUE self ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); StrPtr passwd = p4->GetPassword(); return rb_str_new2( passwd.Text() ); } static VALUE p4_set_password( VALUE self, VALUE passwd ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); p4->SetPassword( STR2CSTR( passwd ) ); return Qtrue; } static VALUE p4_get_port( VALUE self ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); StrPtr port = p4->GetPort(); return rb_str_new2( port.Text() ); } static VALUE p4_set_port( VALUE self, VALUE port ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); p4->SetPort( STR2CSTR( port ) ); return Qtrue; } static VALUE p4_get_user( VALUE self ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); StrPtr user = p4->GetUser(); return rb_str_new2( user.Text() ); } static VALUE p4_set_user( VALUE self, VALUE user ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); p4->SetUser( STR2CSTR( user ) ); return Qtrue; } /******************************************************************************* * 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" ); char *cmd = STR2CSTR( rb_funcall( flatArgs, rb_intern( "shift" ), 0 ) ); 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 ); p4args[ i ] = STR2CSTR( rb_funcall( entry, idTo_S, 0 ) ); } 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_output( VALUE self ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); return p4->GetOutput(); } static VALUE p4_get_errors( VALUE self ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); return p4->GetErrors(); } 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 ) { int level; P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); return INT2NUM( p4->ExceptionLevel() ); } /******************************************************************************* * Self identification ******************************************************************************/ static VALUE p4_identify( VALUE self ) { Ident ident( "%24s : %s\n" ); ident.Append( "\nP4/Ruby Build Summary:\n\n" ); ident.AddVar( "Author","Tony Smith or "); ident.AddVar( "Version", P4RUBY_VERSION ); ident.AddVar( "Perforce API Version", P4APIVER ); ident.AddVar( "CFLAGS", P4CFLAGS ); ident.AddVar( "LIBS", P4LIBS ); ident.AddVar( "LIBPATH", P4LIBPATH ); ident.Append( "\n" ); return ident.ToValue(); } /******************************************************************************* * Debugging support ******************************************************************************/ static VALUE p4_debug( VALUE self, VALUE debug ) { P4ClientApi *p4; Data_Get_Struct( self, P4ClientApi, p4 ); p4->SetDebug( NUM2INT(debug) ); return Qtrue; } /****************************************************************************** * 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, "tagged", RUBY_METHOD_FUNC(p4_set_tagged), 0 ); rb_define_method( cP4, "parse_forms", RUBY_METHOD_FUNC(p4_set_parse_forms), 0); // Perforce client settings. Two sets of interfaces. One with // parameter passing, and one as assignment. e.g. // // p4.port( "localhost:1666" ) // or // p4.port = "localhost:1666" // // the latter is preferred. // rb_define_method( cP4, "cwd=", RUBY_METHOD_FUNC(p4_set_cwd) , 1 ); rb_define_method( cP4, "client=", RUBY_METHOD_FUNC(p4_set_client) , 1 ); rb_define_method( cP4, "host=", RUBY_METHOD_FUNC(p4_set_host) , 1 ); rb_define_method( cP4, "password=", RUBY_METHOD_FUNC(p4_set_password), 1 ); rb_define_method( cP4, "port=", RUBY_METHOD_FUNC(p4_set_port) , 1 ); rb_define_method( cP4, "user=", RUBY_METHOD_FUNC(p4_set_user) , 1 ); rb_define_method( cP4, "cwd?", RUBY_METHOD_FUNC(p4_get_cwd) , 0 ); rb_define_method( cP4, "client?", RUBY_METHOD_FUNC(p4_get_client) , 0 ); rb_define_method( cP4, "host?", RUBY_METHOD_FUNC(p4_get_host) , 0 ); rb_define_method( cP4, "password?", RUBY_METHOD_FUNC(p4_get_password), 0 ); rb_define_method( cP4, "port?", RUBY_METHOD_FUNC(p4_get_port) , 0 ); rb_define_method( cP4, "user?", RUBY_METHOD_FUNC(p4_get_user) , 0 ); // Session Connect/Disconnect rb_define_method( cP4, "connect", RUBY_METHOD_FUNC(p4_connect) , 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, "output", RUBY_METHOD_FUNC(p4_get_output) , 0 ); rb_define_method( cP4, "errors", RUBY_METHOD_FUNC(p4_get_errors) , 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 ); // Identification rb_define_const( cP4, "VERSION", rb_str_new2( P4RUBY_VERSION ) ); rb_define_singleton_method( cP4, "identify", RUBY_METHOD_FUNC(p4_identify), 0 ); // Debugging support rb_define_method( cP4, "debug=", RUBY_METHOD_FUNC(p4_debug), 1 ); } } // Extern C