clientinit.cc #1

  • //
  • guest/
  • perforce_software/
  • p4/
  • 2017-1/
  • client/
  • clientinit.cc
  • View
  • Commits
  • Open Download .zip Download (13 KB)
/*
 * Copyright 1995, 2000 Perforce Software.  All rights reserved.
 *
 * This file is part of Perforce - the FAST SCM System.
 */

/*
 * clientinit() - Perforce client DVCS init
 *
 */

# define NEED_CHDIR
# define NEED_TYPES
# define NEED_STAT
# define NEED_FLOCK

# include <stdhdrs.h>

# include <strbuf.h>
# include <strdict.h>
# include <strtable.h>
# include <error.h>
# include <options.h>
# include <rpc.h>

# include <filesys.h>
# include <handler.h>

# include <msgclient.h>

# include "client.h"
# include "clientuser.h"
# include "serverhelper.h"

static ErrorId cloneHelp = { ErrorOf( 0, 0, E_INFO, 0, 0),
"\n"
"	clone -- Clone a new personal server from a shared server\n"
"\n"
"	p4 [-u user][-d dir][-c client] clone [-m depth -v -p port] -r remote\n"
"	p4 [-u user][-d dir][-c client] clone [-m depth -v -p port] [-f] file\n"
"\n"
"	This command initializes a new Perforce repository in the current\n"
"	directory (or the directory specified by the -d flag).\n"
"\n"
"	In order to run 'p4 clone', you must have up-to-date and matching\n"
"	versions of the 'p4' and 'p4d' executables in your operating system\n"
"	path. You can download these executables from http://www.perforce.com\n"
"\n"
"	Perforce database files will be stored in the directory named\n"
"	\".p4root\". Perforce configuration settings will be stored in\n"
"	P4CONFIG and P4IGNORE files in the top level of your directory.\n"
"	It is not necessary to view or update these files, but you should\n"
"	be aware that they exist.\n"
"\n"
"	The -m flag performs a shallow fetch; only the last number of\n"
"	specified revisions of each file are fetched.\n"
"\n"
"	The -p flag specifies the address of the target server you wish\n"
"	to clone from, overriding the value of $P4PORT in the environment\n"
"	and the default (perforce:1666).\n"
"\n"
"	The -r flag specifies a remote spec installed on the remote server\n"
"	to use as a template for the clone and stream setup. See 'p4 help\n"
"	remote' for more information about how the clone command interprets\n"
"	the remote spec during the setup steps.\n"
"\n"
"	The -f flag specifies a file pattern in the remote server to use as\n"
"	the path to clone; this file specification will also be used to\n"
"	determine the stream setup in the local server.\n"
"\n"
"	The -v flag specifies verbose mode.\n"
"\n"
"	When the clone completes, a default remote spec will be created,\n"
"	called origin. To update your local repository with the latest\n"
"	changes from the target server, run 'p4 fetch'.\n"
"\n"
"	For more information about using Perforce, run 'p4 help' after you\n"
"	have run 'p4 clone', or visit http://www.perforce.com.\n"
};

