#include <windows.h>

#if defined(DEBUG) && !defined(NDEBUG)
#define NDEBUG
#endif

#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <conio.h>
#include <tchar.h>

#include "jet.h"
#include "ntverp.h"

#include "_edbutil.hxx"
#include "utilmsg.h"		// UNDONE: Header file generated by MC, which currently only generates .h files.

#include "esefile.hxx"
#undef UNICODE				//	esefile.hxx enables UNICODE


#include <esebcli2.h>
#define ESEBCLI2_DLL_NAME	"ESEBCLI2.DLL"

// the sever simulation functionality will be available in
// both debug and retail but the help will be DEBUG only
// (like an unsupported functionality)
#define RESTORE_SERVER_SIMULATION

#ifdef DEBUG
#define RESTORE_SERVER_SIMULATION_HELP
#endif

#ifdef RESTORE_SERVER_SIMULATION
#include <eseback2.h>
#define ESEBACK2_DLL_NAME	"ESEBACK2.DLL"
#endif // RESTORE_SERVER_SIMULATION

#ifdef DEBUG
#define RECORD_FORMAT_UPGRADE
#endif

//#define LOG_SHIPPING


//	use this to protect invalid code paths that PREfix complains about

#ifdef _PREFIX_
#define AssertPREFIX( exp )			( ( exp ) ? (void)0 : exit(-1) )
#else
#define AssertPREFIX( exp )			assert( exp )
#endif


LOCAL const char	* const szUser			= "user";
LOCAL const char	* const szPassword		= "";


// UNDONE:  Must still be localised, but centralising all the localisable strings
// here makes my job easier.

LOCAL const char	* const	szCurrDir					= ".";

LOCAL const char	* const szStreamingFileExt			= "STM";

LOCAL const char	* const szDefaultTempDBFormat		= "TEMP%d.EDB";
LOCAL const char	* const szDefaultDefragDBFormat		= "TEMPDFRG%d.EDB";
LOCAL const char	* const szDefaultUpgradeDBFormat	= "TEMPUPGD%d.EDB";
LOCAL const char	* const szDefaultRepairDBFormat		= "TEMPREPAIR%d.EDB";
LOCAL const char	* const szDefaultIntegDBFormat		= "TEMPINTEG%d.EDB";
LOCAL const char	* const szDefaultScrubDBFormat		= "TEMPSCRUB%d.EDB";

LOCAL char szDefaultTempDB[64];
LOCAL char szDefaultDefragDB[64];
LOCAL char szDefaultUpgradeDB[64];
LOCAL char szDefaultRepairDB[64];
LOCAL char szDefaultIntegDB[64];
LOCAL char szDefaultScrubDB[64];

LOCAL char szSourceStreamFileName[ _MAX_PATH + 1 ];
LOCAL char szTempStreamFileName[ _MAX_PATH + 1 ];

LOCAL const char	* const szDefrag				= "Defragmentation";
LOCAL const char	* const szUpgrade				= "Upgrade";
LOCAL const char	* const szRestore				= "Restore";
LOCAL const char	* const szRepair				= "Scanning";
LOCAL const char	* const szScrub					= "Securing";
LOCAL const char	* const szUpgradeRecordFormat	= "Upgrading Record Format";
LOCAL const char	* const szIntegrity				= "Integrity";
LOCAL const char	* const szChecksum				= "Checksum";

LOCAL const char	* const szStatusMsg		= " Status (% complete)";

LOCAL const char	* const szMoveFile		= "Moving '%s' to '%s'...";
LOCAL const char	* const szMoveDone		= " DONE!";
LOCAL const char	* const szMoveFailed	= " FAILED!";
LOCAL const char	* const szCopyFile		= " Copying...";
LOCAL const char	* const szCopyFileStatus= "File Copy";

LOCAL const char	cNewLine				= '\n';

LOCAL const char	* const szSwitches		= "-/";

LOCAL const char	* const szRegistryMsg	= "Using Registry environment...";

LOCAL const char	* const szErr1			= "Error: Source database specification '%s' is invalid.";
LOCAL const char	* const szErr2			= "Error: Temporary database specification '%s' is invalid.";
LOCAL const char	* const szErr3			= "Error: Source database '%s' cannot be the same as the temporary database.";
LOCAL const char	* const szErr4			= "Error: Could not backup to '%s'.";
LOCAL const char	* const szErr5			= "Error: Could not re-instate '%s'. It may be manually re-instated by manually copying '%s' to '%s' (this will overwrite the original copy of the file with the defragmented copy).";
LOCAL const char	* const szErr6			= "Error: Failed loading Registry Environment.";

LOCAL const _TCHAR	* const szErr7			= _T( "Error: Failed to load DLL %s.\r\n" );
LOCAL const _TCHAR	* const szErr8			= _T( "Error: Failed to load function %s from DLL.\r\n" );

LOCAL const char	* const szUsageErr1		= "Usage Error: Missing %s specification.";
LOCAL const char	* const szUsageErr2		= "Usage Error: Duplicate %s specification.";
LOCAL const char	* const szUsageErr3		= "Usage Error: Only one type of recovery allowed.";
LOCAL const char	* const szUsageErr4		= "Usage Error: Invalid option '%s'.";
LOCAL const char	* const szUsageErr5		= "Usage Error: Invalid argument '%s'. Options must be preceded by '-' or '/'.";
LOCAL const char	* const szUsageErr6		= "Usage Error: Invalid buffer cache size.";
LOCAL const char	* const szUsageErr7		= "Usage Error: Invalid batch I/O size.";
LOCAL const char	* const szUsageErr8		= "Usage Error: Invalid database extension size.";
LOCAL const char	* const szUsageErr9		= "Usage Error: No mode specified.";
LOCAL const char	* const szUsageErr10	= "Usage Error: Mode selection must be preceded by '-' or '/'.";
LOCAL const char	* const szUsageErr11	= "Usage Error: Old .DLL required in order to upgrade.";
LOCAL const char	* const szUsageErr12	= "Usage Error: Invalid mode.";
LOCAL const char	* const szUsageErr13	= "Usage Error: Invalid node/lgpos specification.";
LOCAL const char	* const szUsageErr14	= "Usage Error: Invalid logfile size: %s kbytes.";

#ifdef ESENT
LOCAL const char	* const szHelpDesc1		= "DESCRIPTION:  Maintenance utilities for Microsoft(R) Windows databases.";
#else  //  !ESENT
LOCAL const char	* const szHelpDesc1		= "DESCRIPTION:  Maintenance utilities for Microsoft(R) Exchange Server databases.";
#endif  //  ESENT
LOCAL const char	* const szHelpSyntax	= "MODES OF OPERATION:";
LOCAL const char	* const szHelpModes1	= "      Defragmentation:  %s /d <database name> [options]";
LOCAL const char	* const szHelpModes2	= "             Recovery:  %s /r [options]";
LOCAL const char	* const szHelpModes3	= "            Integrity:  %s /g <database name> [options]";
LOCAL const char	* const szHelpModes5	= "             Checksum:  %s /k <database name> [options]";
LOCAL const char	* const szHelpModes4	= "               Repair:  %s /p <database name> [options]";
LOCAL const char	* const szHelpModes6	= "            File Dump:  %s /m[mode-modifier] <filename>";
LOCAL const char	* const szHelpModes7	= "              Restore:  %s /c[mode-modifier] <path name> [options]";
LOCAL const char	* const szHelpModes8	= "               Backup:  %s /b <backup path> [options]";
LOCAL const char	* const szHelpModes9	= "             SLV Move:  %s /s <database name> /p<page>";
LOCAL const char	* const szHelpModes10	= "Record format Upgrade:  %s /f <database name>";
LOCAL const char	* const szHelpModes11	= "              Upgrade:  %s /u <database name> [options]";
LOCAL const char	* const	szHelpModes12	= "               Secure:  %s /z <database name> [options]";

LOCAL const char	* const szHelpPrompt1	= "<<<<<  Press a key for more help  >>>>>";
LOCAL const char	* const szHelpPrompt2	= "D=Defragmentation, R=Recovery, G=inteGrity, K=checKsum, P=rePair, M=file duMp";
#ifdef DEBUG
LOCAL const char	* const szHelpPrompt3	= "C=restore, B=Backup, S=Slv move, F=record Format upgrade, U=upgrade, Z=secure";
#endif
LOCAL const char	* const szHelpPromptCursor	= "=> ";

LOCAL const char	szRepairMessage[]		=
	"You should only run Repair on damaged or corrupted databases. Repair will not apply information in the "
	"transaction log files to the database, and may cause information to be lost. Do you wish to proceed?";

LOCAL const char	szRepairCaption[]		= "Warning";

LOCAL const char * const	szOperSuccess			= "Operation completed successfully in %d.%d seconds.";
LOCAL const char * const	szOperWarn				= "Operation completed successfully with %d (%s) after %d.%d seconds.";
LOCAL const char * const	szOperFailWithError		= "Operation terminated with error %d (%s) after %d.%d seconds.";
LOCAL const char * const	szOperFailUnknownError	= "Operation terminated unsuccessfully after %d.%d seconds.";

#define cbMsgBufMax	256

extern void JetErrorToString( JET_ERR err, const char **szError, const char **szErrorText );

typedef JET_ERR (__stdcall FNJETINIT)( JET_INSTANCE *pinstance );               
typedef JET_ERR (__stdcall FNJETINIT2)( JET_INSTANCE *pinstance, JET_GRBIT grbit );               
typedef JET_ERR (__stdcall FNJETTERM2)( JET_INSTANCE instance, JET_GRBIT grbit );
typedef JET_ERR (__stdcall FNJETSETSYSTEMPARAMETER)(                          
                                        JET_INSTANCE *pinstance,              
                                        JET_SESID sesid,                      
                                        unsigned long paramid,                
                                        JET_API_PTR lParam,                 
                                        const char *sz );                      
typedef JET_ERR (__stdcall FNJETGETSYSTEMPARAMETER)(
										JET_INSTANCE	instance,
										JET_SESID		sesid,
										unsigned long	paramid,
										JET_API_PTR		*plParam,
										char			*sz,
										unsigned long	cbMax );
typedef JET_ERR (__stdcall FNJETCOMPACT)(
										JET_SESID		sesid,
										const char		*szDatabaseSrc,
										const char		*szDatabaseDest,
										JET_PFNSTATUS	pfnStatus,
										JET_CONVERT		*pconvert,
										JET_GRBIT		grbit );
typedef JET_ERR (__stdcall FNJETBEGINSESSION)(
										JET_INSTANCE	instance,
										JET_SESID		*psesid,
										const char		*szUsername,
										const char		*szPassword );
typedef JET_ERR (__stdcall FNJETDETACHDATABASE)(
										JET_SESID sesid,
										const char *szFilename );
typedef JET_ERR (__stdcall FNJETENDSESSION)( JET_SESID sesid, JET_GRBIT grbit );
typedef JET_ERR (__stdcall FNJETRESTORE2)(
										const char *sz,
										const char *szDest,
										JET_PFNSTATUS pfn );
typedef JET_ERR (__stdcall FNJETBACKUP)(
										const char		*szBackupPath,
										JET_GRBIT		grbit,
										JET_PFNSTATUS	pfnStatus );
typedef JET_ERR (__stdcall FNJETDBUTILITIES)( JET_DBUTIL *pdbutil );

LOCAL HMODULE					g_hmod 	= NULL;

LOCAL FNJETINIT					* g_pfnJetInit 				= JetInit;
LOCAL FNJETINIT2   				* g_pfnJetInit2				= JetInit2;
LOCAL FNJETTERM2				* g_pfnJetTerm2				= JetTerm2;
LOCAL FNJETSETSYSTEMPARAMETER	* g_pfnJetSetSystemParameter= JetSetSystemParameter;
LOCAL FNJETGETSYSTEMPARAMETER	* g_pfnJetGetSystemParameter= JetGetSystemParameter;
LOCAL FNJETCOMPACT				* g_pfnJetCompact			= JetCompact;
LOCAL FNJETBEGINSESSION			* g_pfnJetBeginSession		= JetBeginSession;
LOCAL FNJETDETACHDATABASE		* g_pfnJetDetachDatabase	= JetDetachDatabase;
LOCAL FNJETENDSESSION			* g_pfnJetEndSession		= JetEndSession;
LOCAL FNJETRESTORE2				* g_pfnJetRestore2			= JetRestore2;
LOCAL FNJETBACKUP				* g_pfnJetBackup			= JetBackup;
LOCAL FNJETDBUTILITIES			* g_pfnJetDBUtilities		= JetDBUtilities;

#define JetInit (*g_pfnJetInit)
#define JetInit2 (*g_pfnJetInit2)
#define JetTerm2 (*g_pfnJetTerm2)
#define JetSetSystemParameter (*g_pfnJetSetSystemParameter)
#define JetGetSystemParameter (*g_pfnJetGetSystemParameter)
#define JetCompact (*g_pfnJetCompact)
#define JetBeginSession (*g_pfnJetBeginSession)
#define JetDetachDatabase (*g_pfnJetDetachDatabase)
#define JetEndSession (*g_pfnJetEndSession)
#define JetRestore2 (*g_pfnJetRestore2)
#define JetBackup (*g_pfnJetBackup)
#define JetDBUtilities (*g_pfnJetDBUtilities)

const _TCHAR * const szJetInit 					= _T( "JetInit");
const _TCHAR * const szJetInit2 				= _T( "JetInit2");
const _TCHAR * const szJetTerm2 				= _T( "JetTerm2");
const _TCHAR * const szJetSetSystemParameter	= _T( "JetSetSystemParameter");
const _TCHAR * const szJetGetSystemParameter	= _T( "JetGetSystemParameter");
const _TCHAR * const szJetCompact				= _T( "JetCompact");
const _TCHAR * const szJetBeginSession			= _T( "JetBeginSession");
const _TCHAR * const szJetDetachDatabase		= _T( "JetDetachDatabase");
const _TCHAR * const szJetEndSession			= _T( "JetEndSession");
const _TCHAR * const szJetRestore2				= _T( "JetRestore2");
const _TCHAR * const szJetBackup				= _T( "JetBackup");
const _TCHAR * const szJetDBUtilities			= _T( "JetDBUtilities");

//  ================================================================
JET_ERR ErrGetProcAddress(
			const HMODULE hmod,
			const _TCHAR * const szFunc,
			VOID ** const pvPfn )
//  ================================================================
	{
	if( NULL == ( (*pvPfn) = GetProcAddress( hmod, szFunc ) ) )
		{
		_tprintf( szErr8, szFunc );
		return JET_errCallbackNotResolved;
		}
	return JET_errSuccess;
	}

//  ================================================================
JET_ERR ErrUseDifferentDLL(	const _TCHAR * const szLibrary )
//  ================================================================
	{
	JET_ERR 		err;

	assert( NULL == g_hmod );

	_tprintf( "Loading %s...\r\n", szLibrary );
	
	g_hmod = LoadLibraryEx( szLibrary, NULL, 0 );
	if( NULL == g_hmod )
		{
		_tprintf( szErr7, szLibrary );
		Call( JET_errCallbackNotResolved );
		}

	Call( ErrGetProcAddress( g_hmod, szJetInit, (VOID **)&g_pfnJetInit ) );
///	Call( ErrGetProcAddress( g_hmod, szJetInit2, (VOID **)&g_pfnJetInit2 ) );
	Call( ErrGetProcAddress( g_hmod, szJetTerm2, (VOID **)&g_pfnJetTerm2 ) );
	Call( ErrGetProcAddress( g_hmod, szJetSetSystemParameter, (VOID **)&g_pfnJetSetSystemParameter ) );
	Call( ErrGetProcAddress( g_hmod, szJetGetSystemParameter, (VOID **)&g_pfnJetGetSystemParameter ) );
	Call( ErrGetProcAddress( g_hmod, szJetCompact, (VOID **)&g_pfnJetCompact ) );
	Call( ErrGetProcAddress( g_hmod, szJetBeginSession, (VOID **)&g_pfnJetBeginSession ) );
	Call( ErrGetProcAddress( g_hmod, szJetDetachDatabase, (VOID **)&g_pfnJetDetachDatabase ) );
	Call( ErrGetProcAddress( g_hmod, szJetEndSession, (VOID **)&g_pfnJetEndSession ) );
	Call( ErrGetProcAddress( g_hmod, szJetRestore2, (VOID **)&g_pfnJetRestore2 ) );
	Call( ErrGetProcAddress( g_hmod, szJetBackup, (VOID **)&g_pfnJetBackup ) );
	Call( ErrGetProcAddress( g_hmod, szJetDBUtilities, (VOID **)&g_pfnJetDBUtilities ) );

HandleError:
	return err;
	}
	
LOCAL CHAR	*GetNextArg();
LOCAL CHAR	*GetPrevArg();
LOCAL VOID	SetCurArgID( const INT id );
LOCAL INT	GetArgCount();
LOCAL INT	GetCurArgID();
LOCAL CHAR	*GetCurArg();

INLINE VOID EDBUTLFormatMessage( ULONG ulMsgId, CHAR *szMsgBuf, CHAR *szMsgArg )
	{
	DWORD	err;
	CHAR	*rgszMsgArgs[1] = { szMsgArg };		// Currently only support one argument.
	
	if ( szMsgArg )
		{
		err = FormatMessage(
			FORMAT_MESSAGE_FROM_HMODULE|FORMAT_MESSAGE_ARGUMENT_ARRAY,
			NULL,
			ulMsgId,
			LANG_USER_DEFAULT,
			szMsgBuf,
			cbMsgBufMax,
			reinterpret_cast<va_list *>( rgszMsgArgs )  );
		}
	else
		{
		err = FormatMessage(
			FORMAT_MESSAGE_FROM_HMODULE,
			NULL,
			ulMsgId,
			LANG_USER_DEFAULT,
			szMsgBuf,
			cbMsgBufMax,
			NULL );
		}
	if ( err == 0 )
		{
		// Format message failed.  No choice but to dump the error message
		// in English, then bail out.
		printf( "Unexpected Win32 error: %dL\n\n", GetLastError() );
		exit(1);
		}
	}
	

// Allocates a local buffer for the message, retrieves the message, and prints it out.
LOCAL VOID EDBUTLPrintMessage( ULONG ulMsgId, CHAR *szMsgArg )
	{
	CHAR	szMsgBuf[cbMsgBufMax];

	EDBUTLFormatMessage( ulMsgId, szMsgBuf, szMsgArg );
	printf( szMsgBuf );
	}
		

LOCAL VOID EDBUTLPrintLogo( void )
	{
	CHAR	szVersion[8];

	sprintf( szVersion, "%d.%d", VER_PRODUCTMAJORVERSION, VER_PRODUCTMINORVERSION );
	
#ifdef ESENT
	EDBUTLPrintMessage( NTPRODUCTNAME_ID, NULL );
#else  //  !ESENT
	EDBUTLPrintMessage( PRODUCTNAME_ID, NULL );
#endif  //  ESENT
	EDBUTLPrintMessage( VERSION_ID, szVersion );
	EDBUTLPrintMessage( COPYRIGHT_ID, NULL );
	}

LOCAL VOID EDBUTLHelpDefrag( char *szAppName )
	{
	printf( "%c", cNewLine );
	printf( "DEFRAGMENTATION/COMPACTION:%c", cNewLine );
	printf( "    DESCRIPTION:  Performs off-line compaction of a database.%c", cNewLine );
	printf( "         SYNTAX:  %s /d <database name> [options]%c", szAppName, cNewLine );
	printf( "     PARAMETERS:  <database name> - filename of database to compact%c", cNewLine );
	printf( "        OPTIONS:  zero or more of the following switches, separated by a space:%c", cNewLine );
	printf( "                  /s<file>   - set streaming file name (default: NONE)%c", cNewLine );
	printf( "                  /t<db>     - set temp. database name (default: TEMPDFRG*.EDB)%c", cNewLine );
	printf( "                  /f<file>   - set temp. streaming file name%c", cNewLine );
	printf( "                               (default: TEMPDFRG*.STM)%c", cNewLine );
	printf( "                  /i         - do not defragment streaming file%c", cNewLine );
	printf( "                  /p         - preserve temporary database (ie. don't instate)%c", cNewLine );
	printf( "                  /b<db>     - make backup copy under the specified name%c", cNewLine );
#ifdef DEBUG	// Undocumented switches.
	printf( "                  /n         - dump defragmentation information to DFRGINFO.TXT%c", cNewLine );
	printf( "                  /x<#>      - database extension size, in pages (default: 256)%c", cNewLine );
	printf( "                  /w         - set batch IO size%c", cNewLine );
#endif	
	printf( "                  /8         - set 8k database page size (default: auto-detect)%c", cNewLine );
	printf( "                  /o         - suppress logo%c", cNewLine );
	printf( "          NOTES:  1) If instating is disabled (ie. /p), the original database%c", cNewLine );
	printf( "                     is preserved uncompacted, and the temporary database will%c", cNewLine );
	printf( "                     contain the defragmented version of the database.%c", cNewLine );
	}

