// Include header files
#include "ifx.h"
#include "sdrc.h"
#include "resource.h"
#include "odbcconfig.h"
////////////////////// string defines ////////////////////////////
//////////////////// installation declarations ///////////////////
// ----- DLL function prototypes -----
// your DLL function prototypes
prototype BOOL Databases.CanConnectToDatabase();
// ---- script function prototypes -----
// your script function prototypes
prototype BOOL DSNIsExists(int, string);
prototype BOOL ODBCDriverIsExists(int);
prototype RegisterVisualFoxpro5Runtime();
prototype BOOL ConfigDSN(SHORT, string, string);
prototype BOOL FixInterbaseDSN(int, string);
// your global variables
//////////////////////////////////////////////////////////////////////////////
//
// 函数名: DSNIsExist
//
// 功 能: 检查注册表中是否已存在指定的ODBC DSN数据源名称
//
// 参 数: int DSNType; DSN类型
// = 0; 系统DSN
// = 1; 用户DSN
// string DSNName; DSN数据源名称
///////////////////////////////////////////////////////////////////////////////
function BOOL DSNIsExists(DSNType, DSNName)
STRING szKey;
begin
if (DSNType = 0) then
RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE); //系统DSN
else
RegDBSetDefaultRoot(HKEY_CURRENT_USER); //用户DSN
endif;
szKey = "SOFTWARE\\ODBC\\ODBC.INI" ^ DSNName;
return (RegDBKeyExist(szKey) = 1);
end;
///////////////////////////////////////////////////////////////////////////////
//
// 函数名: ODBCDriverIsExists
//
// 功 能: 检查指定数据库系统的ODBC Driver是否已安装
//
// 参 数: int DriverID; 数据库系统类型
//
///////////////////////////////////////////////////////////////////////////////
function BOOL ODBCDriverIsExists(DriverID)
NUMBER nvSize, nvType;
STRING szKey, szName, szValue;
BOOL bzResult;
begin
nvSize = -1;
nvType = REGDB_STRING;
bzResult = FALSE;
RegDBSetDefaultRoot ( HKEY_LOCAL_MACHINE );
szKey = "SOFTWARE\\ODBC\\ODBCINST.INI\\ODBC Drivers";
if (RegDBKeyExist(szKey) = 1) then
switch (DriverID)
case 0: //MS Access
szName = "Microsoft Access Driver (*.mdb)";
case 3: //Borland InterBase
szName = "INTERSOLV InterBase ODBC Driver (*.gdb)";
case 5: //MS SQL Server
szName = "SQL Server";
case 6: //Oracle 8.0.5
szName = "Oracle ODBC Driver";
endswitch;
if RegDBGetKeyValueEx (szKey, szName, nvType, szValue, nvSize) = 0 then
if (szValue = "Installed") then
bzResult = TRUE;
endif;
endif;
endif;
return bzResult;
end;
///////////////////////////////////////////////////////////////////////////////
//
// 函数名: RegisterVisualFoxpro5Runtime
//
// 功 能: 注册Visual Foxpro 5.0运行类库
//
///////////////////////////////////////////////////////////////////////////////
function RegisterVisualFoxpro5Runtime()
NUMBER nResult, nSize;
STRING szKey;
begin
RegDBSetDefaultRoot ( HKEY_LOCAL_MACHINE );
szKey = "SOFTWARE\\CLASSES\\VisualFoxProRuntime.5";
RegDBCreateKeyEx ( szKey, "" );
RegDBSetKeyValueEx ( szKey, "", REGDB_STRING, "Microsoft Visual FoxPro 5.0 Runtime", nSize );
szKey = szKey+ "\\shell\\open\\command";
RegDBCreateKeyEx ( szKey, "" );
RegDBSetKeyValueEx ( szKey, "", REGDB_STRING, WINSYSDIR ^ "\\VFP500.DLL %1", nSize );
szKey = "SOFTWARE\\CLASSES\\VisualFoxProRuntime.5\\Resources";
RegDBCreateKeyEx ( szKey, "" );
RegDBSetKeyValueEx ( szKey, "", REGDB_STRING, WINSYSDIR ^ "\\VFP5CHS.DLL", nSize );
szKey = "SOFTWARE\\CLASSES\\NoDualInterface";
RegDBCreateKeyEx ( szKey + "\\{2B32FBC2-A8F1-11CF-93EE-00AA00C08FDF}", "" );
RegDBCreateKeyEx ( szKey + "\\{D4A97620-8E8F-11CF-93CD-00AA00C08FDF}", "" );
RegDBCreateKeyEx ( szKey + "\\{EAB22AC3-30C1-11CF-A7EB-0000C05BAE0B}", "" );
nResult = CreateRegistrySet( "" );
return nResult;
end;
///////////////////////////////////////////////////////////////////////////////
//
// 函数名: ConfigDSN
//
// 功 能: 配置DSN数据源
//
///////////////////////////////////////////////////////////////////////////////
function BOOL ConfigDSN(nDSNType, szDriver, szArg)
BOOL bzResult;
LONG dwErrCode;
STRING szErrMsg[MAX_PATH + 1];
SHORT wErrMsgLen;
begin
// 调用ODBCCP32中的SQLConfigDataSource添加一个DSN
// 如果返回FALSE,表示添加失败,这时可以调用
// SQLInstallerError来得到失败的原因
bzResult = SQLConfigDataSource(NULL, nDSNType, szDriver, szArg);
if !bzResult then
wErrMsgLen = MAX_PATH;
SQLInstallerError(1, &dwErrCode, szErrMsg, wErrMsgLen, &wErrMsgLen);
MessageBox("配置DSN数据源失败:\n"
+ szErrMsg, SEVERE);
endif;
return bzResult;
end;
///////////////////////////////////////////////////////////////////////////////
//
// 函数名: FixInterbaseDSN
//
// 功 能: 修正Interbase DSN 的LockTimeout项
//
///////////////////////////////////////////////////////////////////////////////
function BOOL FixInterbaseDSN(DSNType, DSNName)
string szKey;
begin
if !DSNIsExists(DSNType, DSNName) then
return FALSE;
endif;
RegDBSetDefaultRoot ( HKEY_LOCAL_MACHINE );
szKey = "SOFTWARE\\ODBC\\ODBC.INI" ^ DSNName;
if RegDBSetKeyValueEx (szKey, "LockTimeOut", REGDB_STRING, "-1", -1) = 0 then
return TRUE;
else
return FALSE;
endif;
end;
/////////////////////////////////////////////////////////////////////////////
//
// FUNCTION: OnFirstUIBefore
//
// EVENT: FirstUIBefore event is sent when installation is run for the first
// time on given machine. In the handler installation usually displays
// UI allowing end user to specify installation parameters. After this
// function returns, ComponentTransferData is called to perform file
// transfer.
//
///////////////////////////////////////////////////////////////////////////////
function OnFirstUIBefore()
number nResult,nSetupType,nRadioResult;
string szTitle, szMsg;
string szLicenseFile, szQuestion;
string szTargetPath;
string szDir;
number nvSize;
BOOL bzDone, bzPassed;
number nvCheck1, nvCheck2, hwndDlg, nvODBCAction;
string szText1, szText2, szType, szDatabase, szDBPath;
string szMsg1, szMsg2;
BOOL bvOpt1, bvOpt2;
string szServer, szDBName, szUserID, szPassword;
string szDLLName, szDefPath, svResultPath;
string szField1, svEdit1, szField2, svEdit2;
STRING svArg[1024];
STRING svDSNName, svServer, svDatabase;
NUMBER nSplitPos;
begin
// TO DO: if you want to enable background, window title, and caption bar title
// SetTitle( @TITLE_MAIN, 24, WHITE );
// SetTitle( @TITLE_CAPTIONBAR, 0, BACKGROUNDCAPTION );
// Enable( FULLWINDOWMODE );
// Enable( BACKGROUND );
// SetColor(BACKGROUND,RGB (0, 128, 128));
nSetupType = TYPICAL;
TARGETDIR = PROGRAMFILES ^ "Ant2000";
szDir = TARGETDIR;
szType = 'Program';
szDatabase = "Interbase";
szDefPath = "";
Dlg_SdWelcome:
szTitle = "";
szMsg = "";
nResult = SdWelcome( szTitle, szMsg );
Dlg_SdLicense:
szLicenseFile = SUPPORTDIR ^ "license.txt";
szTitle = "";
szMsg = "";
szQuestion = "";
nResult = SdLicense( szTitle, szMsg, szQuestion, szLicenseFile );
if (nResult = BACK) goto Dlg_SdWelcome;
Dlg_AskInstallType:
szMsg = "请选择安装类型:";
if (szType = "Program") then
nvCheck1 = TRUE;
nvCheck2 = FALSE;
else
nvCheck1 = FALSE;
nvCheck2 = TRUE;
endif;
szText1 = "安装程序";
szText2 = "安装数据库";
nResult = AskOptions(EXCLUSIVE, szMsg, szText1, nvCheck1,
szText2, nvCheck2);
if (nResult = BACK) goto Dlg_SdLicense;
if (nvCheck1) then
szType = "Program";
if (DSNIsExists(0, "ant2000") || DSNIsExists(0, "ant2000")) then
szDLLName = SUPPORTDIR ^ "databases.dll";
if (UseDLL(szDLLName) = 0) then
bzPassed = CanConnectToDatabase();
endif;
UnUseDLL(szDLLName);
if bzPassed goto Dlg_SdAskDestPath;
endif;
goto Dlg_AskConnectType;
else
szType = "Database";
endif;
Dlg_AskDatabaseType:
szMsg = '请选择要安装的数据库类型:';
if (szDatabase = "Interbase") then
nvCheck1 = TRUE;
nvCheck2 = FALSE;
else
nvCheck1 = FALSE;
nvCheck2 = TRUE;
endif;
szText1 = "Borland Interbase";
szText2 = "Microsoft SQL Server";
nResult = AskOptions(EXCLUSIVE, szMsg, szText1, nvCheck1,
szText2, nvCheck2);
if (nResult = BACK) goto Dlg_AskInstallType;
if (nvCheck1) then
szDatabase = 'Interbase';
goto Dlg_AskDBPath;
else
szDatabase = 'SQL Server';
goto Dlg_ConnectToSQLServer;
endif;
Dlg_AskDBPath:
szTitle = "请选择数据库的安装路径";
szMsg = "";
szDBPath = szDir;
nResult = AskDestPath(szTitle, szMsg, szDBPath, 0);
if (nResult = BACK) goto Dlg_AskDatabaseType;
bzPassed = TRUE;
SdShowMsg ( "安装程序正在安装Interbase数据库,请稍等..." , TRUE );
if (XCopyFile(SUPPORTDIR^'Ant2000.gdb', szDBPath, COMP_NORMAL) < 0) then
MessageBox ("Interbase数据库安装失败。", SEVERE);
bzPassed = FALSE;
endif;
Delay(1);
SdShowMsg ( "安装程序正在安装Interbase数据库,请稍等..." , FALSE );
if !bzPassed goto Dlg_AskDBPath;
goto Dlg_DBFinished;
Dlg_ConnectToSQLServer:
EzDefineDialog( "SQLServerDlg", "", "", DLG_SQLSERVER);
bzDone = FALSE;
while (!bzDone)
nResult = WaitOnDialog("SQLServerDlg");
switch (nResult)
case DLG_INIT:
hwndDlg = CmdGetHwndDlg("SQLServerDlg");
//_WinSubEnableControl(hwndDlg, SD_PBUT_BACK, 0);
szDBName = "Ant2000";
CtrlSetText("SQLServerDlg", IDC_EDIT_DBNAME, szDBName);
CtrlSetText("SQLServerDlg", SD_HEADER_TITLE, "连接SQL Server服务器");
CtrlSetText("SQLServerDlg", SD_HEADER_MSG, "请输入登录SQL Server的连接信息");
_WinSubFocusControl(hwndDlg, IDC_EDIT_SERVER);
//CtrlSetState("INSTALLTYPEDLG", SD_RADIO_PROGRAM, BUTTON_CHECKED);
case SD_PBUT_CONTINUE:
//nRadioResult = CtrlGetState("SQLServerDlg", SD_RADIO_PROGRAM);
CtrlGetText("SQLServerDlg", IDC_EDIT_SERVER, szServer);
CtrlGetText("SQLServerDlg", IDC_EDIT_DBNAME, szDBName);
CtrlGetText("SQLServerDlg", IDC_EDIT_USERNAME, szUserID);
CtrlGetText("SQLServerDlg", IDC_EDIT_PASSWORD, szPassword);
if (StrLength(szServer) = 0) then
MessageBox("必须输入SQL Server服务器名!", WARNING);
_WinSubFocusControl(hwndDlg, IDC_EDIT_SERVER);
elseif (StrLength(szDBName) = 0) then
MessageBox("必须输入要安装的数据库名!", WARNING);
_WinSubFocusControl(hwndDlg, IDC_EDIT_DBNAME);
elseif (StrLength(szUserID) = 0) then
MessageBox("必须输入登录SQL Server服务器的用户名!", WARNING);
_WinSubFocusControl(hwndDlg, IDC_EDIT_USERNAME);
else
bzDone = TRUE;
endif;
case SD_PBUT_BACK:
bzDone = TRUE;
case CANCEL:
Do(EXIT);
case DLG_ERR:
MessageBox( "不能显示连接SQL Server 数据库服务器对话框,\n对话框内部错误!", SEVERE);
bzDone = TRUE;
endswitch;
endwhile;
EndDialog("SQLServerDlg");
ReleaseDialog("SQLServerDlg");
switch (nResult)
case DLG_ERR:
abort;
case SD_PBUT_BACK:
goto Dlg_AskDatabaseType;
endswitch;
Dlg_DBFinished:
szTitle = "安装完成";
szMsg1 = "数据库已经安装成功";
szMsg2 = "请按'完成'按钮继续安装程序文件";
SdFinish (szTitle, szMsg1, szMsg2, "", "", bvOpt1, bvOpt2);
szType = "Program";
goto Dlg_AskInstallType;
Dlg_AskConnectType:
szMsg = "安装软件前必须先连接到数据库,请选择要连接的数据库类型\n"
+ "然后按'下一步'按钮继续安装";
if (szDatabase = "Interbase") then
nvCheck1 = TRUE;
nvCheck2 = FALSE;
else
nvCheck1 = FALSE;
nvCheck2 = TRUE;
endif;
szText1 = "Borland Interbase";
szText2 = "Microsoft SQL Server";
nResult = AskOptions(EXCLUSIVE, szMsg, szText1, nvCheck1,
szText2, nvCheck2);
if (nResult = BACK) goto Dlg_AskInstallType;
if (nvCheck1) then
szDatabase = 'Interbase';
if !ODBCDriverIsExists(3) then
MessageBox("当前机器尚未安装Interbase的ODBC驱动程序,\n"
+ "请先安装驱动程序然后再重新运行本安装程序。", SEVERE);
abort;
endif;
goto Dlg_AskPath;
else
szDatabase = 'SQL Server';
if !ODBCDriverIsExists(5) then
MessageBox("当前机器尚未安装SQL Server的ODBC驱动程序,\n"
+ "请先安装驱动程序然后再重新运行本安装程序。", SEVERE);
abort;
endif;
goto Dlg_ConnectToSQLServer;
endif;
Dlg_AskPath:
szMsg = "请输入或选择Interbase数据库所在机器和目录,\n"
+ "然后按'下一步'按钮继续安装。";
if szDefPath = "" then
szDefPath = TARGETDIR;
endif;
szTitle = "选择Interbase数据库";
SetDialogTitle (DLG_ASK_PATH, szTitle);
nResult = AskPath (szMsg, szDefPath, svResultPath);
if nResult = BACK goto Dlg_AskConnectType;
if svResultPath = "" then
MessageBox("没有输入目录路径,\n"
+ "请输入目录路径后按'下一步'按钮继续安装。", WARNING);
goto Dlg_AskPath;
endif;
szDefPath = svResultPath;
if DSNIsExists(0, "ant2000") then
nvODBCAction = ODBC_CONFIG_SYS_DSN;
else
nvODBCAction = ODBC_ADD_SYS_DSN;
endif;
svDSNName = "DSN=ant2000";
svDatabase = "DATABASE=" + svResultPath ^ "ant2000.gdb";
svArg = svDSNName + ' ' + svDatabase;
nSplitPos = StrLength(svDSNName);
svArg[nSplitPos] = '\0';
nSplitPos += StrLength(svDatabase) + 1;
svArg[nSplitPos] = '\0';
if !ConfigDSN(nvODBCAction, "INTERSOLV InterBase ODBC Driver (*.gdb)", svArg)
goto Dlg_AskPath;
if !FixInterbaseDSN(0, "ant2000") then
MessageBox("异常错误:无法正确的连接数据库。\n"
+ "请按'下一步'按钮再重试一次。", SEVERE);
goto Dlg_AskPath;
endif;
szDLLName = SUPPORTDIR ^ "databases.dll";
if (UseDLL(szDLLName) = 0) then
bzPassed = CanConnectToDatabase();
endif;
UnUseDLL(szDLLName);
if !bzPassed then
MessageBox("输入的目录路径不正确,\n"
+ "请输入正确的目录路径后按'下一步'按钮继续安装。", SEVERE);
goto Dlg_AskPath;
endif;
goto Dlg_SdAskDestPath;
Dlg_AskServerName:
szTitle = "选择SQL Server服务器";
szMsg = "请输入SQL Server服务器的所在机器名和数据库名,\n"
+ "然后按'下一步'按钮继续安装。";
szField1 = "服务器名:";
szField2 = "数据库名:";
nResult = SdShowDlgEdit2 (szTitle, szMsg, szField1, szField2,
svEdit1, svEdit2);
if nResult = BACK goto Dlg_AskConnectType;
if svEdit1 = "" || svEdit2 = "" then
MessageBox("没有输入服务器名或数据库名,\n"
+ "请输入机器名后按'下一步'按钮继续安装。", WARNING);
goto Dlg_AskServerName;
endif;
if DSNIsExists(0, "ant2000") then
nvODBCAction = ODBC_CONFIG_SYS_DSN;
else
nvODBCAction = ODBC_ADD_SYS_DSN;
endif;
svDSNName = "DSN=ant2000";
svServer = "SERVER=" + svEdit1;
svDatabase = "DATABASE=" + svEdit2;
svArg = svDSNName + ' ' + svServer + ' ' + svDatabase;
nSplitPos = StrLength(svDSNName);
svArg[nSplitPos] = '\0';
nSplitPos += StrLength(svServer) + 1;
svArg[nSplitPos] = '\0';
nSplitPos += StrLength(svDatabase) + 1;
svArg[nSplitPos] = '\0';
if !ConfigDSN(nvODBCAction, "SQL Server", svArg)
goto Dlg_AskServerName;
szDLLName = SUPPORTDIR ^ "databases.dll";
if (UseDLL(szDLLName) = 0) then
bzPassed = CanConnectToDatabase();
endif;
UnUseDLL(szDLLName);
if !bzPassed then
MessageBox("输入的输入服务器名或数据库名不正确,\n"
+ "请输入正确的服务器名和数据库名后按'下一步'按钮继续安装。", SEVERE);
goto Dlg_AskServerName;
endif;
Dlg_SdAskDestPath:
szTitle = "";
szMsg = "";
nResult = SdAskDestPath( szTitle, szMsg, szDir, 0 );
TARGETDIR = szDir;
if (nResult = BACK) goto Dlg_AskInstallType;
/*Dlg_SetupType:
szTitle = "";
szMsg = "";
nResult = SetupType ( szTitle , szMsg , "" , nSetupType , 0 );
if (nResult = BACK) then
goto Dlg_SdAskDestPath;
else
nSetupType = nResult;
if (nSetupType != CUSTOM) then
szTargetPath = TARGETDIR;
nvSize = 0;
ComponentCompareSizeRequired(MEDIA,szTargetPath,nvSize);
if (nvSize != 0) then
MessageBox( szSdStr_NotEnoughSpace, WARNING );
goto Dlg_SetupType;
endif;
endif;
endif;*/
Dlg_ObjDialogs:
nResult = ShowObjWizardPages(nResult);
if (nResult = BACK) goto Dlg_SdAskDestPath;//Dlg_SetupType;
// setup default status
SetStatusWindow(0, "");
Enable(STATUSEX);
StatusUpdate(ON, 100);
return 0;
end;
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION: OnMoving
//
// EVENT: Moving event is sent when file transfer is started as a result of
// ComponentTransferData call, before any file transfer operations
// are performed.
//
///////////////////////////////////////////////////////////////////////////////
function OnMoving()
string szAppPath;
begin
// Set LOGO Compliance Application Path
// TO DO : if your application .exe is in a subfolder of TARGETDIR then add subfolder
szAppPath = TARGETDIR;
RegDBSetItem(REGDB_APPPATH, szAppPath);
RegDBSetItem(REGDB_APPPATH_DEFAULT, szAppPath);
end;
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION: OnFirstUIAfter
//
// EVENT: FirstUIAfter event is sent after file transfer, when installation
// is run for the first time on given machine. In this event handler
// installation usually displays UI that will inform end user that
// installation has been completed successfully.
//
///////////////////////////////////////////////////////////////////////////////
function OnFirstUIAfter()
STRING szTitle, szMsg1, szMsg2, szOption1, szOption2;
NUMBER bOpt1, bOpt2;
begin
Disable(STATUSEX);
ShowObjWizardPages(NEXT);
SdShowMsg ( "安装程序正在注册Visual Foxpro 5 运行库,请稍等...", TRUE );
RegisterVisualFoxpro5Runtime();
//Delay(1);
SdShowMsg ( "注册完成,请稍等...", FALSE );
bOpt1 = TRUE;
bOpt2 = FALSE;
szOption1 = "运行授权管理程序";
szMsg1 = SdLoadString(IFX_SDFINISH_MSG1);
szMsg2 = '安装的软件必须经过授权认证后才能正常使用\n'
+ "建议您现在就进行授权认证";
SdFinishEx(szTitle, szMsg1, szMsg2, szOption1, szOption2, bOpt1, bOpt2);
if bOpt1 then
if LaunchApp(TARGETDIR ^ "LicenceCheck.exe", "") < 0 then
MessageBox( "装载授权管理程序失败\n"
+ "请运行Ant2000程序组中的授权管理程序再试", SEVERE);
endif;
else
MessageBox( "您选择了不运行授权管理程序\n"
+ "以后您可以运行Ant2000程序组中的授权管理程序进行授权认证", SEVERE);
endif;
end;
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION: OnMaintUIBefore
//
// EVENT: MaintUIBefore event is sent when end user runs installation that
// has already been installed on the machine. Usually this happens
// through Add/Remove Programs applet. In the handler installation
// usually displays UI allowing end user to modify existing installation
// or uninstall application. After this function returns,
// ComponentTransferData is called to perform file transfer.
//
///////////////////////////////////////////////////////////////////////////////
function OnMaintUIBefore()
NUMBER nResult, nType;
STRING szTitle, szMsg, svDir, svResult, szCaption;
begin
// TO DO: if you want to enable background, window title, and caption bar title
// SetTitle( @TITLE_MAIN, 24, WHITE );
// SetTitle( @TITLE_CAPTIONBAR, 0, BACKGROUNDCAPTION );
// SetColor(BACKGROUND,RGB (0, 128, 128));
// Enable( FULLWINDOWMODE );
// Enable( BACKGROUND );
Dlg_Start:
Disable(BACKBUTTON);
nType = SdWelcomeMaint(szTitle, szMsg, MODIFY);
Enable(BACKBUTTON);
if (nType = REMOVEALL) then
svResult = SdLoadString(IFX_MAINTUI_MSG);
szCaption = SdLoadString(IFX_ONMAINTUI_CAPTION);
nResult = SprintfBox(MB_OKCANCEL,szCaption,"%s",svResult);
if (nResult = IDCANCEL) goto Dlg_Start;
endif;
nResult = NEXT;
Dlg_SdComponentTree:
if (nType = MODIFY) then
szTitle = "";
szMsg = "";
nResult = SdComponentTree(szTitle, szMsg, TARGETDIR, "", 2);
if (nResult = BACK) goto Dlg_Start;
endif;
Dlg_ObjDialogs:
nResult = ShowObjWizardPages(nResult);
if ((nResult = BACK) && (nType != MODIFY)) goto Dlg_Start;
if ((nResult = BACK) && (nType = MODIFY)) goto Dlg_SdComponentTree;
switch(nType)
case REMOVEALL: ComponentRemoveAll();
case REPAIR: ComponentReinstall();
endswitch;
// setup default status
SetStatusWindow(0, "");
Enable(STATUSEX);
StatusUpdate(ON, 100);
end;
// --- include script file section ---