static ErrorId initHelp = { ErrorOf( 0, 0, E_INFO, 0, 0),
"\n"
"	init -- Initialize a new personal server.\n"
"\n"
"	p4 [-u user][-d dir][-c client] init [-hq][-c stream][-p port]\n"
"	p4 [-u user][-d dir][-c client] init [-hq][-c stream][-Cx -xi -n]\n"
"\n"
"	This command initializes a new Perforce repository in the current\n"
"	directory (or the directory specified by the -d flag).\n"
"\n"
"	In order to run 'p4 init', you must have up-to-date and matching\n"
"	versions of the 'p4' and 'p4d' executables in your operating system\n"
"	path. You can download these executables from http://www.perforce.com\n"
"\n"
"	Perforce database files will be stored in the directory named\n"
"	\".p4root\". Perforce configuration settings will be stored in\n"
"	P4CONFIG and P4IGNORE files in the top level of your directory.\n"
"	It is not necessary to view or update these files, but you should\n"
"	be aware that they exist.\n"
"\n"
"	After initializing your new repository, run 'p4 reconcile' to mark\n"
"	all of your source files to be added to Perforce, then 'p4 submit'\n"
"	to submit them.\n"
"\n"
"	The -c flag configures the installation to create the specified stream\n"
"	as the mainline stream rather than the default '//stream/main'.\n"
"\n"
"	The -p flag specifies the address of a target server you wish to\n"
"	discover the case sensitivity and unicode settings from. This will\n"
"	make your local repository compatible with that server.\n"
"\n"
"	The -q flag suppresses informational messages (including if\n"
"	p4 init has already been run.)\n"
"\n"
"	The -Cx flag sets the case sensitivity of the new Perforce\n"
"	installation. You may specify either -C0 or -C1. The -C0 flag\n"
"	specifies case-sensitive operation; the -C1 flag specifies\n"
"	case-insensitive operation. (See discovery note below).\n"
"\n"
"	The -n flag configures the installation without unicode support.\n"
"	The -xi flag configures the installation with unicode support.\n"
"	Without -n or -xi, the installation will decide on unicode support\n"
"	based on the P4CHARSET enviroment being set.\n"
"\n"
"	Note: If neither -Cx, -xi or -n flags are set, init will try and\n"
"	discover the correct settings from a server already set in your\n"
"	environment.  If init cannot find a server then it will fail\n"
"	initialization.\n"
"\n"
"	For more information about using Perforce, run 'p4 help' after you\n"
"	have run 'p4 init', or visit http://www.perforce.com.\n"
};

static ErrorId initUsage = { ErrorOf( 0, 0, E_FAILED, 0, 0),
	"Usage: p4 [-u user][-d dir][-c client] init [-hq][-c stream][-p port | -Cx -xi -n]\n"
	"\tUse p4 init -h for detailed help."
};

static ErrorId cloneUsage = { ErrorOf( 0, 0, E_FAILED, 0, 0),
	"Usage: p4 [-u user][-d dir][-c client] clone [-p <port>] [-m depth -v][-r <remote> | [-f] filespec]\n"
	"\tUse p4 clone -h for detailed help."
};

static ErrorId initUnicodeMixup = { ErrorOf( 0, 0, E_FAILED, 0, 0),
	"p4 init does not allow '-n' and '-xi' to be used together"
};

static ErrorId initCaseFlagMixup = { ErrorOf( 0, 0, E_FAILED, 0, 0),
	"Specify either -C0 or -C1."
};

static ErrorId initServerFail = { ErrorOf( 0, 0, E_FAILED, 0, 0),
	"\np4d server failed to initialize.  A 2015.1 or later p4d server\n"
	"must be in your path and runable."
};

static ErrorId pasiveDiscoverFail  = { ErrorOf( 0, 0, E_FAILED, 0, 0 ),
	"No available server to discover configuration, needs flags to\n"
	"specify case sensitivity and Unicode settings:\n"
	"p4 init [-C0|-C1]   (case-sensitive or case-insensitive)\n"
	"p4 init [-xi|-n]    (with Unicode support or without it)\n"
	"Note, it is important to initialize with the same case sensitivity\n"
	"and Unicode settings as the server you wish to push/fetch from.\n"
	"For more information, run: 'p4 help init'."
};


static ErrorId discoverSucceeded   = { ErrorOf( 0, 0, E_INFO, 0, 3 ),
    "Matching server configuration from '%port%':\n"
    "%case%, %unicode%"
};

static ErrorId discoverUnknownUser = { ErrorOf( 0, 0, E_WARN, 0, 1 ),
    "warning: your user '%user%' unknown at this server!"
};

static void
SetProgAndVersion( ServerHelper *clone )
{
	StrBuf v;
	v << ID_REL "/" << ID_OS << "/" << ID_PATCH;
	clone->SetVersion( v.Text() );
	clone->SetProg( "p4" );
}

int
clientInitHelp( int doClone, Error *e )
{
	ClientUser cuser;
	e->Set( doClone ? cloneHelp : initHelp );
	cuser.Message( e );
	e->Clear();
	return 0;
}


int clientInitInit( int ac, char **av, Options &preops, Error *e );
int clientInitClone( int ac, char **av, Options &preops, Error *e );