LOCAL VOID EDBUTLHelpRecovery( char *szAppName )
	{
	printf( "%c", cNewLine );
	printf( "RECOVERY:%c", cNewLine );
	printf( "    DESCRIPTION:  Performs recovery, bringing all databases to a%c", cNewLine );
	printf( "                  consistent state.%c", cNewLine );
	printf( "         SYNTAX:  %s /r <3-character logfile base name> [options]%c", szAppName, cNewLine );
	printf( "        OPTIONS:  zero or more of the following switches, separated by a space:%c", cNewLine );
	printf( "                  /l<path>   - location of log files%c", cNewLine );
	printf( "                               (default: current directory)%c", cNewLine );
	printf( "                  /s<path>   - location of system files (eg. checkpoint file)%c", cNewLine );
	printf( "                               (default: current directory)%c", cNewLine );
#ifdef LOG_SHIPPING	
	printf( "                  /f         - replaying replicated log files%c", cNewLine );
#endif	
	printf( "                  /i         - ignore mismatched/missing database attachments%c", cNewLine );
	printf( "                  /d[path]   - location of database files, or current directory%c", cNewLine );
	printf( "                               if [path] not specified (default: directory%c", cNewLine );
	printf( "                               originally logged in log files)%c", cNewLine ); 
#ifdef DEBUG
	printf( "                  /b<path>   - restore from backup (ie. hard recovery) from the%c", cNewLine );
	printf( "                               specified location%c", cNewLine );
	printf( "                  /r<path>   - restore to specified location (only valid when%c", cNewLine );
	printf( "                               the /b switch is also specified)%c", cNewLine );
#endif
	printf( "                  /8         - set 8k database page size (default: auto-detect)%c", cNewLine );
	printf( "                  /o         - suppress logo%c", cNewLine );
#ifdef DEBUG
	printf( "          NOTES:  1) Soft recovery is always performed unless the /b switch is%c", cNewLine );
	printf( "                     specified, in which case hard recovery is performed.%c", cNewLine );
#endif
	}

LOCAL VOID EDBUTLHelpIntegrity( char *szAppName )
	{
	printf( "%c", cNewLine );
	printf( "INTEGRITY:%c", cNewLine );
	printf( "    DESCRIPTION:  Verifies integrity of a database.%c", cNewLine );
	printf( "         SYNTAX:  %s /g <database name> [options]%c", szAppName, cNewLine );
	printf( "     PARAMETERS:  <database name> - filename of database to verify%c", cNewLine );
	printf( "        OPTIONS:  zero or more of the following switches, separated by a space:%c", cNewLine );
	printf( "                  /s<file>  - set streaming file name (default: NONE)%c", cNewLine );
	printf( "                  /t<db>    - set temp. database name (default: TEMPINTEG*.EDB)%c", cNewLine );
	printf( "                  /f<name>  - set prefix to use for name of report files%c", cNewLine );
	printf( "                              (default: <database>.integ.raw)%c", cNewLine );
#ifdef DEBUG	// Undocumented switches.
	printf( "                  /b        - don't rebuild and compare indexes%c", cNewLine );
	printf( "                  /n        - dump table statistics to INTGINFO.TXT%c", cNewLine );
#endif	//	DEBUG
	printf( "                  /8        - set 8k database page size (default: auto-detect)%c", cNewLine );
	printf( "                  /o        - suppress logo%c", cNewLine );
	printf( "          NOTES:  1) Integrity-check does not run database recovery.  If%c", cNewLine );
	printf( "                     a database is in an inconsistent state it is strongly%c", cNewLine );
	printf( "                     recommended that recovery is run to ensure the database%c", cNewLine );
	printf( "                     is consistent before proceeding with integrity-check.%c", cNewLine );
	}

LOCAL VOID EDBUTLHelpChecksum( char *szAppName )
	{
	printf( "%c", cNewLine );
	printf( "CHECKSUM:%c", cNewLine );
	printf( "    DESCRIPTION:  Verifies the individual checksum of each page of a database.%c", cNewLine );
	printf( "         SYNTAX:  %s /k <database name> [options]%c", szAppName, cNewLine );
	printf( "     PARAMETERS:  <database name> - filename of database to verify%c", cNewLine );
	printf( "        OPTIONS:  zero or more of the following switches, separated by a space:%c", cNewLine );
	printf( "                  /8   - set 8k database page size (default: auto-detect)%c", cNewLine );
	printf( "                  /o   - suppress logo%c", cNewLine );
	}

LOCAL VOID EDBUTLHelpRepair( char *szAppName )
	{
	printf( "%c", cNewLine );
	printf( "REPAIR:%c", cNewLine );
	printf( "    DESCRIPTION:  Repairs a corrupted or damaged database.%c", cNewLine );
	printf( "         SYNTAX:  %s /p <database name> [options]%c", szAppName, cNewLine );
	printf( "     PARAMETERS:  <database name> - filename of database to repair%c", cNewLine );
	printf( "        OPTIONS:  zero or more of the following switches, separated by a space:%c", cNewLine );
	printf( "                  /s<file>   - set streaming file name (default: NONE)%c", cNewLine );
	printf( "                  /t<db>     - set temp. database name%c", cNewLine );
	printf( "                               (default: TEMPREPAIR*.EDB)%c", cNewLine );
	printf( "                  /f<name>   - set prefix to use for name of report files%c", cNewLine );
	printf( "                               (default: <database>.integ.raw)%c", cNewLine );
#ifdef DEBUG	// Undocumented switches.
	printf( "                  /n         - dump table statistics to INTGINFO.TXT%c", cNewLine );
#endif	//	DEBUG
	printf( "                  /8         - set 8k database page size (default: auto-detect)%c", cNewLine );
	printf( "                  /o         - suppress logo%c", cNewLine );
	printf( "          NOTES:  1) Repair does not run database recovery.  If a database%c", cNewLine );
	printf( "                     is in an inconsistent state it is strongly recommended%c", cNewLine );
	printf( "                     that recovery is run to ensure the database is%c", cNewLine );
	printf( "                     consistent before proceeding with repair.%c", cNewLine );
	}

LOCAL VOID EDBUTLHelpDump( char *szAppName )
	{
	printf( "%c", cNewLine );
	printf( "FILE DUMP:%c", cNewLine );
	printf( "    DESCRIPTION:  Generates formatted output of various database file types.%c", cNewLine );
	printf( "         SYNTAX:  %s /m[mode-modifier] <filename> [options]%c", szAppName, cNewLine );
	printf( "     PARAMETERS:  [mode-modifier] - an optional letter designating the type of%c", cNewLine );
	printf( "                                    file dump to perform. Valid values are:%c", cNewLine );
	printf( "                                    h - dump database header (default)%c", cNewLine );
	printf( "                                    k - dump checkpoint file%c", cNewLine );
	printf( "                                    l - dump log file or set of logs%c", cNewLine );
#ifdef DEBUG
	printf( "                                    f - force database to a consistent state%c", cNewLine );
#endif
	printf( "                                    m - dump meta-data%c", cNewLine );
	printf( "                                    s - dump space usage%c", cNewLine );
#ifdef DEBUG	
	printf( "                                    n - dump nodes%c", cNewLine );
#endif	
	printf( "                  <filename>      - name of file to dump. The type of the%c", cNewLine );
	printf( "                                    specified file should match the dump type%c", cNewLine );
	printf( "                                    being requested (eg. if using /mh, then%c", cNewLine );
	printf( "                                    <filename> must be the name of a database).%c", cNewLine );
	printf( "        OPTIONS:  zero or more of the following switches, separated by a space:%c", cNewLine );
#ifdef DEBUG
	printf( "                  /k<node>@<lgpos>%c", cNewLine );
	printf( "                             - track a specific node in the logfile%c", cNewLine );
	printf( "                               starting from the given lgpos%c", cNewLine );
	printf( "                  /p<page>   - dump the given page from the database%c", cNewLine );
	printf( "                  /n<node>   - dump the given node from the database%c", cNewLine );
#endif	//	DEBUG
	printf( "                  /s<file>   - set streaming file name (default: NONE)%c", cNewLine );
	printf( "                  /t<table>  - perform dump for specified table only%c", cNewLine );
#ifdef DEBUG	
	printf( "                  /a         - dump all nodes including deleted ones%c", cNewLine );
	printf( "                               (dump-nodes mode only)%c", cNewLine );
#endif
	printf( "                  /v         - verbose%c", cNewLine );
	printf( "                  /8         - set 8k database page size (default: auto-detect)%c", cNewLine );
	printf( "                  /o         - suppress logo%c", cNewLine );
	}

#ifdef DEBUG

LOCAL VOID EDBUTLHelpHardRecovery( char *szAppName )
	{
	printf( "%c", cNewLine );
	printf( "RESTORE:%c", cNewLine );
	printf( "    DESCRIPTION:  Restore information and completion.%c", cNewLine );
#ifdef RESTORE_SERVER_SIMULATION_HELP
	printf( "         SYNTAX:  %s /c[mode-modifier] <path name|file name> [options]%c", szAppName, cNewLine );
#else // RESTORE_SERVER_SIMULATION_HELP
	printf( "         SYNTAX:  %s /c[mode-modifier] <path name> [options]%c", szAppName, cNewLine );
#endif // RESTORE_SERVER_SIMULATION_HELP
	printf( "     PARAMETERS:  [mode-modifier] - a letter designating the type of operation%c", cNewLine );
	printf( "                                    to be done%c", cNewLine );
	printf( "                                    m - dump Restore.Env %c", cNewLine );
	printf( "                                    c - start recovery for a Restore.Env%c", cNewLine );
#ifdef RESTORE_SERVER_SIMULATION_HELP
	printf( "                                    s - simulate server based on description%c", cNewLine );
 	printf( "                  <path name>     - (/cm and /cc) directory of the restore%c", cNewLine );
 	printf( "                                    (Restore.Env location)%c", cNewLine );
	printf( "                                  OR%c", cNewLine );
	printf( "                  <file name>     - (/cs) name of the server description file%c", cNewLine );
#else // RESTORE_SERVER_SIMULATION_HELP
	printf( "                  <path name>     - directory of the restore%c", cNewLine );
	printf( "                                    (Restore.Env location)%c", cNewLine );
#endif // RESTORE_SERVER_SIMULATION_HELP
 	printf( "        OPTIONS:  zero or more of the following switches, separated by a space:%c", cNewLine );
	printf( "                  /t<Instance>    - name of the instance where the play forward%c", cNewLine );
	printf( "                                    log file (default: from backup)%c", cNewLine );
	printf( "                                    If <Instance> is blank, don't play forward%c", cNewLine );
	printf( "                                    any log files.%c", cNewLine );
	printf( "                  /k              - preserves the log files used for recovery%c", cNewLine );
	printf( "                  /8              - set 8k database page size (default: auto-detect)%c", cNewLine );
	printf( "                  /o              - suppress logo%c", cNewLine );
	}

LOCAL VOID EDBUTLHelpBackup( char *szAppName )
	{
	printf( "%c", cNewLine );
	printf( "BACKUP:%c", cNewLine );
	printf( "    DESCRIPTION:  Performs backup, bringing all databases to a%c", cNewLine );
	printf( "                  consistent state.%c", cNewLine );
	printf( "         SYNTAX:  %s /b <backup path> [options]%c", szAppName, cNewLine );
	printf( "        OPTIONS:  zero or more of the following switches, separated by a space:%c", cNewLine );
	printf( "                  /l<path>   - location of log files%c", cNewLine );
	printf( "                               (default: current directory)%c", cNewLine );
	printf( "                  /s<path>   - location of system files (eg. checkpoint file)%c", cNewLine );
	printf( "                               (default: current directory)%c", cNewLine );
	printf( "                  /c<path>   - incremental backup%c", cNewLine );
	printf( "                  /8         - set 8k database page size (default: auto-detect)%c", cNewLine );
	printf( "                  /o         - suppress logo%c", cNewLine );
	}

LOCAL VOID EDBUTLHelpSLVMove( char *szAppName )
	{
	printf( "%c", cNewLine );
	printf( "SLV MOVE:%c", cNewLine );
	printf( "    DESCRIPTION:  Moves a SLV run that contains a certain SLV file page.%c", cNewLine );
	printf( "         SYNTAX:  %s /s <database name> /p<page>%c", szAppName, cNewLine );
	printf( "     PARAMETERS:  <database name> - filename of database to use%c", cNewLine );
	printf( "                  <page>          - page number from the SLV file%c", cNewLine );
	}

#endif // DEBUG

#ifdef RECORD_FORMAT_UPGRADE
LOCAL VOID EDBUTLHelpUpgradeRecordFormat( const char * const szAppName )
	{
	printf( "%c", cNewLine );
	printf( "RECORD UPGRADE:%c", cNewLine );
	printf( "    DESCRIPTION:  Upgrades the record format for all pages in the database.%c", cNewLine );
	printf( "     PARAMETERS:  <database name> - filename of database to convert%c", cNewLine );
	}
#endif	

LOCAL VOID EDBUTLHelpUpgrade( char *szAppName )
	{
	printf( "%c", cNewLine );
	printf( "UPGRADE:%c", cNewLine );
	printf( "    DESCRIPTION:  Upgrades a database (created using a previous release of%c", cNewLine );
	printf( "                  Microsoft(R) Windows NT) to the current version.%c", cNewLine );
	printf( "         SYNTAX:  %s /u <database name> /d<previous .DLL> [options]%c", szAppName, cNewLine );
	printf( "     PARAMETERS:  <database name>   - filename of the database to upgrade.%c", cNewLine );
	printf( "                  /d<previous .DLL> - pathed filename of the .DLL that came%c", cNewLine );
	printf( "                                      with the release of Microsoft(R)%c", cNewLine );
	printf( "                                      Windows NT from which you are upgrading.%c", cNewLine );
	printf( "        OPTIONS:  zero or more of the following switches, separated by a space:%c", cNewLine );
	printf( "                  /b<db>  - make backup copy under the specified name%c", cNewLine );
	printf( "                  /t<db>  - set temporary database name (default: TEMPUPGD.EDB)%c", cNewLine );
	printf( "                  /p      - preserve temporary database (ie. don't instate)%c", cNewLine );
#ifdef DEBUG	// Undocumented switches.
	printf( "                  /n      - dump upgrade information to UPGDINFO.TXT%c", cNewLine );
	printf( "                  /x<#>   - database extension size, in 4k pages (default: 256)%c", cNewLine );
	printf( "                  /a      - perform an in-place upgrade%c", cNewLine );
#endif	//	DEBUG
	printf( "                  /8      - set 8k database page size (default: auto-detect)%c", cNewLine );
	printf( "                  /o      - suppress logo%c", cNewLine );
	printf( "          NOTES:  1) This utility should only be used to upgrade a database%c", cNewLine );
	printf( "                     after an internal database format change has taken place.%c", cNewLine );
	printf( "                     If necessary, this will usually only coincide with the%c", cNewLine );
	printf( "                     release of a major, new revision of Microsoft(R)%c", cNewLine );
	printf( "                     Windows NT.%c", cNewLine );
	printf( "                  2) Before upgrading, the database should be in a consistent%c", cNewLine );
	printf( "                     state. An error will be returned if otherwise.%c", cNewLine );
	printf( "                  3) If instating is disabled (ie. /p), the original database%c", cNewLine );
	printf( "                     is preserved unchanged, and the temporary database will%c", cNewLine );
	printf( "                     contain the upgraded version of the database.%c", cNewLine );
	}

LOCAL VOID EDBUTLHelpScrub( const char * const szAppName )
	{
	printf( "%c", cNewLine );
	printf( "SECURE:%c", cNewLine );
	printf( "    DESCRIPTION:  Removes all deleted records from database.%c", cNewLine );
	printf( "         SYNTAX:  %s /s <database name>%c", szAppName, cNewLine );
	printf( "     PARAMETERS:  <database name> - filename of database to secure%c", cNewLine );
	printf( "                  /8   - set 8k database page size (default: auto-detect)%c", cNewLine );
	printf( "                  /o   - suppress logo%c", cNewLine );
	}


LOCAL VOID EDBUTLHelp( char *szAppName )
	{
	char c;

	EDBUTLPrintLogo();
	printf( szHelpDesc1 );
	printf( "%c%c", cNewLine, cNewLine );
	printf( szHelpSyntax );
	printf( "%c", cNewLine );
	printf( szHelpModes1, szAppName );
	printf( "%c", cNewLine );
	printf( szHelpModes2, szAppName );
	printf( "%c", cNewLine );
	printf( szHelpModes3, szAppName );
	printf( "%c", cNewLine );
	printf( szHelpModes4, szAppName );
	printf( "%c", cNewLine );
	printf( szHelpModes5, szAppName );
	printf( "%c", cNewLine );
	printf( szHelpModes6, szAppName );
	printf( "%c", cNewLine );
#ifdef DEBUG
	printf( szHelpModes7, szAppName );	//	mode 7 is Restore
	printf( "%c", cNewLine );

	printf( szHelpModes8, szAppName );	//	mode 8 is Backup
	printf( "%c", cNewLine );

	printf( szHelpModes9, szAppName );	//	mode 9 is OLDSLV
	printf( "%c", cNewLine );

#ifdef RECORD_FORMAT_UPGRADE
	printf( szHelpModes10, szAppName );	//	mode 10 is Record Format Upgrade
	printf( "%c", cNewLine );
#endif

	printf( szHelpModes11, szAppName );	//	mode 11 is Upgrade (undocumented in RETAIL)
	printf( "%c", cNewLine );

	printf( szHelpModes12, szAppName );	//	mode 12 is Scrub (undocumented in RETAIL )
	printf( "%c", cNewLine );
#endif	//	DEBUG

	printf( "%c", cNewLine );
	printf( "%s%c", szHelpPrompt1, cNewLine );
#ifdef DEBUG	
	printf( "%s,%c", szHelpPrompt2, cNewLine );
	printf( "%s%c", szHelpPrompt3, cNewLine );
#else
	printf( "%s%c", szHelpPrompt2, cNewLine );
#endif	
	printf( "%s", szHelpPromptCursor );
	c = (char)_getch();

	printf( "%c%c", cNewLine, cNewLine );

	switch ( c )
		{
		case 'd':
		case 'D':
			EDBUTLHelpDefrag( szAppName );
			break;
		case 'r':
		case 'R':
			EDBUTLHelpRecovery( szAppName );
			break;
		case 'g':
		case 'G':
			EDBUTLHelpIntegrity( szAppName );
			break;
		case 'k':
		case 'K':
			EDBUTLHelpChecksum( szAppName );
			break;			
		case 'p':
		case 'P':
			EDBUTLHelpRepair( szAppName );
			break;
		case 'm':
		case 'M':
			EDBUTLHelpDump( szAppName );
			break;

#ifdef DEBUG
		case 'c':
		case 'C':
			EDBUTLHelpHardRecovery( szAppName );
			break;			
		case 'b':
		case 'B':
			EDBUTLHelpBackup( szAppName );
			break;
		case 's':
		case 'S':
			EDBUTLHelpSLVMove( szAppName );
			break;
#endif

#ifdef RECORD_FORMAT_UPGRADE
		case 'f':
		case 'F':
			EDBUTLHelpUpgradeRecordFormat( szAppName );
			break;
#endif

		//	NOTE: Upgrade is undocumented in RETAIL
		case 'u':
		case 'U':
			EDBUTLHelpUpgrade( szAppName );
			break;

		//	NOTE: Scrub is undocumented in RETAIL
		case 'z':
		case 'Z':
			EDBUTLHelpScrub( szAppName );
			break;
		}
	}


