/* * Copyright 1995, 2000 Perforce Software. All rights reserved. * * This file is part of Perforce - the FAST SCM System. */ /* * ClientUser -- default implementation of user interface for p4 client * * This file implements the p4 client command line interface by defining * the methods of the ClientUser class. This interface reads from stdin, * writes to stdout and stderr, and invokes the user's editor. */ # define NEED_FILE # ifdef OS_NT # include <process.h> # define NEED_FCNTL # endif # include <clientapi.h> # include <diff.h> # include <enviro.h> # include <echoctl.h> # include <signaler.h> # include <strops.h> # include <msgclient.h> # if defined ( OS_MAC ) || defined ( OS_MACOSX ) # include "clientusermac.h" # endif /* * ClientUser::~ClientUser() - virtual destructor holder */ ClientUser::~ClientUser() { } /* * ClientUser::Prompt() - prompt the user, and wait for a response. */ void ClientUser::Prompt( const StrPtr &msg, StrBuf &buf, int noEcho, Error *e ) { printf( msg.Text() ); # ifdef OS_MAC fputc( '\n', stdout ); # endif fflush( stdout ); fflush( stdin ); // Turn off echoing NoEcho *setEcho = noEcho ? new NoEcho : 0; // Prompt'em buf.Clear(); char *b = buf.Alloc( 1024 ); if( !fgets( b, 1024, stdin ) ) { e->Set( MsgClient::Eof ); buf.SetEnd( b ); } else { // Set end and truncate any newline buf.SetEnd( b += strlen( b ) ); if( buf.Length() && buf.End()[ -1 ] == '\n' ) { buf.SetEnd( buf.End() - 1 ); buf.Terminate(); } } delete setEcho; } void ClientUser::Help( const_char *const *help ) { for( ; *help; help++ ) printf( "%s\n", *help ); } void ClientUser::HandleError( Error *err ) { // Dumb implementation is just to format // the error and call (old) OutputError. // API callers against 99.1+ servers can do better by // having HandleError probe 'err' directly for detail. StrBuf buf; err->Fmt( buf, EF_NEWLINE ); OutputError( buf.Text() ); } void ClientUser::OutputError( const_char *errBuf ) { fflush( stdout ); fwrite( errBuf, 1, strlen( errBuf ), stderr ); } void ClientUser::OutputInfo( char level, const_char *data ) { switch( level ) { default: case '0': break; case '1': printf( "... " ); break; case '2': printf( "... ... " ); break; } fwrite( data, 1, strlen( data ), stdout ); fputc( '\n', stdout ); } void ClientUser::OutputText( const_char *data, int length ) { fwrite( data, 1, length, stdout ); } void ClientUser::OutputBinary( const_char *data, int length ) { # ifdef OS_NT // we rely on a trailing zero length buffer to // tell us to turn off binary output. if( binaryStdout == !length ) { // toggle binaryStdout = !!length; fflush( stdout ); setmode( fileno( stdout ), binaryStdout ? O_BINARY : O_TEXT ); } # endif fwrite( data, 1, length, stdout ); } void ClientUser::InputData( StrBuf *buf, Error *e ) { int n; buf->Clear(); do { # ifdef OS_MAC // CW PRO 5 fread/read from stdin hangs machine char *b = buf->Alloc( FileBufferSize + 1 ); n = fgets( b, FileBufferSize + 1, stdin ) ? strlen( b ) : 0; # else char *b = buf->Alloc( FileBufferSize ); n = read( 0, b, FileBufferSize); # endif buf->SetEnd( b + n ); } while( n > 0 ); buf->Terminate(); } void ClientUser::OutputStat( StrDict *varList ) { int i; StrBuf msg; StrRef var, val; // Dump out the variables, using the GetVar( x ) interface. // Don't display the function (duh), which is only relevant to rpc. for( i = 0; varList->GetVar( i, var, val ); i++ ) { if( var == "func" ) continue; // otherAction and otherOpen go at level 2, as per 99.1 + earlier msg.Clear(); msg << var << " " << val; char level = strncmp( var.Text(), "other", 5 ) ? '1' : '2'; OutputInfo( level, msg.Text() ); } // blank line OutputInfo( '0', "" ); } void ClientUser::ErrorPause( char *errBuf, Error *e ) { StrBuf buf; OutputError( errBuf ); # if defined( OS_MAC ) Prompt( StrRef( "Hit enter to continue..." ), buf, 0, e ); # else Prompt( StrRef( "Hit return to continue..." ), buf, 0, e ); # endif } void ClientUser::Edit( FileSys *f1, Error *e ) { Edit( f1, enviro, e ); } void ClientUser::Edit( FileSys *f1, Enviro * env, Error *e ) { // The presence of $SHELL on NT implies MKS, which has vi. // On VMS, we look for POSIX$SHELL - if set, we'll use vi. // Otherwise, EDIT. if( !f1->IsTextual() ) { e->Set( MsgClient::CantEdit ) << f1->Name(); return; } # if defined( OS_MAC ) || defined ( OS_MACOSX ) ClientUserMacEdit( f1, env, e, true ); # else StrBuf n; const char *editor; if( !( editor = env->Get( "P4EDITOR" ) ) ) editor = getenv( "EDITOR" ); # ifdef OS_NT if( !editor && !getenv( "SHELL" ) ) editor = "notepad"; # endif # ifdef OS_VMS if( !editor && !getenv( "POSIX$SHELL" ) ) editor = "edit"; # endif # ifdef OS_AS400 if( !editor ) editor = "edtf"; # endif // Bill Joy rules, Emacs maggots! if( !editor ) editor = "vi"; RunCmd( editor, f1->Name(), 0, 0, 0, 0, e ); # endif /* OS_MAC */ } void ClientUser::Diff( FileSys *f1, FileSys *f2, int doPage, char *df, Error *e ) { if( !f1->IsTextual() || !f2->IsTextual() ) { if( f1->Compare( f2, e ) ) printf( "(... files differ ...)\n" ); return; } // Call diff to do text compare const char *diff; const char *pager; if( !( diff = enviro->Get( "P4DIFF" ) ) ) diff = getenv( "DIFF" ); if( !doPage ) pager = 0; else if( !( pager = enviro->Get( "P4PAGER" ) ) ) pager = getenv( "PAGER" ); // Do our own diff // VMS ReadFile is busted. # ifdef OS_VMS if( !diff ) diff = "diff"; # else /* not VMS */ if( !diff ) { // diff expects to read files in raw mode, we must // create new FileSys to allow this. FileSys *f1_bin = FileSys::Create( FST_BINARY ); f1_bin->Set( f1->Name() ); FileSys *f2_bin = FileSys::Create( FST_BINARY ); f2_bin->Set( f2->Name() ); if( pager ) { FileSys *t = FileSys::CreateGlobalTemp( f1->GetType() ); { # ifndef OS_NEXT :: # endif Diff d; d.SetInput( f1_bin, f2_bin, df, e ); if( !e->Test() ) d.SetOutput( t->Name(), e ); if( !e->Test() ) d.DiffWithFlags( df ); d.CloseOutput( e ); } if( !e->Test() ) RunCmd( pager, t->Name(), 0, 0, 0, 0, e ); delete t; } else { # ifndef OS_NEXT :: # endif Diff d; d.SetInput( f1_bin, f2_bin, df, e ); if( !e->Test() ) d.SetOutput( stdout ); if( !e->Test() ) d.DiffWithFlags( df ); d.CloseOutput( e ); } delete f1_bin; delete f2_bin; return; } # endif /* VMS */ // Build up flags args // Yuk. if( !df || !*df ) { RunCmd( diff, f1->Name(), f2->Name(), 0, 0, pager, e ); } else { StrBuf flags; flags.Set( "-", 1 ); flags << df; RunCmd( diff, flags.Text(), f1->Name(), f2->Name(), 0, pager, e ); } } void ClientUser::Merge( FileSys *base, FileSys *leg1, FileSys *leg2, FileSys *result, Error *e ) { char *merger; if( !( merger = enviro->Get( "P4MERGE" ) ) && !( merger = getenv( "MERGE" ) ) ) { e->Set( MsgClient::NoMerger ); return; } RunCmd( merger, base->Name(), leg1->Name(), leg2->Name(), result->Name(), 0, e ); } void ClientUser::RunCmd( const char *command, const char *arg1, const char *arg2, const char *arg3, const char *arg4, const char *pager, Error *e ) { StrBuf n; fflush( stdout ); signaler.Block(); // reset SIGINT to SIGDFL # ifdef OS_MAC n << command; if( arg1 ) n << " '" << arg1 << "'"; if( arg2 ) n << " '" << arg2 << "'"; if( arg3 ) n << " '" << arg3 << "'"; if( arg4 ) n << " '" << arg4 << "'"; ToolServerExec( n.Text(), e ); # else # ifdef OS_NT if( !pager ) { n << command; const char * argv[32]; int c = StrOps::Words( n, (char **)argv, 32 ); if( arg1 ) argv[ c++ ] = arg1; if( arg2 ) argv[ c++ ] = arg2; if( arg3 ) argv[ c++ ] = arg3; if( arg4 ) argv[ c++ ] = arg4; argv[ c++ ] = 0; if( spawnvp( _P_WAIT, argv[0], argv ) < 0 ) e->Sys( argv[0], arg1 ); signaler.Catch(); // catch SIGINT again return; } # endif /* OS_NT */ # ifdef OS_VMS # define QUOTE "" # else # ifdef OS_OS2 # define QUOTE "\"" # else # define QUOTE "'" # endif # endif n << command; if( arg1 ) n << " " QUOTE << arg1 << QUOTE; if( arg2 ) n << " " QUOTE << arg2 << QUOTE; if( arg3 ) n << " " QUOTE << arg3 << QUOTE; if( arg4 ) n << " " QUOTE << arg4 << QUOTE; if( pager ) n << " | " << pager; system( n.Text() ); # endif /* OS_MAC */ signaler.Catch(); // catch SIGINT again } FileSys * ClientUser::File( FileSysType type ) { return FileSys::Create( type ); } int ClientUser::Resolve( ClientMerge *m, Error *e ) { // pun from ClientMergeStatus return (int)m->Resolve( this, e ); } void ClientUser::Message( Error *err ) { if( err->IsInfo() ) { // Info StrBuf buf; err->Fmt( buf, EF_PLAIN ); OutputInfo( err->GetGeneric() + '0', buf.Text() ); } else { // warn, failed, fatal HandleError( err ); } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#2 | 1689 | Sam Stafford |
Integrate 02.1 API and code cleanup to P4HL. Lots of work. Phew. |
||
#1 | 937 | Sam Stafford |
Renaming my guest directory to the more conventional sam_stafford. |
||
//guest/samwise/p4hl/src/dlls/clientuser.cpp | |||||
#1 | 936 | Sam Stafford |
Adding P4HL to the public depot. See relnotes.txt for installation instructions; all relevant files are under p4hl/dist. Source code is under p4hl/src in the form of a VC++ project. |