int
clientInit( int ac, char **av, Options &preops, int doClone, Error *e )
{
	if( doClone )
	    return clientInitClone( ac, av, preops, e );
	else
	    return clientInitInit(  ac, av, preops, e );
}


int
clientInitInit( int ac, char **av, Options &preops, Error *e )
{
	Options opts;
	ClientUserProgress cuser;
	StrPtr *sp;
	StrPtr *port = 0;
	int unicode = -1;
	StrPtr *caseflag = 0;
	int ecode = 0;
	Error e1;

	int longOpts[] = { Options::Help, Options::Quiet, 0 };

	opts.ParseLong( ac, av, "hp:c:nqC#x:", longOpts, OPT_NONE, initUsage,
	                e );

	if( e->Test() )
	    return 1;

	if( opts[ 'h' ] )
	    return clientInitHelp( 0, e );

	if( opts['p'] && ( opts['x'] || opts['n'] || opts['C'] ) )
	    e->Set( initUsage );
	
	if( e->Test() )
	    return 1;

	ServerHelper ruser( e );
	if( e->Test() )
	    return 1;
	
	SetProgAndVersion( &ruser );

	// Set the DVCS directory
	if( ( sp = preops['d'] ) )
	    ruser.SetDvcsDir( sp );
	
	// Set the P4CHARSET
	if( ( sp = preops[ 'C' ] ) )
	    ruser.SetCharset( sp );

	if( ( sp = preops['v'] ) )
	    ruser.DoDebug( sp );

	// turn-on quiet mode
	if( opts['q'] )
	    ruser.SetQuiet();

	// Set the port
	if( ( sp = opts['p'] ) || ( sp = preops['p'] ))
	    port = sp;
	
	// Was case sensitivity specified on the commandline?
	if( ( caseflag = opts['C'] ) )
	{
	    // Validate it now (it'll be overriden by the Discover())
	    ruser.SetCaseFlag( caseflag, e );
	    if( e->Test() )
	    {
	        e->Clear();
	        e->Set( initCaseFlagMixup );
	    }
	}
	
	// Set the default stream
	if( !e->Test() && ( sp = opts['c'] ) )
	    ruser.SetDefaultStream( sp, e );

	if( e->Test() )
	    goto finish;

	// Was unicode specified on the commandline?
	if( ( sp = opts[ 'x' ] ) && sp->Text()[0] == 'i' )
	{
	    unicode = 1;
	    if( opts[ 'n' ] )
	    {
	        e->Set( initUnicodeMixup );
	        goto finish;
	    }
	}
	if( opts[ 'n' ] )
	    unicode = 0;

	// Set the username and client
	// If null pointers are passed, the values come from the envrionment
	ruser.SetUserClient( preops['u'], preops['c'] );

	// check if already initialized
	// P4INITROOT and .p4root dir
	if( ruser.Exists( &cuser, &e1 ) || ruser.GotError() )
	{
	    ecode = 1;
	    goto finish;
	}

	// If there're any gaps in our configuration, query the server to
	// fill in the gaps

	if( !( ( opts['x'] || opts['n'] ) && opts['C'] ) )
	    ruser.Discover( port, &cuser, &e1 );

	if( unicode >= 0 )
	    ruser.SetUnicode( unicode );
	if( caseflag )
	    ruser.SetCaseFlag( caseflag, e );

	if( !( ( opts['x'] || opts['n'] ) && opts['C'] ) )
	{
	    if( ruser.GotError() )
	    {
	        if( !opts['p'] )
	        {
	            // passive discovery failed, require options
	            ruser.ClearError();
	            e->Clear();
	            e->Set( pasiveDiscoverFail );
	        }
	        ecode = 1;
	        goto finish;
	    }
	    else if( !e->Test() )
	    {
	        e->Set( discoverSucceeded );
	        *e << ruser.Server();
	        if( ruser.GetCaseFlag() == "-C0" )
	            *e << "case-sensitive (-C0)";
	        else
	            *e << "case-insensitive (-C1)";
	        if( ruser.Unicode() )
	            *e << "unicode (-xi)";
	        else
	            *e << "non-unicode (-n)";
	        cuser.Message( e );
	        e->Clear();

	        if( ruser.UserName() == "*unknown*" )
	        {
	            e->Set( discoverUnknownUser ) << ruser.GetUser();
	            cuser.Message( e );
	            e->Clear();
	        }
	    }
	}
	else
	    ruser.ClearError();
	
	// Error messages flush before printing, but info's do not
	// flush now so that the message order is correct.
	fflush( stderr );

	// Make it happen...
	ecode = ruser.InitLocalServer( &cuser, &e1 );

finish:
	if( e->Test() )
	{
	    cuser.Message( e );
	    ecode = 1;
	    e->Clear();
	}
	else if( ruser.GotError() )
	    cuser.Message( ruser.GetError() );

	return ecode;
}