#ifdef ESENT
//	Easter eggs are a fireable offense.
#else
static void EDBUTLSplash()
	{
	static const char foo[] =
	"..................................................."
	"                                                   "
	;
	
	static const char bar[] =
	"..................................................."
	"Laurion.Burchall"
	"....."
	"Adam.Foxman"
	"....."
	"Andrew.Goodsell"
	"....."
	"Adam.Green"
	"....."
	"Phillip.Hupf"
	"....."
	"Stephen.Jiang"
	"....."
	"Chris.Larson"
	"....."
	"Jonathan.Liem"
	"....."
	"Andrei.Marinescu"
	"....."	
	"Stuart.Padley"
	"....."
	"Michael.Rorke"
	"....."
	"Ivan.Trindev"
	"....."
	"Mark.Wistrom"
	"..................................................."
	;
	
	static const char baz[] =
	"                                                   "
	"..................................................."
	;

	const int cchWidth = 51;
	
	_cprintf( "\r\n                  Defragmentation Status (%% complete)\r\n\r\n" );
	_cprintf( "          0    10   20   30   40   50   60   70   80   90  100\r\n" );
	_cprintf( "          |----|----|----|----|----|----|----|----|----|----|\r\n" );
	_cprintf( "          " );

	int i;
	
	for( i = sizeof( foo ) - cchWidth - 1; i >= 0; --i )
		{
		_cprintf( "\r          %*.*s", cchWidth, cchWidth, foo + i );
		Sleep( 75 );
		}

	for( i = 0; i <= sizeof( bar ) - cchWidth - 1; ++i )
		{
		_cprintf( "\r          %*.*s", cchWidth, cchWidth, bar + i );
		Sleep( 75 );
		}

	for( i = sizeof( baz ) - cchWidth - 1; i >= 0; --i )
		{
		_cprintf( "\r          %*.*s", cchWidth, cchWidth, baz + i );
		Sleep( 75 );
		}

	_cprintf( "\r\n\r\n" );
	}
#endif	//	ESENT	


LOCAL VOID EDBUTLGetTime( ULONG timerStart, INT *piSec, INT *piMSec )
	{
	ULONG	timerEnd;

	timerEnd = GetTickCount();
	
	*piSec = ( timerEnd - timerStart ) / 1000;
	*piMSec = ( timerEnd - timerStart ) % 1000;
	}

LOCAL JET_ERR __stdcall PrintStatus( JET_SESID sesid, JET_SNP snp, JET_SNT snt, void *pv )
	{
	static int	iLastPercentage;
	int 		iPercentage;
	int			dPercentage;
	char		*szOperation = NULL;

	switch ( snp )
		{
		case -1:				// during Begin pv will point ot szOperation
		case JET_snpCompact:
		case JET_snpUpgrade:
		case JET_snpRestore:
		case JET_snpRepair:
		case JET_snpScrub:
		case JET_snpUpgradeRecordFormat:
			switch( snt )
				{
				case JET_sntProgress:
					assert( pv );
					iPercentage = static_cast< int >( ( __int64( reinterpret_cast< JET_SNPROG* >( pv )->cunitDone ) * __int64( 100 ) ) / __int64( reinterpret_cast< JET_SNPROG* >( pv )->cunitTotal ) );
					dPercentage = iPercentage - iLastPercentage;
					assert( dPercentage >= 0 );
					while ( dPercentage >= 2 )
						{
						printf( "." );
						iLastPercentage += 2;
						dPercentage -= 2;
						}
					break;

				case JET_sntBegin:
					{
					const char*	szOperation;
					SIZE_T		cbPadding, cbOper;

					switch ( snp )
						{
						default:
							szOperation = szDefrag;
							break;
						case JET_snpUpgrade:
							szOperation = szUpgrade;
							break;
						case JET_snpRestore:
							szOperation = szRestore;
							break;
						case JET_snpRepair:
							szOperation = szRepair;
							break;
						case JET_snpScrub:
							szOperation = szScrub;
							break;
						case JET_snpUpgradeRecordFormat:
							szOperation = szUpgradeRecordFormat;
							break;
						case -1:
							assert( NULL != pv );
							szOperation = (char *)pv;
						}

					printf( "%c", cNewLine );

					// Center the status message above the status bar.
					// Formula is: ( length of status bar - length of message ) / 2
					cbOper = strlen( szOperation );
					cbPadding = ( 51 - ( cbOper + (ULONG)strlen( szStatusMsg ) ) ) / 2;
					assert( cbPadding >= 0 );

					printf( "          %*s%s%c%c", cbPadding+cbOper, szOperation, szStatusMsg, cNewLine, cNewLine );
					printf( "          0    10   20   30   40   50   60   70   80   90  100\n" );
					printf( "          |----|----|----|----|----|----|----|----|----|----|\n" );
					printf( "          " );

					iLastPercentage = 0;
					break;
					}

				case JET_sntComplete:
					dPercentage = 100 - iLastPercentage;
					assert( dPercentage >= 0 );
					while ( dPercentage >= 2 )
						{
						printf( "." );
						iLastPercentage += 2;
						dPercentage -= 2;
						}

					printf( ".%c%c", cNewLine, cNewLine );
					break;
				}
			break;				
		}

	return JET_errSuccess;
	}

const EDBUTL_errInvalidPath = 1;	//	Does not form right path. param1 = file type
const EDBUTL_errSharedName	= 2;	//	Two file types share one file name. param1, param2 - files types
const EDBUTL_errInvalidDB	= 3;	//	cannot read source database header. param1 - error code
const EDBUTL_errNoSLVFile	= 4;	//	Source database does not have SLV file but such is specified

const EDBUTL_paramSrcDB		= 1;
const EDBUTL_paramSrcSLV	= 2;
const EDBUTL_paramTempDB	= 3;
const EDBUTL_paramTempSLV	= 4;
const EDBUTL_paramLast		= 5;

LOCAL VOID PrintErrorMessage( int const err, const UTILOPTS * const popts, int param1 = 0, int param2 = 0 )
	{
	assert( NULL != popts );
	
	static const char * const szObjectName[2*EDBUTL_paramLast] = 
		{
		"",
		"Source database",
		"Source streaming file",
		"Temporary database",
		"Temporary streaming file",
		"",
		"source database",
		"source streaming file",
		"temporary database",
		"temporary streaming file",
		};

	const char *const szObjectData[EDBUTL_paramLast] = 
		{
		"",
		popts->szSourceDB,
		popts->szSourceSLV,
		popts->szTempDB,
		popts->szTempSLV
		};
		
	switch ( err )
		{
		case EDBUTL_errInvalidPath:
			assert( 0 < param1 );
			assert( EDBUTL_paramLast > param1 );
			printf( "Error: %s specification '%s' is invalid.", 
				szObjectName[param1], szObjectData[param1] );
			break;
		case EDBUTL_errSharedName:
			assert( 0 < param1 );
			assert( EDBUTL_paramLast > param1 );
			assert( 0 < param2 );
			assert( EDBUTL_paramLast > param2 );
			printf( "Error: %s '%s' cannot be the same as %s.", 
				szObjectName[param1], szObjectData[param1], szObjectName[param2+EDBUTL_paramLast] );
			break;
		case EDBUTL_errInvalidDB:
			printf( "Error: Access to source database '%s' failed with Jet error %i.", 
				szObjectData[EDBUTL_paramSrcDB], param1 );
			break;
		case EDBUTL_errNoSLVFile:
			assert( 0 < param1 );
			assert( EDBUTL_paramLast > param1 );
			printf( "Usage Error: Source database has no streaming file but %s '%s' was specified.",
				szObjectName[param1+EDBUTL_paramLast], szObjectData[param1] );
			break;
		default:
			assert( 0 );
			return;
		}
	printf( "%c%c", cNewLine, cNewLine );
	}

LOCAL VOID EDBUTLGetSLVNameFromDbName( const CHAR *const szDbName, CHAR *const szSLVName )
	{
	CHAR	szDbDrive[_MAX_DRIVE+1];
	CHAR	szDbDir[_MAX_DIR+1];
	CHAR	szDbBaseName[_MAX_FNAME+1];

	assert( szSLVName != NULL );
	
	_tsplitpath( szDbName, szDbDrive, szDbDir, szDbBaseName, NULL );

	_tmakepath( szSLVName, szDbDrive, szDbDir, szDbBaseName, szStreamingFileExt );
	}

//	Check database and streaming file:
LOCAL JET_ERR ErrEDBUTLCheckDBSLVNames( 
	UTILOPTS*			popts, 
	const char * const	szTempDB, 
	char*				szSrcSLV		= NULL, 
	char*				szTempSLV		= NULL )
//	Parameters:
//		options structure
//		default temporaty database name
//		space to store names of SrcSLV & TempSLV
//			if NULL is passed they will not be retrieved at all
	{
	assert( NULL != popts );
	assert( NULL != szTempDB );
	assert( NULL == szTempSLV || NULL != szSrcSLV );

	CHAR	szFullpathSrcDB[ _MAX_PATH + 1] = "";
	CHAR	szFullpathTempDB[ _MAX_PATH + 1 ] = "";
	CHAR	szFullpathSrcSLV[ _MAX_PATH + 1] = "";
	CHAR	szFullpathTempSLV[ _MAX_PATH + 1 ] = "";

	//	if TempDB is not defined
	if ( NULL == popts->szTempDB )
		{ 
		//	set TempDB to DefaultTempDB
		popts->szTempDB = (char *)szTempDB;
		}

	//  if temp db is not valid path then ERROR
	if ( NULL == _fullpath( szFullpathTempDB, popts->szTempDB, _MAX_PATH ) )
		{
		PrintErrorMessage( EDBUTL_errInvalidPath, popts, EDBUTL_paramTempDB );
		return JET_errInvalidPath;
		}

	//	if SrcDB is not defined or is invalid path then ERROR
	if ( NULL == popts->szSourceDB )
		{
		printf( szUsageErr1, "source database" );
		printf( "%c%c", cNewLine, cNewLine );
		return JET_errInvalidParameter;
		}
	if ( NULL == _fullpath( szFullpathSrcDB, popts->szSourceDB, _MAX_PATH ) )
		{
		PrintErrorMessage( EDBUTL_errInvalidPath, popts, EDBUTL_paramSrcDB );
		return JET_errInvalidPath;
		}

	//	if 8k pages NOT specified, try to consult the database header
	//	to see if the database actually uses 8k pages
	//	WARNING: if a database uses 4k pages and this code erroneously
	//	(due to a bug, corruption, etc.) detects 8k pages, there is
	//	currently no way to override this code and force 4k pages
	ULONG	cbPageSize	= 0;
	if ( !FUTILOPTS8KPage( popts->fUTILOPTSFlags )
		&& JET_errSuccess == JetGetDatabaseFileInfo( popts->szSourceDB, &cbPageSize, sizeof(cbPageSize), JET_DbInfoPageSize )
		&& cbPageSize == 8192 )
		{
		const ERR	err		= JetSetSystemParameter( NULL, 0, JET_paramDatabasePageSize, 8192, NULL );
		if ( err < JET_errSuccess )
			return err;
		UTILOPTSSet8KPage( popts->fUTILOPTSFlags );
		}

	//	if need SrcSTM and SrcDB has a SLV file
	BOOL fDBHasSLV = fFalse;
	if ( szSrcSLV )
		{
		const ERR	err		= JetGetDatabaseFileInfo( popts->szSourceDB, &fDBHasSLV, sizeof( fDBHasSLV ), JET_DbInfoHasSLVFile );
		if ( err < JET_errSuccess )
			{
			PrintErrorMessage( EDBUTL_errInvalidDB, popts, err );
			return err;
			}
		}
	if ( fDBHasSLV )
		{
		//	if SrcSLV is not defined
		if ( NULL == popts->szSourceSLV )
			{
			assert( szSrcSLV != NULL );
			//	retrieve SrcSLV from SrcDB
			EDBUTLGetSLVNameFromDbName( popts->szSourceDB, szSrcSLV );
			popts->szSourceSLV = szSrcSLV;
			}
		//	if SrcSLV is not valid path ERROR
		if ( NULL == _fullpath( szFullpathSrcSLV, popts->szSourceSLV, _MAX_PATH ) )
			{
			PrintErrorMessage( EDBUTL_errInvalidPath, popts, EDBUTL_paramSrcSLV );
			return JET_errInvalidPath;
			}			
		//	if SrcSLV is the same as SrcDB then ERROR
		if ( 0 == _strcmpi( szFullpathSrcDB, szFullpathSrcSLV ) )
			{
			PrintErrorMessage( EDBUTL_errSharedName, popts, EDBUTL_paramSrcDB, EDBUTL_paramSrcSLV );
			return JET_errInvalidDatabase;
			}
		}
	//	if SrcSLV is defined but not expected then ERROR
	else if ( NULL != popts->szSourceSLV )
		{
		PrintErrorMessage( EDBUTL_errNoSLVFile, popts, EDBUTL_paramSrcSLV );
		return JET_errInvalidParameter;
		}

	//	if TempDB is the same as SrcDB or as SrcSLV then ERROR
	if ( 0 == _strcmpi( szFullpathSrcDB, szFullpathTempDB ) )
		{
		PrintErrorMessage( EDBUTL_errSharedName, popts, EDBUTL_paramSrcDB, EDBUTL_paramTempDB );
		return JET_errInvalidDatabase;
		}
	if ( 0 == _strcmpi( szFullpathSrcSLV, szFullpathTempDB ) )
		{
		PrintErrorMessage( EDBUTL_errSharedName, popts, EDBUTL_paramSrcSLV, EDBUTL_paramTempDB );
		return JET_errInvalidDatabase;
		}

	//	if SrcDB has SLV file and need tempSLV
	if ( fDBHasSLV && NULL != szTempSLV )
		{
		//	if TempSLV is not defined
		if ( NULL == popts->szTempSLV )
			{
			//	retrieve TempSLV from TempDB
			EDBUTLGetSLVNameFromDbName( popts->szTempDB, szTempSLV );
			popts->szTempSLV = szTempSLV;
			}
		//	if TempSLV is not valid path then ERROR
		if ( NULL == _fullpath( szFullpathTempSLV, popts->szTempSLV, _MAX_PATH ) )
			{
			PrintErrorMessage( EDBUTL_errInvalidPath, popts, EDBUTL_paramTempSLV );
			return JET_errInvalidPath;
			}			
		//	if TempSLV is the same as TempDB or as SrcDB or as SrcSLV then ERROR
		if ( 0 == _strcmpi( szFullpathSrcDB, szFullpathTempSLV ) )
			{
			PrintErrorMessage( EDBUTL_errSharedName, popts, EDBUTL_paramSrcDB, EDBUTL_paramTempSLV );
			return JET_errInvalidDatabase;
			}
		if ( 0 == _strcmpi( szFullpathSrcSLV, szFullpathTempSLV ) )
			{
			PrintErrorMessage( EDBUTL_errSharedName, popts, EDBUTL_paramSrcSLV, EDBUTL_paramTempSLV );
			return JET_errInvalidDatabase;
			}
		if ( 0 == _strcmpi( szFullpathTempDB, szFullpathTempSLV ) )
			{
			PrintErrorMessage( EDBUTL_errSharedName, popts, EDBUTL_paramTempDB, EDBUTL_paramTempSLV );
			return JET_errInvalidDatabase;
			}
		}
	//	else if TempSLV is defined but not expected then ERROR
	else if ( NULL != popts->szTempSLV )
		{
		PrintErrorMessage( EDBUTL_errNoSLVFile, popts, EDBUTL_paramSrcSLV );
		return JET_errInvalidParameter;
		}
	return JET_errSuccess;
	}

#ifdef DEBUG
LOCAL JET_ERR ErrEDBUTLCheckBackupPath( UTILOPTS *popts )
	{
	CHAR	szFullpathBackup[ _MAX_PATH + 1];

	if ( popts->szBackup == NULL )
		{
		printf( szUsageErr1, "backup path" );
		printf( "%c%c", cNewLine, cNewLine );
		return JET_errInvalidParameter;
		}
	
	if ( _fullpath( szFullpathBackup, popts->szBackup, _MAX_PATH ) == NULL )
		{
		printf( szErr1, popts->szBackup );
		printf( "%c%c", cNewLine, cNewLine );
		return JET_errInvalidPath;
		}

	return JET_errSuccess;
	}
#endif

LOCAL BOOL FEDBUTLParsePath( char *arg, char **pszParam, char *szParamDesc, BOOL fAllowEmpty = fFalse )
	{
	BOOL	fResult = fTrue;

	// if the argument is empty try to read next one argument
	if ( '\0' == *arg )
		{
		char *argT = arg;
		arg = GetNextArg();
		// if it was last argument or option follows it means we passed empty argument
		if ( NULL == arg || NULL != strchr( szSwitches, *arg ) )
			{
			arg = argT;
			SetCurArgID( GetCurArgID() - 1 );
			}
		}

	// no path should contain leading ':'(s)
	while ( ':' == *arg )
		{
		arg++;
		}

	
	if ( '\0' == *arg && !fAllowEmpty )
		{
		printf( szUsageErr1, szParamDesc );			// Missing spec.
		printf( "%c%c", cNewLine, cNewLine );
		fResult = fFalse;
		}
	else if ( NULL == *pszParam )
		{
		*pszParam = arg;
		}
	else
		{
		printf( szUsageErr2, szParamDesc );			// Duplicate spec.
		printf( "%c%c", cNewLine, cNewLine );
		fResult = fFalse;
		}
		
	return fResult;
	}


LOCAL BOOL FEDBUTLParseDefragment( char *arg, UTILOPTS *popts )
	{
	BOOL	fResult = fTrue;

	switch( arg[1] )
		{
		case 'b':
		case 'B':
			fResult = FEDBUTLParsePath( arg+2, &popts->szBackup, "backup database" );
			break;

		case 'n':		
		case 'N':
			UTILOPTSSetDefragInfo( popts->fUTILOPTSFlags );
			break;
			
		case 'p':
		case 'P':
			UTILOPTSSetPreserveTempDB( popts->fUTILOPTSFlags );
			break;
			
		case 't':
		case 'T':
			fResult = FEDBUTLParsePath( arg+2, &popts->szTempDB, "temporary database" );
			break;

		case 'w':
		case 'W':				
			popts->cpageBatchIO = atol( arg + 2 );
			if ( popts->cpageBatchIO <= 0 )
				{
				printf( szUsageErr7 );
				printf( "%c%c", cNewLine, cNewLine );
				fResult = fFalse;
				}
			break;

		case 'x':
		case 'X':
			popts->cpageDbExtension = atol( arg + 2 );
			if ( popts->cpageDbExtension <= 0 )
				{
				printf( szUsageErr8 );
				printf( "%c%c", cNewLine, cNewLine );
				fResult = fFalse;
				}
			break;

		case 'i':
		case 'I':
			UTILOPTSSetDefragSLVDontCopy( popts->fUTILOPTSFlags );
			break;

		case 's':
		case 'S':
			fResult = FEDBUTLParsePath( arg+2, &popts->szSourceSLV, "streaming file" );
			break;

		case 'f':
		case 'F':
			fResult = FEDBUTLParsePath( arg+2, &popts->szTempSLV, "temp. streaming file" );
			break;

		default:
			printf( szUsageErr4, arg );
			printf( "%c%c", cNewLine, cNewLine );
			fResult = fFalse;
			break;
		}

	return fResult;
	}