int
clientInitClone( int ac, char **av, Options &preops, Error *e )
{
	Options opts;
	ClientUserProgress cuser;
	StrPtr *sp;
	StrPtr *port = 0;
	int depth = 0;
	int ecode = 0;
	Error e1;

	int longOpts[] = { Options::Help, Options::Quiet, 0 };

	opts.ParseLong( ac, av, "Ahp:r:f:m#v", longOpts, OPT_OPT, cloneUsage,
	                e );

	if( e->Test() )
	    return 1;

	if( opts[ 'h' ] )
	    return clientInitHelp( 1, e );

	StrPtr *remoteSpec = opts[ 'r' ];
	StrPtr *fileSpec   = opts[ 'f' ];
	StrBuf filePattern;

	if( ( fileSpec || remoteSpec ) && ac )
	{
	    e->Set( cloneUsage );
	    return 1;
	}

	if( !fileSpec && ac )
	{
	    filePattern.Set( av[0] );
	    fileSpec = &filePattern;
	}

	if( (  remoteSpec &&  fileSpec ) ||
	    ( !remoteSpec && !fileSpec ) )
	        e->Set( cloneUsage );
	
	if( e->Test() )
	    return 1;

	ServerHelper ruser( e );
	if( e->Test() )
	    return 1;
	
	SetProgAndVersion( &ruser );

	// Set the DVCS directory
	if( ( sp = preops['d'] ) )
	    ruser.SetDvcsDir( sp );

	// Set the P4PASSWD
	if( ( sp = preops[ 'P' ] ) )
	    ruser.SetPassword( sp );

	// Set the P4CHARSET
	if( ( sp = preops[ 'C' ] ) )
	    ruser.SetCharset( sp );

	// turn-off quiet mode
	if( ( sp = preops['v'] ) )
	    ruser.DoDebug( sp );

	// Set the port
	if( ( sp = opts['p'] ) || ( sp = preops['p'] ) )
	    port = sp;

	// Set the clone depth
	if( ( sp = opts['m'] ) )
	    depth = sp->Atoi();

	// Set the username and client
	// If null pointers are passed, the values come from the envrionment
	ruser.SetUserClient( preops['u'], preops['c'] );

	// check if already initialized
	// P4INITROOT and .p4root dir
	if( ruser.Exists( &cuser, &e1 ) || ruser.GotError() )
	{
	    ecode = 1;
	    goto finish;
	}
	
	// Get the remote, or build our own
	if( remoteSpec )
	    ruser.LoadRemote( port, remoteSpec, &cuser, &e1 );
	if( fileSpec )
	    ruser.MakeRemote( port, fileSpec, &cuser, &e1 );

	if( ruser.GotError() )
	{
	    ecode = 1;
	    goto finish;
	}

	// Make it happen...

	if( !( ecode = ruser.InitLocalServer( &cuser, &e1 ) ) )
	{
	    if( !e->Test() )
	        ruser.FirstFetch( depth, opts[ 'A' ] ? 1 : 0, opts['v'],
	                          &cuser, &e1 );
	}

finish:
	if( e->Test() )
	{
	    cuser.Message( e );
	    ecode = 1;
	    e->Clear();
	}
	else if( ruser.GotError() )
	    cuser.Message( ruser.GetError() );

	return ecode;
}
# Change User Description Committed
#2 23015 Nick Poole Update 2017.1 code drop
#1 22288 mark_mears import 2017.1 code drop