LOCAL BOOL FEDBUTLParseRecovery( char *arg, UTILOPTS *popts )
	{
	BOOL	fResult = fTrue;

	switch( arg[1] )
		{
#ifdef LOG_SHIPPING		
		case 'f':
		case 'F':
			popts->grbitInit |= JET_bitReplayReplicatedLogFiles;
			break;
#endif			

		case 'i':
		case 'I':
			popts->grbitInit |= JET_bitReplayIgnoreMissingDB;
			break;

		case 'l':
		case 'L':
			fResult = FEDBUTLParsePath( arg+2, &popts->szLogfilePath, "logfile path" );
			break;

		case 's':
		case 'S':
			fResult = FEDBUTLParsePath( arg+2, &popts->szSystemPath, "system path" );
			break;

		case 't':
		case 'T':
			popts->pageTempDBMin = atol( arg + 2 );
			fResult = fTrue;
			break;

		case 'd':
		case 'D':
			if ( NULL != popts->szBackup || NULL != popts->szRestore )
				{
				printf( szUsageErr3 );
				printf( "%c%c", cNewLine, cNewLine );
				fResult = fFalse;
				}
			else
				{
				//	if no directory specified, use current directory
				fResult = FEDBUTLParsePath( arg+2, &popts->szSourceDB, "database directory", fTrue );
				if ( fResult && '\0' == popts->szSourceDB[0] )
					popts->szSourceDB = (CHAR *)szCurrDir;
				}
			break;

#ifdef DEBUG
		case 'b':
		case 'B':
			if ( NULL != popts->szSourceDB )
				{
				printf( szUsageErr3 );
				printf( "%c%c", cNewLine, cNewLine );
				fResult = fFalse;
				}
			else
				{
				fResult = FEDBUTLParsePath( arg+2, &popts->szBackup, "backup directory" );
				}
			break;

		case 'r':
		case 'R':
			if ( NULL != popts->szSourceDB )
				{
				printf( szUsageErr3 );
				printf( "%c%c", cNewLine, cNewLine );
				fResult = fFalse;
				}
			else
				{
				fResult = FEDBUTLParsePath( arg+2, &popts->szRestore, "destination directory" );
				}
			break;
#endif

		default:
			printf( szUsageErr4, arg );
			printf( "%c%c", cNewLine, cNewLine );
			fResult = fFalse;
			break;
		}

	return fResult;
	}	

LOCAL BOOL FEDBUTLParseIntegrity( char *arg, UTILOPTS *popts )
	{
	BOOL		fResult = fTrue;

	switch( arg[1] )
		{
		case 'b':
		case 'B':
			UTILOPTSSetDontBuildIndexes( popts->fUTILOPTSFlags );
			break;

		case 'n':
		case 'N':
			UTILOPTSSetDumpStats( popts->fUTILOPTSFlags );
			break;

		case 't':
		case 'T':
			fResult = FEDBUTLParsePath( arg+2, &popts->szTempDB, "temporary database" );
			break;

		case 's':
		case 'S':
			fResult = FEDBUTLParsePath( arg+2, &popts->szSourceSLV, "streaming file" );
			break;

		case 'f':
		case 'F':
			fResult = FEDBUTLParsePath( arg+2, &popts->szIntegPrefix, "report file name prefix" );
			break;
			
		default:
			printf( szUsageErr4, arg );
			printf( "%c%c", cNewLine, cNewLine );
			fResult = fFalse;
			break;
		}

	return fResult;
	}

LOCAL BOOL FEDBUTLParseRepair( char *arg, UTILOPTS *popts )
	{
	BOOL	fResult = fTrue;

	switch( arg[1] )
		{
		case 't':
		case 'T':
			fResult = FEDBUTLParsePath( arg+2, &popts->szTempDB, "temporary database" );
			break;

		case 'n':
		case 'N':
			UTILOPTSSetDumpStats( popts->fUTILOPTSFlags );
			break;

		case 's':
		case 'S':
			fResult = FEDBUTLParsePath( arg+2, &popts->szSourceSLV, "streaming file" );
			break;

		case 'f':
		case 'F':
			fResult = FEDBUTLParsePath( arg+2, &popts->szIntegPrefix, "report file name prefix" );
			break;
			
		default:
			printf( szUsageErr4, arg );
			printf( "%c%c", cNewLine, cNewLine );
			fResult = fFalse;
			break;
		}

	return fResult;
	}

LOCAL VOID EDBUTLGetBaseName( const CHAR * const szLogfile, CHAR * const szBaseName )
	{
	CHAR	szNameT[_MAX_FNAME+1];

	assert( szBaseName != NULL );
	
	_tsplitpath( szLogfile, NULL, NULL, szNameT, NULL );
	_tcsncpy( szBaseName, szNameT, 3 );
	szBaseName[ 3 ] = 0;
	}

LOCAL BOOL FEDBUTLParseDump( char *arg, UTILOPTS *popts )
	{
	BOOL	fResult = fFalse;

	switch( arg[1] )
		{
		case 'a':
		case 'A':
			{
			JET_DBUTIL * pdbutil;
			pdbutil = reinterpret_cast<JET_DBUTIL *>( popts->pv );
			
			if (pdbutil->op != opDBUTILDumpData)
				fResult = fFalse;
			else
				{	
				pdbutil->grbitOptions |= JET_bitDBUtilOptionAllNodes;
				fResult	= fTrue;
				}
			}
			break;

		case 'v':
		case 'V':
			{
			JET_DBUTIL * pdbutil;
			pdbutil = reinterpret_cast<JET_DBUTIL *>( popts->pv );

			pdbutil->grbitOptions |= JET_bitDBUtilOptionDumpVerbose;
			fResult = fTrue;
			}
			break;

		case 's':
		case 'S':
			fResult = FEDBUTLParsePath( arg+2, &popts->szSourceSLV, "streaming file" );
			break;
			
		case 't':
		case 'T':
			{
			JET_DBUTIL * pdbutil;
			pdbutil = reinterpret_cast<JET_DBUTIL *>( popts->pv );

			switch ( pdbutil->op )
				{
				case opDBUTILDumpData:
				case opDBUTILDumpMetaData:
				case opDBUTILDumpSpace:
					pdbutil->szTable = arg+2;
					fResult = fTrue;
					break;
				default:
					fResult = fFalse;
				}
			}
			break;

		case 'p':
		case 'P':
			{
			char 	szPage[256];
			strcpy( szPage, arg+2 );

			JET_DBUTIL * pdbutil;
			pdbutil = reinterpret_cast<JET_DBUTIL *>( popts->pv );

			pdbutil->op 	= opDBUTILDumpPage;
			pdbutil->pgno	= atoi( szPage );

			fResult			= fTrue;
			}
			break;

#ifdef DEBUG
		case 'k':
		case 'K':
			{			
			const char * pchSep;
			if( NULL == ( pchSep = strchr( arg, '@' ) ) )
				{
				printf( "%s (1)", szUsageErr13 );
				printf( "%c%c", cNewLine, cNewLine );
				fResult = fFalse;
				break;
				}

			char 	szNode[1024];
			char	szLgpos[1024];
			strncpy( szNode, arg+2, UINT( pchSep - arg + 1 ) );
			strcpy( szLgpos, pchSep + 1 );
			
			int 	dbid;
			int		pgno;
			int		iline;
			if( sscanf( szNode, "[%d:%d:%d]", &dbid, &pgno, &iline ) != 3 
				&& sscanf( szNode, "%d:%d:%d", &dbid, &pgno, &iline ) != 3 )
				{
				printf( "%s (2)", szUsageErr13 );
				printf( "%c%c", cNewLine, cNewLine );
				fResult = fFalse;
				break;
				}

			long		lGeneration;
			long		isec;
			long		ib;
			if( sscanf( szLgpos, "%x,%x,%x", &lGeneration, &isec, &ib ) != 3 )
				{
				printf( "%s (3)", szUsageErr13 );
				printf( "%c%c", cNewLine, cNewLine );
				fResult = fFalse;
				break;
				}

			JET_DBUTIL * pdbutil;
			pdbutil = reinterpret_cast<JET_DBUTIL *>( popts->pv );
			
			pdbutil->op 	= opDBUTILDumpLogfileTrackNode;
			pdbutil->dbid	= dbid;
			pdbutil->pgno	= pgno;
			pdbutil->iline	= iline;
			pdbutil->lGeneration = lGeneration;
			pdbutil->isec	= isec;
			pdbutil->ib		= ib;
			
			fResult = fTrue;
			}
			break;

		/*
		case 's':
		case 'S':
			{
			char 	szPage[256];
			strcpy( szPage, arg+2 );

			JET_DBUTIL * pdbutil;
			pdbutil = reinterpret_cast<JET_DBUTIL *>( popts->pv );

			pdbutil->op 	= opDBUTILSLVMove;
			pdbutil->pgno	= atoi( szPage );

			fResult			= fTrue;
			}
			break;
		*/
		
		case 'n':
		case 'N':
			{
			char 	szNode[256];
			strcpy( szNode, arg+2 );

			int 	dbid 	= 0;
			int		pgno	= 0;
			int		iline	= 0;
			if(
				sscanf( szNode, "[%d:%d:%d]", &dbid, &pgno, &iline ) != 3 
				&& sscanf( szNode, "%d:%d:%d", &dbid, &pgno, &iline ) != 3
				&& sscanf( szNode, "[%d:%d]", &pgno, &iline ) != 2
				&& sscanf( szNode, "%d:%d", &pgno, &iline ) != 2
				)
				{
				printf( "%s (2)", szUsageErr13 );
				printf( "%c%c", cNewLine, cNewLine );
				fResult = fFalse;
				break;
				}

			JET_DBUTIL * pdbutil;
			pdbutil = reinterpret_cast<JET_DBUTIL *>( popts->pv );

			pdbutil->op 	= opDBUTILDumpNode;
			pdbutil->dbid	= dbid;
			pdbutil->pgno	= pgno;
			pdbutil->iline	= iline;

			fResult			= fTrue;
			}
			break;

#endif	//	DEBUG

#ifdef ESENT
#else  //  !ESENT			
		case 'e':
		case 'E':
			if ( 0 == stricmp( arg+1, "exslv" ) )
				{
				JET_DBUTIL * const	pdbutil		= reinterpret_cast<JET_DBUTIL *>( popts->pv );

				pdbutil->op = opDBUTILDumpExchangeSLVInfo;
				fResult = fTrue;
				break;
				}
#endif  //  ESENT

		//	FALL THROUGH

		default:
			printf( szUsageErr4, arg );
			printf( "%c%c", cNewLine, cNewLine );
			fResult = fFalse;
			break;
		}

	return fResult;
	}	

#ifdef DEBUG

LOCAL BOOL FEDBUTLParseHardRecovery( char *arg, UTILOPTS *popts )
	{
	BOOL	fResult = fTrue;

	switch( arg[1] )
		{
		case 'k':
		case 'K':
			if ( !FUTILOPTSDumpRestoreEnv(popts->fUTILOPTSFlags) )
				{
				UTILOPTSSetPreserveTempDB( popts->fUTILOPTSFlags );
				break;
				}
			// no /t allowed if dump mode specified
		case 't':
		case 'T':
			if ( !FUTILOPTSDumpRestoreEnv(popts->fUTILOPTSFlags) )
				{
				// allow NULL target name. It means : no play forward
				fResult = FEDBUTLParsePath( arg+2, &popts->szRestore, "Target Instance", fTrue );
				break;
				}
			// no /t allowed if dump mode specified
		default:
			printf( szUsageErr4, arg );
			printf( "%c%c", cNewLine, cNewLine );
			fResult = fFalse;
			break;
		}

	return fResult;
	}	
				
LOCAL BOOL FEDBUTLParseBackup( char *arg, UTILOPTS *popts )
	{
	BOOL	fResult = fFalse;	// backup directory must be set at least.

	switch( arg[1] )
		{
		case 'l':
		case 'L':
			fResult = FEDBUTLParsePath( arg+2, &popts->szLogfilePath, "logfile path" );
			break;

		case 's':
		case 'S':
			fResult = FEDBUTLParsePath( arg+2, &popts->szSystemPath, "system path" );
			break;

		case 'c':
		case 'C':
			UTILOPTSSetIncrBackup( popts->fUTILOPTSFlags );
			break;

		default:
			printf( szUsageErr4, arg );
			printf( "%c%c", cNewLine, cNewLine );
			fResult = fFalse;
			break;
		}

	return fResult;
	}

LOCAL BOOL FEDBUTLParseSLVMove( char *arg, UTILOPTS *popts )
	{
	BOOL	fResult = fFalse;

	switch( arg[1] )
		{
		case 'p':
		case 'P':
			{
			char 	szPage[256];
			strcpy( szPage, arg+2 );

			JET_DBUTIL * pdbutil;
			pdbutil = reinterpret_cast<JET_DBUTIL *>( popts->pv );

			pdbutil->op 	= opDBUTILDumpPage;
			pdbutil->pgno	= atoi( szPage );

			fResult			= fTrue;
			}
			break;

		default:
			printf( szUsageErr4, arg );
			printf( "%c%c", cNewLine, cNewLine );
			fResult = fFalse;
			break;
		}

	if ( NULL == popts->szSourceDB )
		{
		fResult = fFalse;
		}
		
	return fResult;
	}	
	
#endif	//	DEBUG

#ifdef RECORD_FORMAT_UPGRADE
LOCAL BOOL FEDBUTLParseUpgradeRecordFormat( char *arg, UTILOPTS *popts )
	{
	BOOL	fResult = fTrue;

	switch( arg[1] )
		{
		case 'd':	//	add a non-default case to avoid compiler warnings
		case 'D':
			popts->lDirtyLevel = atol( arg + 2 );
			break;
			
		default:
			printf( szUsageErr4, arg );
			printf( "%c%c", cNewLine, cNewLine );
			fResult = fFalse;
			break;
		}

	return fResult;
	}
#endif

LOCAL BOOL FEDBUTLParseUpgrade( char *arg, UTILOPTS *popts )
	{
	BOOL	fResult = fTrue;

	switch( arg[1] )
		{
		case 'd':
		case 'D':
			fResult = FEDBUTLParsePath( arg+2, &((JET_CONVERT *)(popts->pv))->szOldDll, "old .DLL" );
			break;

		case 'b':
		case 'B':
			fResult = FEDBUTLParsePath( arg+2, &popts->szBackup, "backup database" );
			break;
			
		case 'n':
		case 'N':
			UTILOPTSSetDefragInfo( popts->fUTILOPTSFlags );
			break;
			
		case 'p':
		case 'P':
			UTILOPTSSetPreserveTempDB( popts->fUTILOPTSFlags );
			break;
			
		case 't':
		case 'T':
			fResult = FEDBUTLParsePath( arg+2, &popts->szTempDB, "temporary database" );
			break;
			
		case 'x':
		case 'X':
			popts->cpageDbExtension = atol( arg + 2 );
			if ( popts->cpageDbExtension <= 0 )
				{
				printf( szUsageErr8 );
				printf( "%c%c", cNewLine, cNewLine );
				fResult = fFalse;
				}
			break;

		case 'a':
		case 'A':
			UTILOPTSSetInPlaceUpgrade( popts->fUTILOPTSFlags );
			break;

		default:
			printf( szUsageErr4, arg );
			printf( "%c%c", cNewLine, cNewLine );
			fResult = fFalse;
		}

	return fResult;
	}	

LOCAL BOOL FEDBUTLParseScrub( char *arg, UTILOPTS *popts )
	{
	BOOL	fResult = fTrue;

	switch( arg[1] )
		{
		case 's':
		case 'S':
			fResult = FEDBUTLParsePath( arg+2, &popts->szSourceSLV, "streaming file" );
			break;

		default:
			printf( szUsageErr4, arg );
			printf( "%c%c", cNewLine, cNewLine );
			fResult = fFalse;
			break;
		}

	return fResult;
	}


LOCAL CHAR **g_argv = NULL;
LOCAL INT	g_argMaxID = 0;
LOCAL INT	g_argCurID = -1;

//	initalizes argument index, and set to point before firts index
LOCAL VOID InitArg( int argc, char *argv[] )
	{
	g_argMaxID = argc;
	g_argv = argv;
	g_argCurID = -1;
	}

//	returns current argument index
//	returns -1 if is before first argument
//	return  ArgCount after last argument
LOCAL INT GetCurArgID()
	{
	return g_argCurID;
	}

LOCAL INT GetArgCount()
	{
	return g_argMaxID;
	}

LOCAL VOID SetCurArgID( const INT id )
	{
	if ( -1 > id )
		{
		g_argCurID = -1;
		}
	else if ( g_argMaxID < id )
		{
		g_argCurID = g_argMaxID;
		}
	else
		{
		g_argCurID = id;
		}
	}

LOCAL CHAR *GetCurArg()
	{
	AssertPREFIX( -1 <= g_argCurID );
	AssertPREFIX( 0 != g_argMaxID );
	AssertPREFIX( g_argMaxID >= g_argCurID );
	if ( -1 == g_argCurID || g_argMaxID == g_argCurID )
		{
		return NULL;
		}
	return g_argv[g_argCurID];
	}

LOCAL CHAR *GetNextArg()
	{
	SetCurArgID( GetCurArgID()+1 );
	return GetCurArg();
	}

LOCAL CHAR *GetPrevArg()
	{
	SetCurArgID( GetCurArgID()-1 );
	return GetCurArg();
	}

LOCAL BOOL FSetDumpModeModifier( char chMode, JET_DBUTIL *pdbutil )
	{
	switch( chMode )
		{
		case 0:
		case 'h':
		case 'H':
			pdbutil->op = opDBUTILDumpHeader;
			break;

		case 'k':
		case 'K':
			pdbutil->op = opDBUTILDumpCheckpoint;
			break;

		case 'm':
		case 'M':
			pdbutil->op = opDBUTILDumpMetaData;
			break;

#ifdef DEBUG
		case 'f':
		case 'F':
			pdbutil->op = opDBUTILSetHeaderState;
			break;

		case 'n':
		case 'N':
			pdbutil->op = opDBUTILDumpData;
			break;

#endif
			
		case 'l':
		case 'L':
			pdbutil->op = opDBUTILDumpLogfile;
			break;

		case 's':
		case 'S':
			pdbutil->op = opDBUTILDumpSpace;
			break;

		default:
			return fFalse;
		}
		return fTrue;
	}

LOCAL BOOL FSetHardRecoveryModeModifier( char chMode, UTILOPTS *popts )
	{
	switch( chMode )
		{
		case 'c':
		case 'C':
			break;
		case 'm':
		case 'M':
			UTILOPTSSetDumpRestoreEnv( popts->fUTILOPTSFlags );
			break;
#ifdef RESTORE_SERVER_SIMULATION
		case 's':
		case 'S':
			UTILOPTSSetServerSim ( popts->fUTILOPTSFlags );
			break;
#endif // RESTORE_SERVER_SIMULATION
		default:
			return fFalse;
		}
	return fTrue;
	}

LOCAL BOOL FEDBUTLParseOptions(
	UTILOPTS	*popts,
	BOOL		(*pFEDBUTLParseMode)( char *arg, UTILOPTS *popts ) )
	{
	BOOL		fResult = fTrue;
	char		*arg = GetCurArg();
	INT 		iSkipID = -1; // argument ID to skip. Related to dump and hard recovery hacks

	assert( NULL != arg );
	assert( NULL != strchr( szSwitches, *arg ) );
	assert( '\0' != *arg + 1 );

	//	HACK for dump 
	if ( modeDump == popts->mode ) 
		{
		// when we have only one char after mode it is mode modifier
		if ( '\0' != arg[2] && '\0' == arg[3] )
			{
			fResult = FSetDumpModeModifier( arg[2], (JET_DBUTIL *)popts->pv );
			arg++;
			}
		else	// run search to set mode specifier
			{
			INT curid = GetCurArgID();
			char *argT;
			fResult = fFalse;
			while ( !fResult && NULL != ( argT = GetNextArg() ) )
				{
				if ( NULL != strchr( szSwitches, *argT ) && '\0' != argT[1] && '\0' == argT[2] )
					{
					fResult = FSetDumpModeModifier( argT[1], (JET_DBUTIL *)popts->pv );
					}
				}
			if ( !fResult )
				{
				fResult = FSetDumpModeModifier( '\0', (JET_DBUTIL *)popts->pv );
				}
			iSkipID = GetCurArgID();
			SetCurArgID( curid );
			assert( GetCurArgID() == curid );
			}
		}
	//	HACK for hard recovery
	else if ( modeHardRecovery == popts->mode )
		{
		// when we have only one char afret mode it is mode modifier
		if ( '\0' != arg[2] && '\0' == arg[3] )
			{
			fResult = FSetHardRecoveryModeModifier( arg[2], popts );
			arg ++;	// ignore mode modifier
			}
		else	// run search to set mode specifier
			{
			INT curid = GetCurArgID();
			char *argT;
			fResult = fFalse;
			while ( !fResult && NULL != ( argT = GetNextArg() ) )
				{
				if ( NULL != strchr( szSwitches, *argT ) && '\0' != argT[1] && '\0' == argT[2] )
					{
					fResult = FSetHardRecoveryModeModifier( argT[1], popts );
					}
				}
			iSkipID = GetCurArgID();
			SetCurArgID( curid );
			assert( GetCurArgID() == curid );
			}
		}
		
	if ( !fResult )
		{
		printf( szUsageErr12 );
		printf( "%c%c", cNewLine, cNewLine );
		}
		
	arg += 2;
	if ( '\0' == *arg )
		{
		arg = GetNextArg();
		}

	// First option specifies the mode, so start with the second option.
	for ( ; fResult && NULL != arg; arg = GetNextArg() )
		{
		//	dump and hard recovery hack
		if ( GetCurArgID() == iSkipID )
			{
			continue;
			}
			
		if ( strchr( szSwitches, arg[0] ) == NULL )
			{
			// SPECIAL CASE: Backup mode does not DB specification.
			switch ( popts->mode )
				{
				case modeRecovery:
					if ( fResult = ( NULL == popts->szBase ) )
						{
						popts->szBase = arg;
						}
					break;
				case modeBackup:
					if ( fResult = ( NULL == popts->szBackup ) )
						{
						popts->szBackup = arg;
						}
					break;
				default:
					if ( fResult = ( NULL == popts->szSourceDB ) )
						{
						popts->szSourceDB = arg;
						}
					break;
				}

			if ( !fResult )
				{
				printf( szUsageErr5, arg );
				printf( "%c%c", cNewLine, cNewLine );
				}
			}

		else
			{
			// Parse options common to all modes.  Pass off unique options to the
			// custom parsers.
			switch ( arg[1] )
				{
				case '8':
					UTILOPTSSet8KPage( popts->fUTILOPTSFlags );
					break;

				case 'o':
				case 'O':
					UTILOPTSSetSuppressLogo( popts->fUTILOPTSFlags );
					break;

				case '!':
					//	logfile size param no longer needed (Jet now
					//	auto-detects logfile size when needed)
					break;

				default:
					if ( pFEDBUTLParseMode )
						{
						fResult = (*pFEDBUTLParseMode)( arg, popts );
						}
					else
						{
						printf( szUsageErr4, arg );
						printf( "%c%c", cNewLine, cNewLine );
						fResult = fFalse;
						}
					break;
				}
			}
		}

	return fResult;		
	}


//  ================================================================
LOCAL JET_ERR ErrEDBUTLRepair(
	const JET_SESID	sesid,
	const char * const szDatabase,
	const char * const szSLV,
	const char * const szBackup,
	const char * const szTable,
	const char * const szIntegPrefix,
	const JET_PFNSTATUS pfnStatus,
	const JET_GRBIT grbit
	)
//  ================================================================
	{
	JET_DBUTIL dbutil;
	memset( &dbutil, 0, sizeof( dbutil ) );

	dbutil.cbStruct			= sizeof( JET_DBUTIL );
	dbutil.op				= opDBUTILEDBRepair;
	dbutil.szDatabase 		= const_cast<char *>( szDatabase );
	dbutil.szSLV 			= const_cast<char *>( szSLV );
	dbutil.szBackup			= const_cast<char *>( szBackup );
	dbutil.szTable			= const_cast<char *>( szTable );
	dbutil.szIntegPrefix	= const_cast<char *>( szIntegPrefix );
	dbutil.sesid			= sesid;
	dbutil.grbitOptions		= grbit;	
	dbutil.pfnCallback		= pfnStatus;

	const JET_ERR err = JetDBUtilities( &dbutil );
	return err;
	}


//  ================================================================
LOCAL JET_ERR ErrEDBUTLScrub(
	JET_SESID	sesid,
	const char * const szDatabase,
	const char * const szSLV,
	JET_PFNSTATUS pfnStatus,
	JET_GRBIT grbit
	)
//  ================================================================
	{
	JET_DBUTIL dbutil;
	memset( &dbutil, 0, sizeof( dbutil ) );

	dbutil.cbStruct			= sizeof( JET_DBUTIL );
	dbutil.op				= opDBUTILEDBScrub;
	dbutil.szDatabase 		= const_cast<char *>( szDatabase );
	dbutil.szSLV 			= const_cast<char *>( szSLV );
	dbutil.sesid			= sesid;
	dbutil.grbitOptions		= grbit;	
	dbutil.pfnCallback		= pfnStatus;

	const JET_ERR err = JetDBUtilities( &dbutil );
	return err;
	}


#ifdef RECORD_FORMAT_UPGRADE
//  ================================================================
LOCAL JET_ERR ErrEDBUTLUpgradeRecordFormat(
	JET_SESID	sesid,
	const char * const szDatabase,
	JET_PFNSTATUS pfnStatus,
	JET_GRBIT grbit
	)
//  ================================================================
	{
	JET_DBUTIL dbutil;
	memset( &dbutil, 0, sizeof( dbutil ) );

	dbutil.cbStruct			= sizeof( JET_DBUTIL );
	dbutil.op				= opDBUTILDBConvertRecords;
	dbutil.szDatabase 		= const_cast<char *>( szDatabase );
	dbutil.szSLV 			= NULL;
	dbutil.sesid			= sesid;
	dbutil.grbitOptions		= grbit;	
	dbutil.pfnCallback		= pfnStatus;

	const JET_ERR err = JetDBUtilities( &dbutil );
	return err;
	}
#endif


//	callback function used by CopyFileEx
LOCAL DWORD CALLBACK DwEDBUTILCopyProgressRoutine(
	LARGE_INTEGER	cTotalFileSize,          // file size
	LARGE_INTEGER	cTotalBytesTransferred,  // bytes transferred
	LARGE_INTEGER	cStreamSize,             // bytes in stream
	LARGE_INTEGER	cStreamBytesTransferred, // bytes transferred for stream
	DWORD			dwStreamNumber,			// current stream
	DWORD			dwCallbackReason,		// callback reason
	HANDLE			hSourceFile,			// handle to source file
	HANDLE			hDestinationFile,		// handle to destination file
	INT				*pcShift				// from CopyFileEx. How much to shift file size to fit in INT value
)
	{
	enum { LITOI_LOW = 1, LITOI_HIGH, LITOI_SHIFT };
	//	zero sized file?
	if ( 0 == cTotalFileSize.QuadPart )
		{
		return PROGRESS_CONTINUE;
		}
	JET_SNPROG snpprog;
	assert( NULL != pcShift );
	if ( -1 == *pcShift )
		{ 
		PrintStatus( 0, -1, JET_sntBegin, (void *)szCopyFileStatus );
		assert( sizeof( ULONG ) == sizeof( INT ) );
		assert( sizeof( LARGE_INTEGER ) > sizeof( INT ) );
		//  if all data fits in INT value
		if ( 0 == (ULONG)cTotalFileSize.HighPart )
			{
			*pcShift = LITOI_LOW;
			}
		//  if High part can be used as 1% change detector
		else if ( 100 <= (ULONG)cTotalFileSize.HighPart )
			{
			*pcShift = LITOI_HIGH;
			}
		else
			{
			*pcShift = LITOI_SHIFT;
			}
		}
	snpprog.cbStruct = sizeof( snpprog );
	switch ( *pcShift )
		{
		//  if all data fits in INT value
		case LITOI_LOW:
			assert( 0 == cTotalFileSize.HighPart );
			snpprog.cunitTotal = cTotalFileSize.LowPart;
			snpprog.cunitDone = cTotalBytesTransferred.LowPart;
			break;
		//  if High part can be used as 1% change detector
		case LITOI_HIGH:
			assert( 100 <= cTotalFileSize.HighPart );
			snpprog.cunitTotal = cTotalFileSize.HighPart;
			snpprog.cunitDone = cTotalBytesTransferred.HighPart;
			break;
		//  if none of the above shift it 7 times because 2^7 > 100 and it will move 
		//	all High Part data to Low Part
		case LITOI_SHIFT:
			assert( 100 > cTotalFileSize.HighPart && 0 < cTotalFileSize.HighPart );
			snpprog.cunitTotal = (INT)( cTotalFileSize.QuadPart >> 7 );
			snpprog.cunitDone = (INT)( cTotalBytesTransferred.QuadPart >> 7 );
			break;
		default:
			assert( fFalse );
		}
	if ( cTotalBytesTransferred.QuadPart == cTotalFileSize.QuadPart )
		{
		PrintStatus( 0, -1, JET_sntComplete, &snpprog );
		}
	else
		{
		PrintStatus( 0, -1, JET_sntProgress, &snpprog );
		}
	return PROGRESS_CONTINUE;
	}

LOCAL JET_ERR ErrEDBUTLMoveFile(
	const char * const szExistingFileName,  // file name
	const char * const szNewFileName,       // new file name
	const DWORD dwFlags )
	{
	printf( szMoveFile, szExistingFileName, szNewFileName );
	if ( !MoveFileEx( szExistingFileName, szNewFileName, dwFlags ) )
		{
		DWORD dw = GetLastError();
		
		if ( ( dwFlags & ~MOVEFILE_REPLACE_EXISTING ) != 0 )
			{
			// unsupported move flag
			assert( fFalse );
			printf( "%s%c", szMoveFailed, cNewLine );
			return JET_errFileAccessDenied;
			}

		if ( dw == ERROR_CALL_NOT_IMPLEMENTED )
			{
			//	MoveFileEx is not implemented in Win9x
			dw = 0;
			if ( dwFlags & MOVEFILE_REPLACE_EXISTING )
				{
				DeleteFile( szNewFileName );
				}
			if ( !MoveFile( szExistingFileName, szNewFileName ) )
				{
				dw = GetLastError();
				if ( dw == ERROR_NOT_SAME_DEVICE )
					{
					//	the source file is on a different device -- we must copy it instead
					dw = 0;
					printf( szCopyFile );
					if ( !CopyFile( szExistingFileName, szNewFileName, ( dwFlags & MOVEFILE_REPLACE_EXISTING ? FALSE : TRUE ) ) )
						{
						dw = GetLastError();
						}
					}
				}
			}
		else if ( dw == ERROR_NOT_SAME_DEVICE )
			{
			typedef WINBASEAPI BOOL WINAPI PFNCopyFileEx( LPCTSTR, LPCTSTR, LPPROGRESS_ROUTINE, LPVOID, LPBOOL, DWORD );

#ifdef UNICODE
			PFNCopyFileEx*	pfnCopyFileEx = (PFNCopyFileEx*)GetProcAddress( GetModuleHandle( _T( "kernel32.dll" ) ), _T( "CopyFileExW" ) );
#else  //  !UNICODE
			PFNCopyFileEx*	pfnCopyFileEx = (PFNCopyFileEx*)GetProcAddress( GetModuleHandle( _T( "kernel32.dll" ) ), _T( "CopyFileExA" ) );
#endif  //  UNICODE

			//	the source file is on a different device -- we must copy it instead
			BOOL		fCancel		= fFalse;
			INT			cShift		= -1;		// used from progress routine
			const BOOL	fSuccess	= (	pfnCopyFileEx &&
										pfnCopyFileEx(	szExistingFileName,
														szNewFileName,
														LPPROGRESS_ROUTINE( DwEDBUTILCopyProgressRoutine ),
														&cShift,
														&fCancel,
														(	dwFlags & MOVEFILE_REPLACE_EXISTING ?
																0 :
																COPY_FILE_FAIL_IF_EXISTS ) ) );
			return ( fSuccess ? JET_errSuccess : JET_errFileAccessDenied );
			}
			
		if ( dw != 0 )
			{
			printf( "%s%c", szMoveFailed, cNewLine );
			return JET_errFileAccessDenied;
			}
		}

	printf( "%s%c", szMoveDone, cNewLine );
	return JET_errSuccess;
	}


LOCAL VOID EDBUTLDeleteTemp( const UTILOPTS * const popts )
	{
	assert( NULL != popts->szTempDB );
	DeleteFile( popts->szTempDB);
	if ( NULL != popts->szTempSLV )
		{
		DeleteFile( popts->szTempSLV );
		}
	}

// Backs up source database if required, then copies temporary database over
// source database if required.  Should be called after Jet has terminated.	
LOCAL JET_ERR ErrEDBUTLBackupAndInstateDB(
	JET_SESID	sesid,
	UTILOPTS	*popts )
	{
	JET_ERR		err = JET_errSuccess;;

	assert( popts->szSourceDB != NULL );
	assert( popts->szTempDB != NULL );

	// backup/instate cannot be respected on in-place upgrade.

	if ( FUTILOPTSInPlaceUpgrade( popts->fUTILOPTSFlags ) )
		{
		return JET_errSuccess;
		}

	//	BUGFIX (X5:123014): upgrade from 8.3 to full filename
	
	_TCHAR	szSourceDB[_MAX_PATH+1];
	_TCHAR	szSourceSLV[_MAX_PATH+1];

	WIN32_FIND_DATA wfd;
	HANDLE 			hFind;
		
	hFind = FindFirstFile( popts->szSourceDB, &wfd );
	if( INVALID_HANDLE_VALUE != hFind )
		{
		_TCHAR	szDrive[_MAX_PATH+1];
		_TCHAR	szDir[_MAX_PATH+1];
		
		_tsplitpath( popts->szSourceDB, szDrive, szDir, NULL, NULL );
		_makepath( szSourceDB, szDrive, szDir, NULL, NULL );
		_tcscat( szSourceDB, wfd.cFileName );
		FindClose( hFind );
		}
	else
		{
		_tcscpy( szSourceDB, popts->szSourceDB );
		}

	if ( !FUTILOPTSDefragSLVDontCopy( popts->fUTILOPTSFlags ) )
		{
		assert( NULL != popts->szSourceSLV );
		assert( NULL != popts->szTempSLV );
		
		hFind = FindFirstFile( popts->szSourceSLV, &wfd );
		if( INVALID_HANDLE_VALUE != hFind )
			{
			_TCHAR	szDrive[_MAX_PATH+1];
			_TCHAR	szDir[_MAX_PATH+1];
		
			_tsplitpath( popts->szSourceSLV, szDrive, szDir, NULL, NULL );
			_makepath( szSourceSLV, szDrive, szDir, NULL, NULL );
			_tcscat( szSourceSLV, wfd.cFileName );
			FindClose( hFind );
			}
		else
			{
			_tcscpy( szSourceSLV, popts->szSourceSLV );
			}
		}


	// Make backup before instating, if requested.

	if ( popts->szBackup != NULL )
		{
		err = ErrEDBUTLMoveFile( szSourceDB, popts->szBackup, 0 );
		if ( err < 0 )
			{
			printf( szErr4, popts->szBackup );
			printf( "%c%c", cNewLine, cNewLine );
			Call( err );
			}
		if ( !FUTILOPTSDefragSLVDontCopy( popts->fUTILOPTSFlags ) )
			{
			_TCHAR	szBackupSLV[_MAX_PATH+1];

			EDBUTLGetSLVNameFromDbName( popts->szBackup, szBackupSLV );
			err = ErrEDBUTLMoveFile( szSourceSLV, szBackupSLV, 0 );
			if ( err < 0 )
				{
				printf( szErr4, popts->szBackup );
				printf( "%c%c", cNewLine, cNewLine );
				Call( err );
				}
			}
		}

	if ( !FUTILOPTSPreserveTempDB( popts->fUTILOPTSFlags ) )
		{
		err = ErrEDBUTLMoveFile( popts->szTempDB, szSourceDB, MOVEFILE_REPLACE_EXISTING );
		if ( err < 0 )
			{
			printf( szErr5, szSourceDB, popts->szTempDB, szSourceDB );
			printf( "%c%c", cNewLine, cNewLine );
			Call( err );
			}
		if ( !FUTILOPTSDefragSLVDontCopy( popts->fUTILOPTSFlags ) )
			{
			err = ErrEDBUTLMoveFile( popts->szTempSLV, szSourceSLV, MOVEFILE_REPLACE_EXISTING );
			if ( err < 0 )
				{
				printf( szErr5, szSourceSLV, popts->szTempSLV, szSourceSLV );
				printf( "%c%c", cNewLine, cNewLine );
				Call( err );
				}
			}
		// Delete temporary database only if everything was successful.			
		EDBUTLDeleteTemp( popts );
		}

HandleError:
	return err;
	}


// Load registry environment, if enabled.  Then load command-line overrides.
LOCAL JET_ERR ErrEDBUTLUserSystemParameters( JET_INSTANCE *pinstance, UTILOPTS *popts )
	{
	JET_ERR	err;

	// Facilitate debugging.
	Call( JetSetSystemParameter( pinstance, 0, JET_paramAssertAction, JET_AssertMsgBox, NULL ) );

	// Command-line parameters override all default and registry values.
	if ( popts->szLogfilePath != NULL )
		{
		Call( JetSetSystemParameter( pinstance, 0, JET_paramLogFilePath, 0, popts->szLogfilePath ) );
		}
	if ( popts->szSystemPath != NULL )
		{
		Call( JetSetSystemParameter( pinstance, 0, JET_paramSystemPath, 0, popts->szSystemPath ) );
		}
	if ( popts->cpageBuffers != 0 )
		{
#if 0	// no longer needed because of DBA
		unsigned long ulCacheSizeMax;
		Call( JetGetSystemParameter( *pinstance, 0, JET_paramCacheSizeMax, &ulCacheSizeMax, NULL, 0 ) );
		if ( (long)ulCacheSizeMax < popts->cpageBuffers )
			{
			Call( JetSetSystemParameter( pinstance, 0, JET_paramCacheSizeMax, popts->cpageBuffers, NULL ) );
			Call( JetSetSystemParameter( pinstance, 0, JET_paramStopFlushThreshold, popts->cpageBuffers, NULL ) );
			Call( JetSetSystemParameter( pinstance, 0, JET_paramStartFlushThreshold, popts->cpageBuffers * 1 / 100, NULL ) );
			Call( JetSetSystemParameter( pinstance, 0, JET_paramStopFlushThreshold, popts->cpageBuffers * 10 / 100, NULL ) );
			}
		else
			{
			Call( JetSetSystemParameter( pinstance, 0, JET_paramStartFlushThreshold, 1, NULL ) );
			Call( JetSetSystemParameter( pinstance, 0, JET_paramStopFlushThreshold, popts->cpageBuffers * 10 / 100, NULL ) );
			Call( JetSetSystemParameter( pinstance, 0, JET_paramStartFlushThreshold, popts->cpageBuffers * 1 / 100, NULL ) );
			Call( JetSetSystemParameter( pinstance, 0, JET_paramCacheSizeMax, popts->cpageBuffers, NULL ) );
			}
#endif	//	0
		}
	if ( popts->cpageBatchIO != 0 )
		{
		Call( JetSetSystemParameter( pinstance, 0, JET_paramBatchIOBufferMax, popts->cpageBatchIO * 4, NULL ) );
		}
	if ( popts->cpageDbExtension != 0 )
		{
		Call( JetSetSystemParameter( pinstance, 0, JET_paramDbExtensionSize, popts->cpageDbExtension, NULL ) );
		}

	if ( NULL != popts->szBase )
		{
		Call( JetSetSystemParameter( pinstance, 0, JET_paramBaseName, NULL, popts->szBase ) );
		}
HandleError:
	return err;
	}


// Teminate Jet, either normally or abnormally.
LOCAL JET_ERR ErrEDBUTLCleanup( JET_INSTANCE instance, JET_SESID sesid, JET_ERR err )
	{
	if ( 0 != sesid && JET_sesidNil != sesid )
		{
		JET_ERR	errT = JetEndSession( sesid, 0 );

		if ( err >= 0 )
			err = errT;
		}

	if ( err < 0 ) 
		{
		// On error, terminate abruptly and throw out return code from JetTerm2().
		JetTerm2( instance, JET_bitTermAbrupt );
		}
	else 
		{
		err = JetTerm2( instance, JET_bitTermComplete );
		}

	return err;
	}


LOCAL BOOL FAquireBackupRestoreRights()
{

   BOOL		ret_val = TRUE ;
   HANDLE 	ProcessHandle;
   DWORD  	DesiredAccess;
   HANDLE 	TokenHandle;
   LUID   	BackupValue;
   LUID   	RestoreValue;
   TOKEN_PRIVILEGES NewState;


   // get process handle

   ProcessHandle = GetCurrentProcess();

   // open process token

   DesiredAccess = MAXIMUM_ALLOWED;

   if ( ! OpenProcessToken( ProcessHandle, DesiredAccess, &TokenHandle ) ) {
      return FALSE;
   }

   // adjust backup token privileges
   if ( ! LookupPrivilegeValue( NULL, TEXT("SeRestorePrivilege"), &RestoreValue ) ) {
      ret_val = FALSE;
   }

   if ( ! LookupPrivilegeValue( NULL, TEXT("SeBackupPrivilege"), &BackupValue ) ) {
      ret_val = FALSE;
   }

   // Enable backup privilege for this process

   NewState.PrivilegeCount = 1;
   NewState.Privileges[0].Luid = BackupValue;
   NewState.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

   if ( ! AdjustTokenPrivileges( TokenHandle, FALSE, &NewState, (DWORD)0, NULL, NULL ) ) {
      ret_val = FALSE;
   }


   NewState.PrivilegeCount = 1;
   NewState.Privileges[0].Luid = RestoreValue;
   NewState.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

   if ( ! AdjustTokenPrivileges( TokenHandle, FALSE, &NewState, (DWORD)0, NULL, NULL ) ) {
      ret_val = FALSE;
   }
// AdjustTokenPriv always returns SUCCESS, call GetLast to see if it worked.

   if ( GetLastError() != ERROR_SUCCESS ) {
      ret_val = FALSE;
   }

   // close process token

   CloseHandle( TokenHandle );
   return( ret_val );
}

#define JET_errReturnedForESEBCLI2		JET_errInternalError
#define JET_errReturnedForESEBACK2		JET_errInternalError

#define CallHr( func )									\
	{ 													\
	hr = func; 											\
	hrGLE = GetLastError(); 							\
	if ( hrNone != hr ) 								\
		{												\
		goto HandleError;								\
		}												\
	}

unsigned long WszFromSzGetSize( const char * sz )
	{
	return MultiByteToWideChar(CP_OEMCP, 0, sz, -1, NULL, 0);
	}

WCHAR * WszFromSz( const char *  sz )
	{
	WCHAR *  wsz;
	long cw;

	if ( 0 == ( cw = WszFromSzGetSize(sz) ) )
		return(NULL);

	if ( ( wsz = (WCHAR *) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, cw * sizeof(WCHAR) ) ) == NULL )
		return(NULL);

	if ( MultiByteToWideChar(CP_OEMCP, 0, sz, -1, wsz, cw ) == 0 )
		{
		LocalFree(wsz);
		return(NULL);
		}

	return(wsz);
	}

WCHAR * WszCopy( const WCHAR *  wsz )
	{
	WCHAR *  wszCopy;
	long cw;

	assert ( wsz );
	cw = (ULONG)wcslen( wsz ) + 1;

	if ( ( wszCopy = (WCHAR *) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, cw * sizeof(WCHAR) ) ) == NULL )
		return(NULL);

	wcscpy ( wszCopy, wsz );
	return( wszCopy );
	}


void PrintESEBCLI2Error ( HRESULT hr, HRESULT hrGLE, HMODULE hESEBCLI2 )
	{		
	LPVOID 		lpMsgBuf 				= NULL;
	char *		szFinalMsg 				= NULL;

	if ( hrNone == hr )
		return;
		
	if ( 0 == FormatMessage( 
			    FORMAT_MESSAGE_FROM_HMODULE | 
			    FORMAT_MESSAGE_FROM_SYSTEM | 
			    FORMAT_MESSAGE_IGNORE_INSERTS |
		    	FORMAT_MESSAGE_ALLOCATE_BUFFER,
			    hESEBCLI2,
			    hr,
			    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
			    (LPTSTR) &lpMsgBuf,
		    	0,
			    NULL ) )
	    {
		lpMsgBuf = NULL;
	    }

	if ( lpMsgBuf )
		{
		if ( hr == hrErrorFromESECall || hr == hrErrorFromCallbackCall )
			{
			szFinalMsg = (char *) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT,	sizeof( char ) * ( strlen( (char *)lpMsgBuf ) + 1 ) + 32 );
			if ( szFinalMsg )						
				{
				sprintf( szFinalMsg, (char *) lpMsgBuf, hrGLE );
				LocalFree( lpMsgBuf );	
				}
			else
				{
				// print the message without error number
				szFinalMsg = (char *)lpMsgBuf;
				}				
			}
		else
			{
			szFinalMsg = (char *)lpMsgBuf;
			}
			
		printf( szFinalMsg );
		LocalFree ( szFinalMsg );		
		}
	}

typedef HRESULT (ESEBACK_API * PfnHrESERestoreReopen)(
	IN  WCHAR *					wszServerName,
	IN  WCHAR *					wszServiceAnnotation,
	IN  WCHAR *		 			wszRestoreLogPath,
	OUT HCCX *					phccxRestoreContext);
	
typedef HRESULT (ESEBACK_API * PfnHrESERestoreClose)(
	IN HCCX hccxRestoreContext,
	IN unsigned long fRestoreAbort);

typedef HRESULT (ESEBACK_API * PfnHrESERestoreComplete)(
	IN  HCCX 				hccxRestoreContext,
	IN  WCHAR *				wszRestoreInstanceSystemPath,
	IN  WCHAR *				wszRestoreInstanceLogPath,
	IN  WCHAR *				wszTargetInstanceName,
	IN  unsigned long 		fFlags);

typedef HRESULT (ESEBACK_API * PfnHrESERestoreLoadEnvironment)(
	IN	WCHAR *				wszServerName,
	IN	WCHAR *				wszRestoreLogPath,
	OUT RESTORE_ENVIRONMENT ** 	ppRestoreEnvironment);

typedef HRESULT (ESEBACK_API * PfnHrESERestoreGetEnvironment)(
	IN  HCCX 					hccxRestoreContext,
	OUT RESTORE_ENVIRONMENT ** 	ppRestoreEnvironment);

typedef void (ESEBACK_API * PfnESERestoreFreeEnvironment)(
	IN  RESTORE_ENVIRONMENT * 	pRestoreEnvironment);

LOCAL void PrintField( const WCHAR * wszDesc, int cDesc, const WCHAR * wszData, const BOOL fNewLine = TRUE )
	{
	wprintf( L"%*s %s%s", cDesc, wszDesc, wszData?wszData:L"", fNewLine?L"\n":L"" );
	}

WCHAR * rwszRecoverStatus[] = { 
	L"recoverInvalid",
	L"recoverNotStarted",
	L"recoverStarted",
	L"recoverEnded"
	};


LOCAL BOOL FDBUTLLoadLibrary( const _TCHAR* szLibrary, HMODULE *plibrary )
	{
	while ( NULL == ( *plibrary = LoadLibrary( (LPTSTR)szLibrary ) ) )
		{		
		_TCHAR szMessage[256];
		(void)_stprintf(
			szMessage,
			_T( 	"Unable to find the callback library %s (or one of its dependencies).\r\n"
					"Copy in the file and hit OK to retry, or hit Cancel to abort.\r\n" ),
			szLibrary );

		const int id = MessageBox(
							NULL,
							szMessage,
							_T( "Callback DLL not found" ),
							MB_SERVICE_NOTIFICATION | MB_SYSTEMMODAL | MB_ICONSTOP |
							MB_OKCANCEL );

		if ( IDOK != id )
			{
			break;
			}
		}
		
	return ( NULL != *plibrary );
	}


LOCAL JET_ERR ErrDBUTLDumpRestoreEnv( const char * szRestorePath )
	{
	JET_ERR			err 				= JET_errSuccess;
	HRESULT 		hr 					= hrNone;
	HRESULT 		hrGLE 				= hrNone;
	WCHAR * 		wszRestorePath 		= NULL;
	HMODULE 		hESEBCLI2 			= NULL;	
	int 			cDesc 				= 30;

	RESTORE_ENVIRONMENT * 				pREnv 					= NULL;
	PfnHrESERestoreLoadEnvironment 		pfnHrESERestoreLoadEnv 	= NULL;
	PfnESERestoreFreeEnvironment 		pfnESERestoreFreeEnv 	= NULL;

	unsigned long 	iDb;

	assert ( szRestorePath );

	if ( !FDBUTLLoadLibrary( ESEBCLI2_DLL_NAME, &hESEBCLI2 ) )
		{
		Call ( JET_errCallbackNotResolved );
		}

	pfnHrESERestoreLoadEnv = (PfnHrESERestoreLoadEnvironment) GetProcAddress( hESEBCLI2, "HrESERestoreLoadEnvironment" );
	pfnESERestoreFreeEnv = (PfnESERestoreFreeEnvironment) GetProcAddress( hESEBCLI2, "ESERestoreFreeEnvironment" );

	if ( !pfnESERestoreFreeEnv || !pfnHrESERestoreLoadEnv )
		{
		Call ( JET_errCallbackNotResolved );
		}
			
	wszRestorePath = WszFromSz( szRestorePath );
	if ( !wszRestorePath )
		{
		Call ( JET_errOutOfMemory );
		}
		
	PrintField( L"Restore log file:", cDesc, wszRestorePath );
	PrintField( L"", cDesc, NULL );

	assert ( pfnHrESERestoreLoadEnv );
	CallHr ( (*pfnHrESERestoreLoadEnv)( 	NULL,
											wszRestorePath,
											&pREnv ) );

	// dump Restore.Env
	assert ( pREnv );

	PrintField( L"Restore Path:", cDesc, pREnv->m_wszRestoreLogPath );
	PrintField( L"Annotation:", cDesc, pREnv->m_wszAnnotation );	

	PrintField( L"Backup Instance:", cDesc, pREnv->m_wszSrcInstanceName );
	PrintField( L"Target Instance:", cDesc, pREnv->m_wszTargetInstanceName );

	PrintField( L"Restore Instance System Path:", cDesc, pREnv->m_wszRestoreInstanceSystemPath );
	PrintField( L"Restore Instance Log Path:", cDesc, pREnv->m_wszRestoreInstanceLogPath );

	PrintField( L"", cDesc, NULL );

	{
	WCHAR wszBuffer[32];
	swprintf( wszBuffer, L"%d database(s)", pREnv->m_cDatabases );
	PrintField( L"Databases:", cDesc, wszBuffer );
	}

	assert ( pREnv->m_wszDatabaseDisplayName || 0 == pREnv->m_cDatabases);
	assert ( pREnv->m_rguidDatabase || 0 == pREnv->m_cDatabases);
	assert ( pREnv->m_wszDatabaseStreamsS || 0 == pREnv->m_cDatabases);
	assert ( pREnv->m_wszDatabaseStreamsD || 0 == pREnv->m_cDatabases);

	cDesc += 8;
	for (iDb = 0; iDb < pREnv->m_cDatabases; iDb++)
		{		
		assert ( pREnv->m_wszDatabaseDisplayName[iDb] );
		assert ( pREnv->m_wszDatabaseStreamsS[iDb] );
		assert ( pREnv->m_wszDatabaseStreamsS[iDb] );

		WCHAR guidStr[256];
		WCHAR * wszStreams;
		GUID guid = pREnv->m_rguidDatabase[iDb];
		
		PrintField( L"Database Name:", cDesc, pREnv->m_wszDatabaseDisplayName[iDb] );
		// like: 6B29FC40-CA47-1067-B31D-00DD010662DA
		swprintf(	guidStr,
					L"%08X-%04X-%04X-%08X%08X",
					guid.Data1, guid.Data2, guid.Data3,
					*(DWORD *)&guid.Data3,*( 1 + (DWORD *)&guid.Data3 ) );

		PrintField( L"GUID:", cDesc, guidStr );

		wszStreams = pREnv->m_wszDatabaseStreamsS[iDb];
		PrintField( L"Source Files:", cDesc, NULL, FALSE );
		while ( L'\0' != wszStreams[0] )
			{
			wprintf( L"%s ", wszStreams );
			wszStreams += wcslen( wszStreams ) + 1;
			}
		wprintf( L"\n" );
		wszStreams = pREnv->m_wszDatabaseStreamsD[iDb];
		PrintField( L"Destination Files:", cDesc, NULL, FALSE );
		while ( L'\0' != wszStreams[0] )
			{
			wprintf( L"%s ", wszStreams );
			wszStreams += wcslen( wszStreams ) + 1;
			}
		PrintField( L"", cDesc, NULL );
		PrintField( L"", cDesc, NULL );
		}
		
	cDesc -= 8;
	PrintField( L"", cDesc, NULL );
	PrintField( L"", cDesc, NULL );
		
	{
	WCHAR wszBuffer[32];
	
	if ( pREnv->m_wszLogBaseName )
		{
		assert ( 0 != pREnv->m_ulGenLow );
		assert ( 0 != pREnv->m_ulGenHigh );
		assert ( pREnv->m_ulGenLow <= pREnv->m_ulGenHigh );
		swprintf( wszBuffer, L"%s%05X.log - %s%05X.log", 
			pREnv->m_wszLogBaseName,
			pREnv->m_ulGenLow,
			pREnv->m_wszLogBaseName,
			pREnv->m_ulGenHigh);
		}
	else
		{
		assert ( 0 == pREnv->m_ulGenLow );
		assert ( 0 == pREnv->m_ulGenHigh );
		swprintf( wszBuffer, L"no log files restored");
		}
	PrintField( L"Log files range:", cDesc, wszBuffer );
	
	PrintField( L"Last Restore Time:", cDesc, _wctime ( &pREnv->m_timeLastRestore) );

	RECOVER_STATUS status = pREnv->m_statusLastRecover;

	if ( status >= sizeof(rwszRecoverStatus)/ sizeof(rwszRecoverStatus[0] ) )
		status = recoverInvalid;
		
	PrintField( L"Recover Status:", cDesc, rwszRecoverStatus [ status ] );

	swprintf( wszBuffer, L"0x%08X", pREnv->m_hrLastRecover);
	PrintField( L"Recover Error:", cDesc, wszBuffer );
	
	PrintField( L"Recover Time:", cDesc, _wctime ( &pREnv->m_timeLastRecover ) );
	}

											
HandleError:

	LocalFree ( wszRestorePath );

	if ( pREnv )
		{
		assert ( pfnESERestoreFreeEnv );
		(*pfnESERestoreFreeEnv)( pREnv );
		pREnv = NULL;
		}

	if ( hrNone != hr )
		{
		assert ( hESEBCLI2 );
		PrintESEBCLI2Error ( hr, hrGLE, hESEBCLI2 );
		err = JET_errReturnedForESEBCLI2;
		}
	
	if ( NULL != hESEBCLI2 )
		{
		FreeLibrary( hESEBCLI2 );
		hESEBCLI2 = NULL;
		}

	return err;	
	}

LOCAL JET_ERR ErrDBUTLRestoreComplete( const char * szFullRestorePath, const char * szTargetInstance, BOOL fKeepLogs )
	{
	JET_ERR			err 				= JET_errSuccess;
	HRESULT 		hr 					= hrNone;
	HRESULT 		hrGLE;

	WCHAR * 		wszRestorePath 		= NULL;
	WCHAR * 		wszTargetInstance 	= NULL;
	WCHAR * 		wszComputerName 	= NULL;
	
	DWORD 			nSize ;
	char 			szComputerName[MAX_COMPUTERNAME_LENGTH + 1];
	
	HCCX 			hccxRestoreContext 	= NULL;	

	HMODULE 		hESEBCLI2 = NULL;
	
	PfnHrESERestoreReopen 				pfnErrESERestoreReopen =	 NULL;
	PfnHrESERestoreClose 				pfnErrESERestoreClose 		= NULL;
	PfnHrESERestoreComplete 			pfnErrESERestoreComplete 	= NULL;

	RESTORE_ENVIRONMENT * 				pREnv 						= NULL;
	PfnHrESERestoreGetEnvironment 		pfnHrESERestoreGetEnv 		= NULL;
	PfnESERestoreFreeEnvironment 		pfnESERestoreFreeEnv 		= NULL;

	assert ( szFullRestorePath );
	
	if ( !FDBUTLLoadLibrary( ESEBCLI2_DLL_NAME, &hESEBCLI2 ) )
		{
		Call ( JET_errCallbackNotResolved );
		}

	pfnErrESERestoreReopen = (PfnHrESERestoreReopen) GetProcAddress( hESEBCLI2, "HrESERestoreReopen" );
	pfnErrESERestoreClose = (PfnHrESERestoreClose) GetProcAddress( hESEBCLI2, "HrESERestoreClose" );
	pfnErrESERestoreComplete = (PfnHrESERestoreComplete) GetProcAddress( hESEBCLI2, "HrESERestoreComplete" );
	pfnHrESERestoreGetEnv = (PfnHrESERestoreGetEnvironment) GetProcAddress( hESEBCLI2, "HrESERestoreGetEnvironment" );
	pfnESERestoreFreeEnv = (PfnESERestoreFreeEnvironment) GetProcAddress( hESEBCLI2, "ESERestoreFreeEnvironment" );

	if ( 	!pfnErrESERestoreReopen || !pfnErrESERestoreClose || !pfnErrESERestoreComplete ||
			!pfnHrESERestoreGetEnv || !pfnESERestoreFreeEnv)
		{
		Call ( JET_errCallbackNotResolved );
		}

	if ( !FAquireBackupRestoreRights() )
		{
		Call ( JET_errReturnedForESEBCLI2 ); 
		}

	nSize = sizeof(szComputerName);		
	if ( !GetComputerName ( szComputerName, &nSize ) )
		{
		Call ( JET_errNTSystemCallFailed );
		}
	
	wszRestorePath = WszFromSz( szFullRestorePath );
	wszComputerName = WszFromSz( szComputerName );

	if ( !wszRestorePath || !wszComputerName )
		{
		Call ( JET_errOutOfMemory );
		}
		
	printf( "Restore log files: %s\n", szFullRestorePath );

	assert ( pfnErrESERestoreReopen );
	CallHr ( (*pfnErrESERestoreReopen)( 	wszComputerName,
											NULL,
											wszRestorePath,
											&hccxRestoreContext ) );
	
	if ( szTargetInstance )
		{
		if ( szTargetInstance[0] == '\0' )
			{
			// no play forward 
			wszTargetInstance = NULL;
			}
		else
			{
			wszTargetInstance = WszFromSz( szTargetInstance );
			if ( !wszTargetInstance )
				{
				Call ( JET_errOutOfMemory );
				}
			}
		}
	else
		{
		// get the instance form the Restore.Env, as the Source Instance Name
		assert ( pfnHrESERestoreGetEnv );
		CallHr ( (*pfnHrESERestoreGetEnv)( 	hccxRestoreContext,
											&pREnv ) );

		// dump Restore.Env
		assert ( pREnv );
		assert ( pREnv->m_wszSrcInstanceName );
		wszTargetInstance = WszCopy( pREnv->m_wszSrcInstanceName );

		assert ( pfnESERestoreFreeEnv );
		(*pfnESERestoreFreeEnv)( pREnv );
		pREnv = NULL;

		if ( !wszTargetInstance )
			{
			Call ( JET_errOutOfMemory );
			}
		}
		
	wprintf( L"  Target Instance: %s\n", wszTargetInstance?wszTargetInstance:L"" );
	
	assert ( pfnErrESERestoreComplete );
	CallHr ( (*pfnErrESERestoreComplete)( 	hccxRestoreContext,
									wszRestorePath,
									wszRestorePath,
									wszTargetInstance,
									fKeepLogs?ESE_RESTORE_KEEP_LOG_FILES:0 // no db mount, wait restore complete
									) );	
									
HandleError:

	LocalFree ( wszRestorePath );
	LocalFree ( wszTargetInstance );
	LocalFree ( wszComputerName );

	if ( pREnv )
		{
		assert ( pfnESERestoreFreeEnv );
		(*pfnESERestoreFreeEnv)( pREnv );
		pREnv = NULL;
		}

	if ( hccxRestoreContext )
		{
		assert ( pfnErrESERestoreClose );
		(void) (*pfnErrESERestoreClose)( hccxRestoreContext, (hrNone == hr) ? RESTORE_CLOSE_NORMAL:RESTORE_CLOSE_ABORT );
		hccxRestoreContext = NULL;
		}

	if ( hrNone != hr )
		{
		assert ( hESEBCLI2 );
		PrintESEBCLI2Error ( hr, hrGLE, hESEBCLI2 );
		err = JET_errReturnedForESEBCLI2;
		}
	
	if ( NULL != hESEBCLI2 )
		{
		FreeLibrary( hESEBCLI2 );
		hESEBCLI2 = NULL;
		}

	return err;
	}

#ifdef RESTORE_SERVER_SIMULATION

// must match the definition from ESEBACK2\srvsim.cxx
typedef HRESULT (__stdcall * PfnServerSim)( const char * szFileDef );

LOCAL JET_ERR ErrDBUTLServerSim( const char * szSimulationDef )
	{
	JET_ERR			err 			= JET_errSuccess;
	HRESULT 		hr 				= hrNone;
	HRESULT 		hrGLE;
	HMODULE 		hESEBACK2 		= NULL;
	PfnServerSim 	pfnServerSim 	= NULL;

	if ( !FDBUTLLoadLibrary( ESEBACK2_DLL_NAME, &hESEBACK2 ) )
		{
		Call ( JET_errCallbackNotResolved );
		}

	pfnServerSim = (PfnServerSim) GetProcAddress( hESEBACK2, "ServerSim" );
	if ( !pfnServerSim )
		{
		Call ( JET_errCallbackNotResolved );
		}

	CallHr ( (*pfnServerSim)( szSimulationDef ) );
	
HandleError:

	if ( hrNone != hr )
		{
		assert ( hESEBACK2 );
		PrintESEBCLI2Error ( hr, hrGLE, hESEBACK2 );
		err = JET_errReturnedForESEBACK2;
		}

	if ( NULL != hESEBACK2 )
		{
		FreeLibrary( hESEBACK2 );
		hESEBACK2 = NULL;
		}

	return err;
	
	}

#endif // RESTORE_SERVER_SIMULATION

int _cdecl main( int argc, char *argv[] )
	{
	JET_INSTANCE	instance			= 0;
	JET_SESID		sesid				= JET_sesidNil;
	JET_ERR			err					= JET_errSuccess;
	JET_ERR			errRepaired			= JET_errSuccess;
	BOOL			fResult				= fTrue;
	UTILOPTS		opts;
	JET_CONVERT		convert;
	JET_DBUTIL		dbutil;
	ULONG			timer				= GetTickCount();
	INT				iSec, iMSec;
	BOOL			fWhitespaceOnErr	= fFalse;
	BOOL			fUnknownError		= fFalse;

	InitArg( argc, argv );
	memset( &opts, 0, sizeof(UTILOPTS) );
	opts.lDirtyLevel = 2;
	memset( &dbutil, 0, sizeof(JET_DBUTIL) );
	dbutil.cbStruct	= sizeof( dbutil );
	
	printf( "%c", cNewLine );

	const INT pid = GetCurrentProcessId();
	
	if ( GetArgCount() < 2 )
		{
		printf( szUsageErr9 );
		printf( "%c%c", cNewLine, cNewLine );
		goto Usage;
		}

	SetCurArgID(1);
	assert( GetCurArgID() == 1 );
	if ( strchr( szSwitches, GetCurArg()[0] ) == NULL )
		{
		printf( szUsageErr10 );
		printf( "%c%c", cNewLine, cNewLine );
		goto Usage;
		}

	sprintf( szDefaultTempDB, szDefaultTempDBFormat, pid );
	sprintf( szDefaultDefragDB, szDefaultDefragDBFormat, pid );
	sprintf( szDefaultUpgradeDB, szDefaultUpgradeDBFormat, pid );
	sprintf( szDefaultRepairDB, szDefaultRepairDBFormat, pid );
	sprintf( szDefaultIntegDB, szDefaultIntegDBFormat, pid );
	sprintf( szDefaultScrubDB, szDefaultScrubDBFormat, pid );

	assert( NULL == opts.pv );

	switch( GetCurArg()[1] )
		{
		case 'd':		// Defragment
		case 'D':
			opts.mode	= modeDefragment;
			fResult = FEDBUTLParseOptions( &opts, FEDBUTLParseDefragment );
			break;

		case 'r':		// Recovery
		case 'R':
			opts.mode = modeRecovery;
			fResult = FEDBUTLParseOptions( &opts, FEDBUTLParseRecovery );
			break;

		case 'g':		// inteGrity
		case 'G':
			opts.mode = modeIntegrity;
			fResult = FEDBUTLParseOptions( &opts, FEDBUTLParseIntegrity );
			break;

		case 'k':		// esefile - checksum
		case 'K':
			opts.mode = modeChecksum;
			fResult = FEDBUTLParseOptions( &opts, NULL );
			break;

		case 'p':		// rePair
		case 'P':
			opts.mode 	= modeRepair;
			fResult = FEDBUTLParseOptions( &opts, FEDBUTLParseRepair );
			break;

		case 'm':		// file duMp.
		case 'M':
			opts.mode = modeDump;
			opts.pv = &dbutil;

			fResult = FEDBUTLParseOptions( &opts, FEDBUTLParseDump );
			break;

#ifdef DEBUG
		case 'c':		// Hard Recovery (dump Restore.Env or/and RestoreComplete)
		case 'C':
			opts.mode = modeHardRecovery;
			opts.pv = &dbutil;

			fResult = FEDBUTLParseOptions( &opts, FEDBUTLParseHardRecovery );
			break;

		case 'b':		// Backup
		case 'B':
			opts.mode = modeBackup;
			fResult = FEDBUTLParseOptions( &opts, FEDBUTLParseBackup );
			break;

		case 's':
		case 'S':
			opts.mode 	= modeSLVMove;
			opts.pv 	= &dbutil;

			fResult = FEDBUTLParseOptions( &opts, FEDBUTLParseSLVMove );
			// db name and pgno are mandatory
			if ( NULL == opts.szSourceDB || 0 == dbutil.pgno )
				{
				fResult = fFalse;
				}
			dbutil.op = opDBUTILSLVMove;
			break;
#endif

#ifdef RECORD_FORMAT_UPGRADE
		case 'f':		// upgrade record Format
		case 'F':
			opts.mode 	= modeUpgradeRecordFormat;
			fResult = FEDBUTLParseOptions( &opts, FEDBUTLParseUpgradeRecordFormat );
			break;
#endif			

		case 'u':		// Upgrade/convert
		case 'U':
			opts.mode = modeUpgrade;
			memset( &convert, 0, sizeof(JET_CONVERT) );
			opts.pv = &convert;
			UTILOPTSSetDefragSLVDontCopy( opts.fUTILOPTSFlags );
			fResult = FEDBUTLParseOptions( &opts, FEDBUTLParseUpgrade );
			if ( fResult  &&  convert.szOldDll == NULL )
				{
				printf( szUsageErr11 );
				printf( "%c%c", cNewLine, cNewLine );
				fResult = fFalse;					
				}				
			break;		

		case 'z':		// Zero out deleted portions of the database
		case 'Z':
			opts.mode 	= modeScrub;
			fResult = FEDBUTLParseOptions( &opts, FEDBUTLParseScrub );
			break;

		case '?':
			goto Usage;			

#ifdef ESENT
#else
		case 'e':
		case 'E':
			if( !_strcmpi( argv[1]+1, "ese" ) )
				{
				opts.mode = modeSplash;
				break;
				}
#endif				

			//  else FALLTHRU
		default:
			printf( szUsageErr12 );
			printf( "%c%c", cNewLine, cNewLine );
			fResult = fFalse;
		}
		
	if ( !fResult )
		goto Usage;


	if( modeUpgrade == opts.mode ) 
		{
		Call( ErrUseDifferentDLL( _T( "ESENT97.DLL" ) ) );
		}

	Call( JetSetSystemParameter( &instance, 0, JET_paramRecovery, 0, "ESEUTIL" ) );

	//	generate a new temporary database name
	//	this may be overwritten later
	
	Call( JetSetSystemParameter( &instance, 0, JET_paramTempPath, 0, szDefaultTempDB ) );
	
	if ( !FUTILOPTSSuppressLogo( opts.fUTILOPTSFlags ) )
		{
		EDBUTLPrintLogo();
		}

	if ( FUTILOPTS8KPage( opts.fUTILOPTSFlags ) )
		{
		Call( JetSetSystemParameter( &instance, 0, JET_paramDatabasePageSize, 8192, NULL ) );
		}

	// Lights, cameras, action...
	timer = GetTickCount();

	switch ( opts.mode )
		{
		case modeRecovery:
			if ( NULL == opts.szBase )
				{
				printf( szUsageErr1, "logfile base name" );
				printf( "%c%c", cNewLine, cNewLine );
				Call( JET_errInvalidParameter );
				}

			printf( "Initiating RECOVERY mode...%c", cNewLine );
			printf( "    Logfile base name: %s%c", opts.szBase, cNewLine );

			if( 0 != opts.pageTempDBMin )
				{
				printf( "   Temp database size: %d%c", opts.pageTempDBMin, cNewLine );
				Call( JetSetSystemParameter( &instance, 0, JET_paramPageTempDBMin , opts.pageTempDBMin, NULL ) );
				}
			
			printf( "            Log files: %s%c", opts.szLogfilePath ? opts.szLogfilePath : "<current directory>", cNewLine );
			printf( "         System files: %s%c", opts.szSystemPath ? opts.szSystemPath : "<current directory>", cNewLine, cNewLine );

			if ( NULL != opts.szSourceDB )
				{
				//	HACK: Need some way of specifying an alternate database directory
				//	without having to expose a new system param
				printf( "   Database Directory: %s%c", opts.szSourceDB != szCurrDir ? opts.szSourceDB : "<current directory>", cNewLine, cNewLine );
				Call( JetSetSystemParameter( &instance, 0, JET_paramRecovery, 0xE5E, opts.szSourceDB ) );
				}

			Call( JetSetSystemParameter( &instance, 0, JET_paramRecovery, 0, "on" ) );
///			Call( JetSetSystemParameter( &instance, 0, JET_paramCacheSizeMax, 500, NULL ) );
///			Call( JetSetSystemParameter( &instance, 0, JET_paramMaxOpenTables, 10000, NULL ) );
///			Call( JetSetSystemParameter( &instance, 0, JET_paramMaxCursors, 10000, NULL ) );
///			Call( JetSetSystemParameter( &instance, 0, JET_paramMaxSessions, 16, NULL ) );
///			Call( JetSetSystemParameter( &instance, 0, JET_paramMaxVerPages, 128, NULL ) );
///			Call( JetSetSystemParameter( &instance, 0, JET_paramMaxTemporaryTables, 10000, NULL ) );
///			Call( JetSetSystemParameter( &instance, 0, JET_paramLogBuffers, 41, NULL ) );
			printf( "%c", cNewLine );

			// Set user overrides.
			Call( ErrEDBUTLUserSystemParameters( &instance, &opts ) );

			if ( opts.szBackup == NULL )
				{
				
				// Soft recovery.
				fWhitespaceOnErr = fTrue;
				printf( "Performing soft recovery..." );
				err = JetInit2( &instance, opts.grbitInit );
				Call( ErrEDBUTLCleanup( instance, 0, err ) );
				printf( "%c%c", cNewLine, cNewLine );
				}
			else
				{

				// Hard recovery.

				if ( opts.szRestore )
					{
					printf( "Restoring to '%s' from '%s'...", opts.szRestore, opts.szBackup );
					}
				else
					{
					printf( "Restoring to <current directory> from '%s'...", opts.szBackup );
					}
				err = JetRestore2( opts.szBackup, opts.szRestore, PrintStatus );
				printf( "%c%c", cNewLine, cNewLine );
				Call( err );
				}
			break;
	
#ifdef DEBUG
		case modeBackup:

			Call( ErrEDBUTLCheckBackupPath( &opts ) );
			printf( "Initiating BACKUP mode...%c", cNewLine );
			printf( "       Log files: %s%c", opts.szLogfilePath ? opts.szLogfilePath : "<current directory>", cNewLine );
			printf( "    System files: %s%c%c", opts.szSystemPath ? opts.szSystemPath : "<current directory>", cNewLine, cNewLine );

			Call( JetSetSystemParameter( &instance, 0, JET_paramRecovery, 0, "on" ) );
///			Call( JetSetSystemParameter( &instance, 0, JET_paramCacheSizeMax, 500, NULL ) );
///			Call( JetSetSystemParameter( &instance, 0, JET_paramMaxOpenTables, 10000, NULL ) );
///			Call( JetSetSystemParameter( &instance, 0, JET_paramMaxCursors, 10000, NULL ) );
///			Call( JetSetSystemParameter( &instance, 0, JET_paramMaxSessions, 16, NULL ) );
///			Call( JetSetSystemParameter( &instance, 0, JET_paramMaxVerPages, 128, NULL ) );
///			Call( JetSetSystemParameter( &instance, 0, JET_paramMaxTemporaryTables, 10000, NULL ) );
///			Call( JetSetSystemParameter( &instance, 0, JET_paramLogBuffers, 41, NULL ) );

			// Set user overrides.
			Call( ErrEDBUTLUserSystemParameters( &instance, &opts ) );

			fWhitespaceOnErr = fTrue;
			if ( FUTILOPTSIncrBackup( opts.fUTILOPTSFlags ) )
				{
				printf( "Performing incremental backup..." );
				Call( JetInit( &instance ) );
				CallJ( JetBackup( opts.szBackup, JET_bitBackupIncremental | JET_bitBackupAtomic, NULL ), Cleanup );
				}
			else
				{
				printf( "Performing full backup..." );
				Call( JetInit( &instance ) );
				CallJ( JetBackup( opts.szBackup, JET_bitBackupAtomic, NULL ), Cleanup );
				}
			fWhitespaceOnErr = fFalse;

			printf( "%c%c", cNewLine, cNewLine );
			Call( ErrEDBUTLCleanup( instance, sesid, JET_errSuccess ) );
			break;
#endif

		case modeDefragment:
				{
				JET_GRBIT	grbitDefrag = 0;
				if( FUTILOPTSDefragInfo( opts.fUTILOPTSFlags ) )
					grbitDefrag |= JET_bitCompactStats;
				if( FUTILOPTSDefragRepair( opts.fUTILOPTSFlags ) )
					grbitDefrag |= JET_bitCompactRepair;
				if ( !FUTILOPTSDefragSLVDontCopy( opts.fUTILOPTSFlags ) )
					grbitDefrag |= JET_bitCompactSLVCopy;
				
				CallJ( ErrEDBUTLCheckDBSLVNames( &opts, szDefaultDefragDB, szSourceStreamFileName, szTempStreamFileName ), Cleanup );

				//	there is no streaming file attached to database
				if ( NULL == opts.szSourceSLV )
					{
					UTILOPTSSetDefragSLVDontCopy( opts.fUTILOPTSFlags );
					grbitDefrag &= ~JET_bitCompactSLVCopy;
					}
				
				printf( "Initiating DEFRAGMENTATION mode...%c", cNewLine );
				printf( "            Database: %s%c", opts.szSourceDB, cNewLine );
				if( opts.szSourceSLV )
					{
					printf( "      Streaming File: %s%c", opts.szSourceSLV, cNewLine );
					}
				printf( "      Temp. Database: %s%c", opts.szTempDB, cNewLine );
				if( opts.szTempSLV )
					{
					printf( "Temp. Streaming File: %s%c", opts.szTempSLV, cNewLine );
					}

				fWhitespaceOnErr = fTrue;

				// Restart with logging/recovery disabled.
				Call( JetSetSystemParameter( &instance, 0, JET_paramRecovery, 0, "off" ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramEnableOnlineDefrag, 0, NULL ) );
				ULONG_PTR cbfCacheMax;
				Call( JetGetSystemParameter( instance, 0, JET_paramCacheSizeMax, &cbfCacheMax, NULL, 0 ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramStopFlushThreshold, 10 * cbfCacheMax / 100, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramStartFlushThreshold, 1 * cbfCacheMax / 100, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramDbExtensionSize, 256, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramEnableTempTableVersioning, fFalse, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramEnableIndexChecking, 0, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramMaxOpenTables, 10000, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramPreferredMaxOpenTables, 10000, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramDisableCallbacks, fTrue, NULL ) );

				// Set user overrides.
				Call( ErrEDBUTLUserSystemParameters( &instance, &opts ) );

				Call( JetInit( &instance ) );
				CallJ( JetBeginSession( instance, &sesid, szUser, szPassword ), Cleanup );

				// Detach temporary database and delete file if present (ignore errors).
				EDBUTLDeleteTemp( &opts );

				dbutil.sesid		= sesid;
				dbutil.op			= opDBUTILDBDefragment;
				dbutil.szDatabase	= opts.szSourceDB;
				dbutil.szSLV 		= opts.szSourceSLV;
				dbutil.szTable		= opts.szTempDB;
				dbutil.szIndex		= opts.szTempSLV;
				dbutil.grbitOptions	= grbitDefrag;
				dbutil.pfnCallback	= PrintStatus;
				
				CallJ( JetDBUtilities( &dbutil ), Cleanup );
				
				Call( ErrEDBUTLCleanup( instance, sesid, JET_errSuccess ) );

				Call( ErrEDBUTLBackupAndInstateDB( sesid, &opts ) );

				printf( "%cNote:%c", cNewLine, cNewLine );
				printf( "  It is recommended that you immediately perform a full backup%c", cNewLine );
				printf( "  of this database. If you restore a backup made before the%c", cNewLine );
				printf( "  defragmentation, the database will be rolled back to the state%c", cNewLine );
				printf( "  it was in at the time of that backup.%c%c", cNewLine, cNewLine );
				}
			break;

		case modeRepair:
				{			
				if( !FUTILOPTSSuppressLogo( opts.fUTILOPTSFlags ) )
					{
					const int id = MessageBox(
								NULL,
								szRepairMessage,
								szRepairCaption,
								MB_ICONSTOP | MB_OKCANCEL );
					if ( IDCANCEL == id )
						{
						err = JET_errInvalidParameter;
						Call( err );
						}
					}

				Call( ErrEDBUTLCheckDBSLVNames( &opts, szDefaultRepairDB, szSourceStreamFileName ) );

				printf( "Initiating REPAIR mode...%c", cNewLine );
				printf( "        Database: %s%c", opts.szSourceDB, cNewLine );
				if( opts.szSourceSLV )
					{
					printf( "  Streaming File: %s%c", opts.szSourceSLV, cNewLine );
					}
				printf( "  Temp. Database: %s%c", opts.szTempDB, cNewLine );

				fWhitespaceOnErr = fTrue;

				Call( JetSetSystemParameter( &instance, 0, JET_paramRecovery, 0, "repair_off" ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramTempPath, 0, opts.szTempDB ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramEnableTempTableVersioning, fFalse, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramEnableOnlineDefrag, 0, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramDbExtensionSize, 256, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramEnableIndexChecking, 0, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramMaxOpenTables, 10000, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramPreferredMaxOpenTables, 10000, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramDisableCallbacks, fTrue, NULL ) );
				//  create plenty of sessions for multi-threaded integrity/repair
				//  need to have plenty of pages to have that many sessions
				Call( JetSetSystemParameter( &instance, 0, JET_paramCacheSizeMin, 4096, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramMaxSessions, 128, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramMaxTemporaryTables, 10000, NULL ) );

				// Set user overrides.
				Call( ErrEDBUTLUserSystemParameters( &instance, &opts ) );

				Call( JetInit( &instance ) );
				CallJ( JetBeginSession( instance, &sesid, szUser, szPassword ), Cleanup );
				
				JET_GRBIT grbit;
				grbit = 0;
				if( FUTILOPTSDontRepair( opts.fUTILOPTSFlags ) )
					{
					grbit |= JET_bitDBUtilOptionDontRepair;
					}
				if( FUTILOPTSDumpStats( opts.fUTILOPTSFlags ) )
					{
					grbit |= JET_bitDBUtilOptionStats;
					}

				CallJ( ErrEDBUTLRepair(
					sesid,
					opts.szSourceDB,
					opts.szSourceSLV,
					opts.szBackup,
					(char *)(opts.pv),
					opts.szIntegPrefix,
					PrintStatus,
					grbit ), Cleanup );

				errRepaired = err;
				
				Call( ErrEDBUTLCleanup( instance, sesid, JET_errSuccess ) );
				
				printf( "Note:%c", cNewLine );
				printf( "  It is recommended that you immediately perform a full backup%c", cNewLine );
				printf( "  of this database. If you restore a backup made before the%c", cNewLine );
				printf( "  repair, the database will be rolled back to the state%c", cNewLine );
				printf( "  it was in at the time of that backup.%c%c", cNewLine, cNewLine );
				}			
			break;
			
		case modeChecksum:
				{			
				wchar_t szFile[_MAX_PATH+1];	//	UNDONE: don't know if +1 is really needed, but add it to be safe

				Call( ErrEDBUTLCheckDBSLVNames( &opts, szDefaultTempDB ) );

				if ( !MultiByteToWideChar(
						CP_ACP,
						0,
						opts.szSourceDB,
						-1,
						szFile,
						_MAX_PATH ) )
					{
					Call( JET_errUnicodeTranslationFail );
					}
					
				printf( "Initiating CHECKSUM mode...%c%c", cNewLine, cNewLine );
				fWhitespaceOnErr = fTrue;

				if ( FChecksumFile( szFile, FUTILOPTS8KPage( opts.fUTILOPTSFlags ), NULL ) )
					{
					err = JET_errSuccess;
					}
				else
					{
					fUnknownError = fTrue;
					goto HandleError;
					}
				}			
			break;

		case modeSLVMove:
				{
				printf( "Initiating SLV Move mode...%c", cNewLine );
				printf( "        Database: %s%c", opts.szSourceDB, cNewLine );

				fWhitespaceOnErr = fTrue;

				Call( JetSetSystemParameter( &instance, 0, JET_paramRecovery, 0, "on" ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramEnableOnlineDefrag, 0, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramEnableIndexChecking, 0, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramDisableCallbacks, fTrue, NULL ) );
				//  create enough sessions for multi-threadeding
				//  need to have plenty of pages to have that many sessions
				Call( JetSetSystemParameter( &instance, 0, JET_paramCacheSizeMin, 16 * 4, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramMaxSessions, 16, NULL ) );
				ULONG_PTR cbfCacheMax;
				Call( JetGetSystemParameter( instance, 0, JET_paramCacheSizeMax, &cbfCacheMax, NULL, 0 ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramStopFlushThreshold, 30 * cbfCacheMax / 100, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramStartFlushThreshold, 20 * cbfCacheMax / 100, NULL ) );

				// Set user overrides.
				Call( ErrEDBUTLUserSystemParameters( &instance, &opts ) );

				dbutil.szDatabase = opts.szSourceDB;
 
				Call( JetDBUtilities( &dbutil ) );
				printf( "%c", cNewLine );		
				}		
			break;

		case modeScrub:
				{			

				Call( ErrEDBUTLCheckDBSLVNames( &opts, szDefaultScrubDB, szSourceStreamFileName ) );

				printf( "Initiating SECURE mode...%c", cNewLine );
				printf( "        Database: %s%c", opts.szSourceDB, cNewLine );
				if( opts.szSourceSLV )
					{
					printf( "  Streaming File: %s%c", opts.szSourceSLV, cNewLine );
					}

				fWhitespaceOnErr = fTrue;

				Call( JetSetSystemParameter( &instance, 0, JET_paramRecovery, 0, "scrub_off" ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramEnableOnlineDefrag, 0, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramEnableIndexChecking, 0, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramDisableCallbacks, fTrue, NULL ) );
				//  create enough sessions for multi-threadeding
				//  need to have plenty of pages to have that many sessions
				Call( JetSetSystemParameter( &instance, 0, JET_paramCacheSizeMin, 16 * 4, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramMaxSessions, 16, NULL ) );
				ULONG_PTR cbfCacheMax;
				Call( JetGetSystemParameter( instance, 0, JET_paramCacheSizeMax, &cbfCacheMax, NULL, 0 ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramStopFlushThreshold, 30 * cbfCacheMax / 100, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramStartFlushThreshold, 20 * cbfCacheMax / 100, NULL ) );

				// Set user overrides.
				Call( ErrEDBUTLUserSystemParameters( &instance, &opts ) );

				Call( JetInit( &instance ) );
				CallJ( JetBeginSession( instance, &sesid, szUser, szPassword ), Cleanup );
									
				CallJ( ErrEDBUTLScrub(
					sesid,
					opts.szSourceDB,
					opts.szSourceSLV,
					PrintStatus,
					0 ), Cleanup );
				
				Call( ErrEDBUTLCleanup( instance, sesid, JET_errSuccess ) );

				printf( "Warning:%c", cNewLine );
				printf( "  You MUST delete the logfiles for this database%c%c", cNewLine, cNewLine );
				
				printf( "Note:%c", cNewLine );
				printf( "  It is recommended that you immediately perform a full backup%c", cNewLine );
				printf( "  of this database. If you restore a backup made before the%c", cNewLine );
				printf( "  repair, the database will be rolled back to the state%c", cNewLine );
				printf( "  it was in at the time of that backup.%c%c", cNewLine, cNewLine );				
				}			
			break;

#ifdef RECORD_FORMAT_UPGRADE
		case modeUpgradeRecordFormat:
				{
				Call( ErrEDBUTLCheckDBSLVNames( &opts, szDefaultScrubDB ) );

				printf( "Initiating RECORD UPGRADE mode...%c", cNewLine );
				printf( "        Database: %s%c", opts.szSourceDB, cNewLine );
				printf( "     Dirty Level: %d%c", opts.lDirtyLevel, cNewLine );

				fWhitespaceOnErr = fTrue;

				Call( JetSetSystemParameter( &instance, 0, JET_paramRecovery, 0, "record_upgrade_off" ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramEnableOnlineDefrag, 0, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramEnableIndexChecking, 0, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramDisableCallbacks, fTrue, NULL ) );
				//  create enough sessions for multi-threadeding
				//  need to have plenty of pages to have that many sessions
				Call( JetSetSystemParameter( &instance, 0, JET_paramCacheSizeMin, 16 * 4, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramMaxSessions, 16, NULL ) );
				ULONG_PTR cbfCacheMax;
				Call( JetGetSystemParameter( instance, 0, JET_paramCacheSizeMax, &cbfCacheMax, NULL, 0 ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramStopFlushThreshold, 30 * cbfCacheMax / 100, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramStartFlushThreshold, 20 * cbfCacheMax / 100, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramRecordUpgradeDirtyLevel, opts.lDirtyLevel, NULL ) );

				// Set user overrides.
				
				Call( ErrEDBUTLUserSystemParameters( &instance, &opts ) );

				Call( JetInit( &instance ) );
				CallJ( JetBeginSession( instance, &sesid, szUser, szPassword ), Cleanup );

				CallJ( ErrEDBUTLUpgradeRecordFormat(
					sesid,
					opts.szSourceDB,
					PrintStatus,
					0 ), Cleanup );
				
				Call( ErrEDBUTLCleanup( instance, sesid, JET_errSuccess ) );
				}			
			break;
#endif	//	RECORD_FORMAT_UPGRADE

		case modeIntegrity:
				{		
				Call( ErrEDBUTLCheckDBSLVNames( &opts, szDefaultIntegDB, szSourceStreamFileName ) );

				printf( "Initiating INTEGRITY mode...%c", cNewLine );
				printf( "        Database: %s%c", opts.szSourceDB, cNewLine );
				if( opts.szSourceSLV )
					{
					printf( "  Streaming File: %s%c", opts.szSourceSLV, cNewLine );
					}
				printf( "  Temp. Database: %s%c", opts.szTempDB, cNewLine );
				if( NULL != opts.pv )
					{
					printf( "           Table: %s%c", (char *)opts.pv, cNewLine );
					}

				fWhitespaceOnErr = fTrue;

				Call( JetSetSystemParameter( &instance, 0, JET_paramRecovery, 0, "off" ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramTempPath, 0, opts.szTempDB ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramEnableOnlineDefrag, 0, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramEnableTempTableVersioning, fFalse, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramEnableIndexChecking, 0, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramMaxOpenTables, 10000, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramPreferredMaxOpenTables, 10000, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramDisableCallbacks, fTrue, NULL ) );
				//  create plenty of sessions for multi-threaded integrity/repair
				//  need to have plenty of pages to have that many sessions
				Call( JetSetSystemParameter( &instance, 0, JET_paramCacheSizeMin, 128 * 4, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramMaxSessions, 128, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramMaxTemporaryTables, 10000, NULL ) );

				// Set user overrides.
				Call( ErrEDBUTLUserSystemParameters( &instance, &opts ) );

				Call( JetInit( &instance ) );
				CallJ( JetBeginSession( instance, &sesid, szUser, szPassword ), Cleanup );
				
				JET_GRBIT grbit;
				grbit = JET_bitDBUtilOptionDontRepair;
				if( FUTILOPTSDumpStats( opts.fUTILOPTSFlags ) )
					{
					grbit |= JET_bitDBUtilOptionStats;
					}	
				if( FUTILOPTSDontBuildIndexes( opts.fUTILOPTSFlags ) )
					{
					grbit |= JET_bitDBUtilOptionDontBuildIndexes;
					}	
				assert( grbit & JET_bitDBUtilOptionDontRepair );

				CallJ( ErrEDBUTLRepair(
					sesid,
					opts.szSourceDB,
					opts.szSourceSLV,
					opts.szBackup,
					(char *)(opts.pv),
					opts.szIntegPrefix,
					PrintStatus,
					grbit ), Cleanup );
				
				Call( ErrEDBUTLCleanup( instance, sesid, JET_errSuccess ) );
				}
			break;

		case modeUpgrade:		
			Call( ErrEDBUTLCheckDBSLVNames( &opts, szDefaultUpgradeDB ) );

			Call( JetSetSystemParameter( &instance, 0, JET_paramRecovery, 0, "off" ) );
			Call( JetSetSystemParameter( &instance, 0, JET_paramEnableOnlineDefrag, 0, NULL ) );
			Call( JetSetSystemParameter( &instance, 0, JET_paramDbExtensionSize, 256, NULL ) );
			Call( JetSetSystemParameter( &instance, 0, JET_paramEnableTempTableVersioning, fFalse, NULL ) );
			Call( JetSetSystemParameter( &instance, 0, JET_paramMaxOpenTables, 10000, NULL ) );
			Call( JetSetSystemParameter( &instance, 0, JET_paramPreferredMaxOpenTables, 10000, NULL ) );

			fWhitespaceOnErr = fTrue;

			// Set user overrides.
			Call( ErrEDBUTLUserSystemParameters( &instance, &opts ) );

			Call( JetInit( &instance ) );
			CallJ( JetBeginSession( instance, &sesid, szUser, szPassword ), Cleanup );

			// Detach temporary database and delete file if present (ignore errors).
			JetDetachDatabase( sesid, opts.szTempDB );
			DeleteFile( opts.szTempDB );

			assert( opts.pv == &convert );
			CallJ( JetCompact(
				sesid,
				opts.szSourceDB,
				FUTILOPTSInPlaceUpgrade( opts.fUTILOPTSFlags ) ? opts.szSourceDB : opts.szTempDB,
				PrintStatus,
				&convert,
				FUTILOPTSDefragInfo( opts.fUTILOPTSFlags ) ? JET_bitCompactStats : 0 ), Cleanup );

			Call( ErrEDBUTLCleanup( instance, sesid, JET_errSuccess ) );

			Call( ErrEDBUTLBackupAndInstateDB( sesid, &opts ) );
			
			break;

#ifdef ESENT
#else
		case modeSplash:
			EDBUTLSplash();
			break;
#endif			

		case modeHardRecovery:
			{
			char * 	szTargetInstance = opts.szRestore;
			char 	szFullRestorePath[_MAX_PATH + 1];

			// if missing, get current directory
			if ( _fullpath( szFullRestorePath, opts.szSourceDB?opts.szSourceDB:".", _MAX_PATH ) == NULL )
				{
				Call ( JET_errInvalidPath );
				}

			if ( FUTILOPTSServerSim ( opts.fUTILOPTSFlags ) )
				{
#ifdef RESTORE_SERVER_SIMULATION
				// if no definition file was provided, call with NULL and
				// it will print a sample of such a file
				Call ( ErrDBUTLServerSim( opts.szSourceDB?szFullRestorePath:NULL ) );
#else // RESTORE_SERVER_SIMULATION
				// FUTILOPTSServerSim() set only with RESTORE_SERVER_SIMULATION 
				// defined, check command line parsing above
				assert ( fFalse );
#endif // RESTORE_SERVER_SIMULATION
				}
			else if ( FUTILOPTSDumpRestoreEnv( opts.fUTILOPTSFlags ) )
				{
				Call ( ErrDBUTLDumpRestoreEnv(szFullRestorePath) );
				}
			else
				{
				Call ( ErrDBUTLRestoreComplete( szFullRestorePath, szTargetInstance, FUTILOPTSPreserveTempDB( opts.fUTILOPTSFlags ) ) );
				}
			}
			break;				

		case modeDump:
		default:
			{
			char	szBaseName[4];

			// Make the most innocuous operation the fall-through (to cover
			// ourselves in the unlikely event we messed up the modes).
			assert( opts.mode == modeDump );
			assert( opts.pv == &dbutil );

			if ( opts.mode == modeDump )
				{
				Call( JetSetSystemParameter( &instance, 0, JET_paramEnableIndexChecking, 0, NULL ) );
				Call( JetSetSystemParameter( &instance, 0, JET_paramDisableCallbacks, fTrue, NULL ) );

				if ( opts.szSourceDB == NULL )
					{
					printf( szUsageErr1, "database/filename" );			// Missing spec.
					printf( "%c%c", cNewLine, cNewLine );
					Call( JET_errInvalidParameter );
					}

				printf( "Initiating FILE DUMP mode...%c", cNewLine );

				switch( dbutil.op )	//lint !e644
					{
					case opDBUTILDumpLogfile:
						opts.szBase = szBaseName;
						EDBUTLGetBaseName( opts.szSourceDB, opts.szBase );
						break;
					case opDBUTILDumpLogfileTrackNode:
						printf( "      Log file: %s%c", opts.szSourceDB, cNewLine );
						printf( "          Node: %d:%d:%d%c", dbutil.dbid, dbutil.pgno, dbutil.iline, cNewLine );
						printf( "         Lgpos: %06X,%04X,%04X%c", dbutil.lGeneration, dbutil.isec, dbutil.ib, cNewLine );
						break;
					case opDBUTILDumpCheckpoint:
						printf( "      Checkpoint file: %s%c", opts.szSourceDB, cNewLine );
						break;
					case opDBUTILSetHeaderState:
						printf( "      Database %s will be forced to a consistent state.%c",
							opts.szSourceDB, cNewLine );
						break;

					case opDBUTILSLVMove:
					case opDBUTILDumpPage:
						Call( ErrEDBUTLCheckDBSLVNames( &opts, szDefaultTempDB ) );
						printf( "      Database: %s%c", opts.szSourceDB, cNewLine );
						printf( "          Page: %d%c", dbutil.pgno, cNewLine );
						break;
					case opDBUTILDumpNode:
						Call( ErrEDBUTLCheckDBSLVNames( &opts, szDefaultTempDB ) );
						printf( "      Database: %s%c", opts.szSourceDB, cNewLine );
						printf( "          Node: %d:%d%c", dbutil.pgno, dbutil.iline, cNewLine );
						break;
					case opDBUTILDumpHeader:
					case opDBUTILDumpMetaData:
					case opDBUTILDumpSpace:
					case opDBUTILDumpData:
					case opDBUTILDumpExchangeSLVInfo:
						Call( ErrEDBUTLCheckDBSLVNames( &opts, szDefaultTempDB ) );
						printf( "      Database: %s%c", opts.szSourceDB, cNewLine );
						if( opts.szSourceSLV )
							{
							printf( " Streaming File: %s%c", opts.szSourceSLV, cNewLine );
							}

						break;					
					default:
						assert( 0 );
					}
				}

			printf( "%c", cNewLine );

			Call( JetSetSystemParameter( &instance, 0, JET_paramRecovery, 0, "off" ) );
			Call( JetSetSystemParameter( &instance, 0, JET_paramEnableOnlineDefrag, 0, NULL ) );

			//	set temp table size to be 0 so that no temp db will be created
			
			Call( JetSetSystemParameter( &instance, 0, JET_paramMaxTemporaryTables, 0, NULL ) );

			//	set user overrides.
			
			Call( ErrEDBUTLUserSystemParameters( &instance, &opts ) );

			dbutil.szDatabase = opts.szSourceDB;
			dbutil.szSLV = opts.szSourceSLV;
			Call( JetDBUtilities( &dbutil ) );
			printf( "%c", cNewLine );
			break;
			}
		}
		
	EDBUTLGetTime( timer, &iSec, &iMSec );

	if( JET_errSuccess != errRepaired ) //db is repaired
		{
		ULONG_PTR	ulWarn		= errRepaired;
		CHAR		szWarn[512];
		szWarn[0] = 0;
		(void)JetGetSystemParameter(
			instance,
			JET_sesidNil,
			JET_paramErrorToString,
			(ULONG_PTR *)&ulWarn,
			szWarn,
			sizeof( szWarn ) );
		if ( fWhitespaceOnErr )
			printf( "%c%c", cNewLine, cNewLine );
		printf( szOperWarn, errRepaired, szWarn, iSec, iMSec );
		}	
	else // JET_errSuccess == err 
		{ 
		printf( szOperSuccess, iSec, iMSec );
		}
	printf( "%c%c", cNewLine, cNewLine );
	return 0;
	

Cleanup:
	ErrEDBUTLCleanup( instance, sesid, err );

HandleError:
	EDBUTLGetTime( timer, &iSec, &iMSec );

	if ( fUnknownError )
		{
		if ( fWhitespaceOnErr )
			printf( "%c%c", cNewLine, cNewLine );
		printf( szOperFailUnknownError, iSec, iMSec );
		}
	else
		{
		assert( err < 0 );
		ULONG_PTR	ulErr		= err;
		CHAR		szError[512];

		szError[0] = 0;
		(void)JetGetSystemParameter(
			instance,
			JET_sesidNil,
			JET_paramErrorToString,
			(ULONG_PTR *)&ulErr,
			szError,
			sizeof( szError ) );
		if ( fWhitespaceOnErr )
		
			printf( "%c%c", cNewLine, cNewLine );
		printf( szOperFailWithError, err, szError, iSec, iMSec );
		}	

	printf( "%c%c", cNewLine, cNewLine );
	return err;


Usage:
	SetCurArgID( 0 );
	assert( GetCurArgID() == 0 );
	EDBUTLHelp( _strupr( GetCurArg() ) );
	return -1;
	}

