windows-nt/Source/XPSP1/NT/base/wow64/setup/boot/setup.c

2860 lines
80 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
#include <windows.h>
#if 0
#include <stdtypes.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <direct.h>
#include <dos.h>
#include <sys\types.h>
#include <sys\stat.h>
#include <ctype.h> /* isspace */
#include <io.h>
#include <limits.h> /* UINT_MAX */
#include <memory.h> /* _fmemcpy, _fmemccpy */
#include <lzexpand.h>
#include <shellapi.h> /* HKEY, HKEY_CLASSES_ROOT, ERROR_SUCCESS */
#include "setup.h"
#include "genthk.h" /* thunks for calls to get 32-bit version */
#include "driveex.h"
#include <stdtypes.h>
/* Messages for optional background task.
*/
#define IDM_ACME_STARTING 261
#define IDM_ACME_COMPLETE 262
#define IDM_ACME_FAILURE 263
#ifdef APPCOMP
#include <decomp.h>
#endif /* APPCOMP */
#include <fdi.h>
/* List file extension */
char szLstExt[] = "LST";
/* List file section names */
char szDefaultParamsSect[] = "Params";
char szDefaultFilesSect[] = "Files";
char szWin3xParamsSect[] = "Win3.x Params";
char szWin3xFilesSect[] = "Win3.x Files";
char szWin95ParamsSect[] = "Windows 95 Params";
char szWin95FilesSect[] = "Windows 95 Files";
char szNTAlphaParamsSect[] = "NT Alpha Params";
char szNTAlphaFilesSect[] = "NT Alpha Files";
char szNTMipsParamsSect[] = "NT Mips Params";
char szNTMipsFilesSect[] = "NT Mips Files";
char szNTPpcParamsSect[] = "NT PPC Params";
char szNTPpcFilesSect[] = "NT PPC Files";
char szNTIntelParamsSect[] = "NT Intel Params";
char szNTIntelFilesSect[] = "NT Intel Files";
char szNTVerIntelParamsSect[] = "NT3.51 Intel Params";
char szNTVerIntelFilesSect[] = "NT3.51 Intel Files";
char * szParamsSect = szNull;
char * szFilesSect = szNull;
typedef struct _PLATFORM_SPEC
{
BYTE minMajorVersion;
BYTE minMinorVersion;
char *szParamsSect;
char *szFilesSect;
} PLATFORM_SPEC, *PPLATFORM_SPEC;
PLATFORM_SPEC aIntelSpec[] =
{
{3, 51, szNTVerIntelParamsSect, szNTVerIntelFilesSect},
{0, 0, szNTIntelParamsSect, szNTIntelFilesSect},
{0, 0, NULL, NULL}
};
PLATFORM_SPEC aAlphaSpec[] =
{
{0, 0, szNTAlphaParamsSect, szNTAlphaFilesSect},
{3, 51, szNTVerIntelParamsSect, szNTVerIntelFilesSect},
{0, 0, szNTIntelParamsSect, szNTIntelFilesSect},
{0, 0, NULL, NULL}
};
PLATFORM_SPEC aMipsSpec[] =
{
{0, 0, szNTMipsParamsSect, szNTMipsFilesSect},
{0, 0, NULL, NULL}
};
PLATFORM_SPEC aPpcSpec[] =
{
{0, 0, szNTPpcParamsSect, szNTPpcFilesSect},
{0, 0, NULL, NULL}
};
PLATFORM_SPEC aEmptySpec[] =
{
{0, 0, NULL, NULL}
};
// Note: this is indexed by PROCESSOR_ARCHITECTURE_xxx
// definitions in ntexapi.h
//
// Note: may want to add extra sections for IA64 and AXP64
PPLATFORM_SPEC aaPlatformSpecs[] =
{
aIntelSpec, // PROCESSOR_ARCHITECTURE_INTEL 0
aMipsSpec, // PROCESSOR_ARCHITECTURE_MIPS 1
aAlphaSpec, // PROCESSOR_ARCHITECTURE_ALPHA 2
aPpcSpec, // PROCESSOR_ARCHITECTURE_PPC 3
aEmptySpec, // PROCESSOR_ARCHITECTURE_SHX 4
aEmptySpec, // PROCESSOR_ARCHITECTURE_ARM 5
aIntelSpec, // PROCESSOR_ARCHITECTURE_IA64 6
aAlphaSpec, // PROCESSOR_ARCHITECTURE_ALPHA64 7
};
/* CPU environment variable and values */
/* (NOTE: These must be upper case) */
char szCpuEnvVarName[] = "PROCESSOR_ARCHITECTURE";
char szIntelEnvVarVal[] = "X86";
char szIA64EnvVarVal[] = "IA64";
char szAlphaEnvVarVal[] = "ALPHA";
char szAXP64EnvVarVal[] = "AXP64";
char szMipsEnvVarVal[] = "MIPS";
char szPpcEnvVarVal[] = "PPC";
/* Bootstrapper class name */
char szBootClass[] = "STUFF-BOOT";
/* String buffer sizes */
#define cchLstLineMax 128
#define cchWinExecLineMax (256 + cchFullPathMax)
/* No. of retries to attempt when removing files or dirs,
* or when executing a chmod.
*/
#define cRetryMax 1200
/* SetErrorMode flags */
#define fNoErrMes 1
#define fErrMes 0
/* Quiet Mode -- Note: EEL must be kept in sync with acmsetup.h */
typedef UINT EEL; /* Exit Error Level */
#define eelSuccess ((EEL)0x0000)
#define eelBootstrapperFailed ((EEL)0x0009) /* Used only in Bootstrapper! */
EEL eelExitErrorLevel = eelBootstrapperFailed;
BOOL fQuietMode = fFalse;
BOOL fExeced = fFalse;
BOOL fWin31 = fFalse;
/* Forward Declarations */
VOID CleanUpTempDir ( char *, char * );
BRC BrcInstallFiles ( char *, char *, char * );
BOOL FCreateTempDir ( char *, char * );
BRC BrcCopyFiles ( char *, char *, char * );
VOID RemoveFiles ( char * );
BRC BrcCopy ( char *, char * );
LONG LcbFreeDrive ( int );
BOOL FVirCheck ( HANDLE );
HWND HwndInitBootWnd ( HANDLE );
LRESULT CALLBACK BootWndProc ( HWND, UINT, WPARAM, LPARAM );
BOOL FGetFileSize ( char *, UINT * );
BRC BrcBuildFileLists ( char *, UINT );
VOID FreeFileLists ( VOID );
BOOL FExecAndWait ( char *, HWND );
BOOL FWriteBatFile ( OFSTRUCT, char *, char * );
BOOL FLstSectionExists ( char * szLstFileName, char * szSect );
DWORD GetCpuArchitecture();
BOOL FNotifyAcme ( VOID );
BOOL FGetAcmeErrorLevel ( EEL * peel );
BOOL FCreateRegKey ( CSZC cszcKey );
BOOL FDoesRegKeyExist ( CSZC cszcKey );
BOOL FCreateRegKeyValue ( CSZC cszcKey, CSZC cszcValue );
BOOL FGetRegKeyValue ( CSZC cszcKey, SZ szBuf, CB cbBufMax );
VOID DeleteRegKey ( CSZC cszcKey );
BOOL FFlushRegKey ( VOID );
BOOL FWriteToRestartFile ( SZ szTmpDir );
BOOL FCreateIniFileName ( SZ szIniFile, CB cbBufMax );
BOOL FReadIniFile ( SZ szIniFile, HLOCAL * phlocal, PCB pcbBuf );
BOOL FAllocNewBuf ( CB cbOld, SZ szTmpDir, SZ szSection, SZ szKey,
HLOCAL * phlocal, PCB pcbToBuf );
BOOL FProcessFile ( HLOCAL hlocalFrom, HLOCAL hlocalTo, CB cbToBuf,
SZ szTmpDir, SZ szSection, SZ szKey );
VOID CopyIniLine ( SZ szKey, SZ szTmpDir, SZ szFile, PSZ pszToBuf );
BOOL FWriteIniFile ( SZ szIniFile, HLOCAL hlocalTo );
BRC BrcInsertDisk(CHAR *pchStf, CHAR *pchSrcDrive);
BOOL FRenameBadMaintStf ( SZ szStf );
/* Bootstrapper list file params */
char rgchSetupDirName[cchLstLineMax];
#ifdef UNUSED /* Replaced by DrvWinClass */
char rgchDrvModName[cchLstLineMax];
#endif /* UNUSED */
char rgchDrvWinClass[cchLstLineMax];
char rgchCmdLine[cchLstLineMax];
char rgchBootTitle[cchLstLineMax];
char rgchBootMess[cchLstLineMax];
char rgchWin31Mess[cchLstLineMax];
char rgchCabinetFName[cchLstLineMax];
char rgchBackgroundFName[cchLstLineMax];
char rgchBkgWinClass[cchLstLineMax];
char rgchInsertCDMsg[cchLstLineMax];
char rgchInsertDiskMsg[cchLstLineMax];
LONG lcbDiskFreeMin;
int cFirstCabinetNum;
int cLastCabinetNum;
HANDLE hSrcLst = NULL;
HANDLE hDstLst = NULL;
char rgchErrorFile[cchFullPathMax];
HANDLE hinstBoot = NULL;
HWND hwndBoot = NULL;
CHAR rgchInsufMem[cchSzMax] = "";
CHAR rgchInitErr[cchSzMax] = "";
CHAR rgchSetup[cchSzMax] = "";
/*
** 'Fixup' temp dir string by removing any subdirs and ensuring
** extension is only one character. (Note - Win3.0 has bug with
** WinExec'ing some EXEs from a full 8.3 directory!)
**************************************************************************/
void FixupTempDirName( LPSTR szDir )
{
LPSTR szNext;
int cch = 0;
if (*szDir == '\\'
|| *(AnsiNext(szDir)) == ':')
{
lstrcpy(szDir, "~msstfqf.t");
return;
}
while (*szDir != '\\'
&& *szDir != '.'
&& *szDir != '\0'
&& *szDir != ':'
&& cch++ < 8)
{
szDir = AnsiNext(szDir);
}
szNext = AnsiNext(szDir);
if (*szDir == '.'
&& *szNext != '.'
&& *szNext != '\\'
&& *szNext != '\0'
&& *szNext != ':')
{
*(AnsiNext(szNext)) = '\0';
return;
}
*szDir = '\0';
lstrcat(szDir, ".t");
}
/* Displays bootstrapper messages.
* If fError is true, it's an error message, otherwise it's
* just a message (e.g. insert disk 1).
**************************************************************************/
int DispErrBrc ( BRC brc, BOOL fError, UINT fuStyle,
const char *sz1, const char *sz2,
const char *sz3 )
{
char rgchTitle[256];
char rgchMessage[256];
char szBuf[256 + cchFullPathMax + 256];
#ifndef DEBUG
if (fQuietMode)
{
return (IDCANCEL);
}
#endif
if (LoadString(hinstBoot, brcGen, rgchTitle, 256) == 0
|| LoadString(hinstBoot, brc, rgchMessage, 256) == 0)
{
MessageBox(hwndBoot, rgchInsufMem, rgchInitErr, MB_OK | MB_ICONSTOP);
return 0;
}
if (!fError)
lstrcpy(rgchTitle, rgchSetup);
if (sz1 == NULL) sz1 = "";
if (sz2 == NULL) sz2 = "";
if (sz3 == NULL) sz3 = "";
if (brc == brcFile)
wsprintf(szBuf, rgchMessage, (LPSTR)AnsiUpper(rgchErrorFile));
else if (brc == brcDS || brc == brcMemDS)
wsprintf(szBuf, rgchMessage, lcbDiskFreeMin / 1024L);
else
wsprintf(szBuf, rgchMessage, sz1, sz2, sz3);
if ((brc == brcMemDS || brc == brcNoSpill)
&& LoadString(hinstBoot, brcMemDSHlp, rgchMessage, 256))
{
lstrcat(szBuf, rgchMessage);
}
else if (brc == brcConnectToSource
&& LoadString(hinstBoot, brcConnectHlp, rgchMessage, 256))
{
lstrcat(szBuf, rgchMessage);
}
return (MessageBox(hwndBoot, szBuf, rgchTitle, fuStyle));
}
/*
** Purpose:
** Installs Setup executable in a temporary directory on an
** available hardrive, and launches Setup. After Setup
** completes, removes the temporary files and directory.
** Arguments:
** Standard Windows WinMain args.
** Returns:
** Returns eelExitErrorLevel. 0 == Success.
**************************************************************************/
int WINAPI WinMain ( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdParam, int nCmdShow )
{
char chDrive;
char rgchDstDir[cchFullPathMax] = " :\\"; // WARN: kept as OEM chars
char * szDstDirSlash = szNull;
char rgchModuleFileName[cchFullPathMax]; // WARN: kept as ANSI chars
char rgchLstFileName[cchFullPathMax];
char rgchTemp[cchFullPathMax];
char rgchSrcDir[cchFullPathMax];
UINT cbLstSize;
char rgchWinExecLine[cchWinExecLineMax];
UINT uiRes;
int iModLen;
BRC brc;
BOOL fCleanupTemp = FALSE;
LPSTR sz;
HWND hWndBkg = 0; /* window of background task */
UINT hMod;
Unused(nCmdShow);
hinstBoot = hInstance;
rgchErrorFile[0] = '\0';
if (LoadString(hinstBoot, IDS_InsufMem, rgchInsufMem,
sizeof rgchInsufMem) == 0
|| LoadString(hinstBoot, IDS_InitErr, rgchInitErr,
sizeof rgchInitErr) == 0
|| LoadString(hinstBoot, IDS_Setup, rgchSetup,
sizeof rgchSetup) == 0)
{
/* REVIEW: If these LoadStrngs fail, the user will never know...
* But we can't hard-code strings in an .h file because INTL
* requires all localizable strings to be in resources!
*/
#ifdef DEBUG
MessageBox(NULL, "Initial LoadString's failed; probably out of memory.",
szDebugMsg, MB_OK | MB_ICONEXCLAMATION);
#endif /* DEBUG */
}
for (sz = lpszCmdParam; *sz != '\0'; sz = AnsiNext(sz))
{
if ((*sz == '-' || *sz == '/')
&& toupper(*(sz+1)) == 'Q' && toupper(*(sz+2)) == 'T')
{
fQuietMode = fTrue;
break;
}
}
/*
* REVIEW: Check that this code is still functional before restoring it.
*/
#if VIRCHECK
if (!FVirCheck(hInstance))
{
DispErrBrc(brcVir, TRUE, MB_OK | MB_ICONSTOP, NULL, NULL, NULL);
goto LCleanupAndExit;
}
#endif
if (hPrevInstance || FindWindow(szBootClass, NULL) != NULL)
{
DispErrBrc(brcInst, TRUE, MB_OK | MB_ICONSTOP, NULL, NULL, NULL);
goto LCleanupAndExit;
}
GetModuleFileName(hInstance, rgchModuleFileName, cchFullPathMax);
/*
* If the first switch on the command line is /M, then it specifies
* the real module name to use.
*/
if ((lpszCmdParam[0] == '-' || lpszCmdParam[0] == '/')
&& toupper(lpszCmdParam[1]) == 'M')
{
char *pCh, *pCh2;
BOOL fQuotedFileName;
/* Skip the spaces */
for (pCh = lpszCmdParam+2; *pCh == ' '; pCh++);
fQuotedFileName = (*pCh == '\"');
if (fQuotedFileName)
{
pCh++;
}
/* Copy the file name, and add the EOS */
lstrcpy(rgchModuleFileName, pCh);
for (pCh2=rgchModuleFileName;
(*pCh2 != ' ' || fQuotedFileName) &&
(*pCh2 != '\"' || !fQuotedFileName) &&
*pCh2 != '\0';
pCh2++);
*pCh2 = '\0';
/* Remove the /M param from the command line */
lpszCmdParam = pCh + lstrlen(rgchModuleFileName);
if (fQuotedFileName && *lpszCmdParam == '\"')
{
lpszCmdParam++;
}
/* Remove trailing whitespace from the command line */
for (pCh = lpszCmdParam; *pCh == ' '; pCh++);
lpszCmdParam = pCh;
}
OemToAnsi(rgchModuleFileName, rgchModuleFileName);
// Windows 3.0 bug with UNC paths - prepends windows drive letter
sz = (LPSTR)rgchModuleFileName;
if (*sz != '\0'
&& *sz != '\\'
&& *(sz = AnsiNext(sz)) == ':'
&& *(sz = AnsiNext(sz)) == '\\'
&& *AnsiNext(sz) == '\\')
{
LPSTR szDst = (LPSTR)rgchModuleFileName;
while ((*szDst++ = *sz++) != '\0')
;
}
iModLen = lstrlen(rgchModuleFileName);
lstrcpy(rgchSrcDir, rgchModuleFileName);
sz = (LPSTR)&rgchSrcDir[iModLen];
while (sz > (LPSTR)rgchSrcDir && *sz != '\\')
sz = AnsiPrev(rgchSrcDir, sz);
Assert(sz > (LPSTR)rgchSrcDir);
*(AnsiNext(sz)) = '\0';
/*
* If the first switch on the command line is /L, then it specifies
* the name of the .lst file to use.
*/
rgchTemp[0] = '\0';
if ((lpszCmdParam[0] == '-' || lpszCmdParam[0] == '/')
&& toupper(lpszCmdParam[1]) == 'L')
{
char *pCh, *pCh2;
/* Skip the spaces */
for (pCh = lpszCmdParam+2; *pCh == ' ' && *pCh != '\0'; pCh++);
/* Copy the .lst file name, and add the newline */
lstrcpy(rgchTemp, pCh);
for (pCh2=rgchTemp; *pCh2 != ' ' && *pCh2!= '\0'; pCh2++);
*pCh2 = '\0';
/* Remove the /L param from the command line */
lpszCmdParam = pCh + lstrlen(rgchTemp);
for (pCh = lpszCmdParam; *pCh == ' ' && *pCh != '\0'; pCh++);
lpszCmdParam = pCh;
}
/* If there is something on the command line, use it as the .lst file */
if (*rgchTemp != '\0')
{
lstrcpy(rgchLstFileName, rgchSrcDir);
lstrcat(rgchLstFileName, rgchTemp);
}
else
{
lstrcpy(rgchLstFileName, rgchModuleFileName);
sz = (LPSTR)&rgchLstFileName[iModLen];
while (sz > (LPSTR)rgchLstFileName && *sz != '.')
sz = AnsiPrev(rgchLstFileName, sz);
Assert(sz > (LPSTR)rgchLstFileName);
*(AnsiNext(sz)) = '\0';
lstrcat(rgchLstFileName, szLstExt);
}
if (!FGetFileSize(rgchLstFileName, &cbLstSize) || cbLstSize == 0)
{
lstrcpy(rgchErrorFile, rgchLstFileName);
DispErrBrc(brcFile, TRUE, MB_OK | MB_ICONSTOP, NULL, NULL, NULL);
goto LCleanupAndExit;
}
#ifndef WF_WINNT
#define WF_WINNT 0x4000
#endif
/* Attempt to use appropriate platform.
*/
szParamsSect = szNull;
szFilesSect = szNull;
if (1)
{
DWORD dwVers = 0;
DWORD dwCpuArchitecture;
DWORD dwMajorVersion;
DWORD dwMinorVersion;
PPLATFORM_SPEC pPlatformSpec;
dwCpuArchitecture = GetCpuArchitecture();
dwVers = GetVersion();
dwMajorVersion = LOBYTE(LOWORD(dwVers));
dwMinorVersion = HIBYTE(LOWORD(dwVers));
if (dwCpuArchitecture < (sizeof (aaPlatformSpecs) / sizeof(aaPlatformSpecs[0])))
{
pPlatformSpec = aaPlatformSpecs[dwCpuArchitecture];
}
else
{
pPlatformSpec = aEmptySpec;
}
for (; pPlatformSpec->szParamsSect != NULL; pPlatformSpec++)
{
if (((pPlatformSpec->minMajorVersion < dwMajorVersion) ||
(pPlatformSpec->minMajorVersion == dwMajorVersion && pPlatformSpec->minMinorVersion <= dwMinorVersion)) &&
FLstSectionExists(rgchLstFileName, pPlatformSpec->szParamsSect))
{
szParamsSect = pPlatformSpec->szParamsSect;
szFilesSect = pPlatformSpec->szFilesSect;
break;
}
}
}
else /* non-WinNT */
{
if (FLstSectionExists(rgchLstFileName, szWin95ParamsSect)
&& (LOBYTE(LOWORD(GetVersion())) > 3
|| HIBYTE(LOWORD(GetVersion())) >= 95))
{
szParamsSect = szWin95ParamsSect;
szFilesSect = szWin95FilesSect;
}
else
{
fWin31 = fTrue;
if (FLstSectionExists(rgchLstFileName, szWin3xParamsSect))
{
szParamsSect = szWin3xParamsSect;
szFilesSect = szWin3xFilesSect;
}
}
}
if (szParamsSect == szNull)
{
if (FLstSectionExists(rgchLstFileName, szDefaultParamsSect))
{
szParamsSect = szDefaultParamsSect;
szFilesSect = szDefaultFilesSect;
}
else
{
DispErrBrc(brcNoCpuSect, TRUE, MB_OK | MB_ICONSTOP, NULL,
NULL, NULL);
goto LCleanupAndExit;
}
}
if (GetPrivateProfileString(szParamsSect, "TmpDirName", "",
rgchSetupDirName, cchLstLineMax, rgchLstFileName) <= 0
|| (lcbDiskFreeMin = GetPrivateProfileInt(szParamsSect,
"TmpDirSize", 0, rgchLstFileName) * 1024L) <= 0
|| (cFirstCabinetNum = GetPrivateProfileInt(szParamsSect,
"FirstCabNum", 1, rgchLstFileName)) <= 0
|| (cLastCabinetNum = GetPrivateProfileInt(szParamsSect,
"LastCabNum", 1, rgchLstFileName)) <= 0
#ifdef UNUSED
|| GetPrivateProfileString(szParamsSect, "DrvModName", "",
rgchDrvModName, cchLstLineMax, rgchLstFileName) <= 0
#endif /* UNUSED */
|| GetPrivateProfileString(szParamsSect, "DrvWinClass", "",
rgchDrvWinClass, cchLstLineMax, rgchLstFileName) <= 0
|| GetPrivateProfileString(szParamsSect, "CmdLine", "", rgchCmdLine,
cchLstLineMax, rgchLstFileName) <= 0
//|| GetPrivateProfileString(szParamsSect, "Require31", "",
// rgchWin31Mess, cchLstLineMax, rgchLstFileName) <= 0
|| GetPrivateProfileString(szParamsSect, "WndTitle", "",
rgchBootTitle, cchLstLineMax, rgchLstFileName) <= 0
|| GetPrivateProfileString(szParamsSect, "WndMess", "",
rgchBootMess, cchLstLineMax, rgchLstFileName) <= 0)
{
DispErrBrc(brcLst, TRUE, MB_OK | MB_ICONSTOP, NULL, NULL, NULL);
goto LCleanupAndExit;
}
if (FindWindow(rgchDrvWinClass, NULL) != NULL)
{
DispErrBrc(brcInst, TRUE, MB_OK | MB_ICONSTOP, NULL, NULL, NULL);
goto LCleanupAndExit;
}
GetPrivateProfileString(szParamsSect, "CabinetFile", "",
rgchCabinetFName, cchLstLineMax, rgchLstFileName);
GetPrivateProfileString(szParamsSect, "InsertCDMsg", "",
rgchInsertCDMsg, cchLstLineMax, rgchLstFileName);
GetPrivateProfileString(szParamsSect, "InsertDiskMsg", "",
rgchInsertDiskMsg, cchLstLineMax, rgchLstFileName);
if (rgchWin31Mess[0] != '\0'
&& LOBYTE(LOWORD((DWORD)GetVersion())) == 3
&& HIBYTE(LOWORD((DWORD)GetVersion())) < 10)
{
if (!fQuietMode)
{
char rgchTitle[256];
if (LoadString(hinstBoot, brcGen, rgchTitle, 256) == 0)
lstrcpy(rgchTitle, rgchSetup);
MessageBox(hwndBoot, rgchWin31Mess, rgchTitle,
MB_OK | MB_ICONSTOP);
}
goto LCleanupAndExit;
}
FixupTempDirName(rgchSetupDirName);
for (sz = rgchBootMess; *sz != '\0'; sz = AnsiNext(sz))
if (*sz == '\\' && *(sz+1) == 'n')
{
*sz++ = '\r';
*sz = '\n';
}
/* If there is a /W then is specifies we are in add/remove mode with
the setup app not installed. We need to read it off CD/Floppy/Network
*/
if ((lpszCmdParam[0] == '-' || lpszCmdParam[0] == '/')
&& toupper(lpszCmdParam[1]) == 'W')
{
CHAR rgchStf[_MAX_PATH];
char *pCh, *pCh2, *pCh3;
/* Skip the spaces */
for (pCh = lpszCmdParam+2; *pCh == ' ' && *pCh != '\0'; pCh++);
lstrcpy(rgchStf, rgchSrcDir);
pCh3 = rgchStf + lstrlen(rgchStf);
/* Copy the .stf file name, and add the newline */
for (pCh2=pCh; *pCh2 != ' ' && *pCh2!= '\0'; pCh2++)
*pCh3++ = *pCh2;
*pCh3 = '\0';
/* Remove the /W parameter */
lpszCmdParam = pCh2;
/* Get them to insert the correct disk */
if ((brc = BrcInsertDisk(rgchStf, rgchSrcDir)) != brcOkay)
{
if (brc != brcMax)
DispErrBrc(brc, TRUE, MB_OK | MB_ICONSTOP, rgchStf, NULL, NULL);
goto LCleanupAndExit;
}
}
GetPrivateProfileString(szParamsSect, "Background", "",
rgchBackgroundFName, cchLstLineMax, rgchLstFileName);
GetPrivateProfileString(szParamsSect, "BkgWinClass", "",
rgchBkgWinClass, cchLstLineMax, rgchLstFileName);
if (rgchBackgroundFName[0] != '\0')
{
lstrcpy(rgchTemp, rgchSrcDir);
lstrcat(rgchTemp, rgchBackgroundFName);
if (rgchBkgWinClass[0] != '\0')
{
lstrcat(rgchTemp, " /C");
lstrcat(rgchTemp, rgchBkgWinClass);
}
lstrcat(rgchTemp, " /T");
lstrcat(rgchTemp, rgchBootTitle);
lstrcat(rgchTemp, " /M");
lstrcat(rgchTemp, rgchBootMess);
hMod = WinExec(rgchTemp, SW_SHOWNORMAL); /* ignore if exec failed */
#ifdef DEBUG
if (hMod < 32)
{
wsprintf(szDebugBuf, "%s: Background WinExec failed.",
rgchBackgroundFName);
MessageBox(NULL, szDebugBuf, szDebugMsg, MB_OK | MB_ICONSTOP);
}
#endif /* DEBUG */
hWndBkg = FindWindow(rgchBkgWinClass, rgchBootTitle);
}
if (!hWndBkg && (hwndBoot = HwndInitBootWnd(hInstance)) == NULL)
{
DispErrBrc(brcMem, TRUE, MB_OK | MB_ICONSTOP, NULL, NULL, NULL);
goto LCleanupAndExit;
}
if ((brc = BrcBuildFileLists(rgchLstFileName, cbLstSize)) != brcOkay)
{
DispErrBrc(brc, TRUE, MB_OK | MB_ICONSTOP, NULL, NULL, NULL);
goto LCleanupAndExit;
}
lstrcat(rgchDstDir, "~MSSETUP.T");
szDstDirSlash = rgchDstDir + lstrlen(rgchDstDir);
lstrcat(rgchDstDir, "\\");
lstrcat(rgchDstDir, rgchSetupDirName);
AnsiToOem(rgchDstDir, rgchDstDir);
for (chDrive = 'Z'; chDrive >= 'A'; --chDrive)
{
UINT fModeSav;
BOOL fDriveFixed;
fModeSav = SetErrorMode(fNoErrMes);
fDriveFixed = (GetDriveTypeEx(chDrive - 'A') == EX_DRIVE_FIXED);
SetErrorMode(fModeSav);
if (fDriveFixed)
{
*rgchDstDir = chDrive;
brc = BrcInstallFiles(rgchSrcDir, rgchDstDir, szDstDirSlash);
if (brc == brcOkay)
break;
if (brc == brcFile)
{
DispErrBrc(brc, TRUE, MB_OK | MB_ICONSTOP, NULL, NULL, NULL);
goto LCleanupAndExit;
}
else if (brc == brcNoSpill)
{
/* Message already handled in HfOpenSpillFile */
goto LCleanupAndExit;
}
}
}
if (chDrive < 'A')
{
uiRes = GetWindowsDirectory(rgchDstDir, cchFullPathMax);
Assert(uiRes > 0);
#if DBCS // [J1] Fixed DBCS raid #46.
AnsiUpper(rgchDstDir);
#endif
/* BLOCK */
{
LPSTR sz = (LPSTR)&rgchDstDir[uiRes];
sz = AnsiPrev(rgchDstDir, sz);
if (*sz != '\\')
lstrcat(rgchDstDir, "\\");
}
lstrcat(rgchDstDir, "~MSSETUP.T");
szDstDirSlash = rgchDstDir + lstrlen(rgchDstDir);
lstrcat(rgchDstDir, "\\");
Assert(lstrlen(rgchDstDir) + lstrlen(rgchSetupDirName)
< cchFullPathMax);
lstrcat(rgchDstDir, rgchSetupDirName);
AnsiToOem(rgchDstDir, rgchDstDir);
brc = BrcInstallFiles(rgchSrcDir, rgchDstDir, szDstDirSlash);
if (brc != brcOkay)
{
/* NoSpill message already handled in HfOpenSpillFile */
if (brc != brcNoSpill)
{
DispErrBrc(brc, TRUE, MB_OK | MB_ICONSTOP, NULL, NULL, NULL);
}
goto LCleanupAndExit;
}
}
hSrcLst = LocalFree(hSrcLst); /* don't need src list anymore */
Assert(hSrcLst == NULL);
/* Use full path to .exe; don't rely on cwd (fails under Win95).
*/
/* block */
{
char rgchTmp[cchWinExecLineMax];
wsprintf(rgchTmp, rgchCmdLine, (LPSTR)rgchSrcDir,
lpszCmdParam);
Assert(lstrlen(rgchTmp) + lstrlen(rgchDstDir) + 1 < cchWinExecLineMax);
lstrcpy(rgchWinExecLine, rgchDstDir);
lstrcat(rgchWinExecLine, "\\");
lstrcat(rgchWinExecLine, rgchTmp);
}
GlobalCompact((DWORD)(64L * 1024L));
fCleanupTemp = TRUE;
if (!fWin31 && !FNotifyAcme())
{
#if DEBUG
DispErrBrc(brcRegDb, TRUE, MB_OK | MB_ICONEXCLAMATION, NULL, NULL, NULL);
#endif /* DEBUG */
/* Try running Acme anyway. */
}
if (!fWin31 && !FWriteToRestartFile(rgchDstDir))
{
#ifdef DEBUG
MessageBox(NULL, "Write to restart file failed. Setup can continue, "
"but some initialization files might not get removed "
"if Setup must restart Windows.",
szDebugMsg, MB_OK | MB_ICONEXCLAMATION);
#endif /* DEBUG */
/*
* Any errors encountered will have been displayed where they occured.
* Try running Acme anyway.
*/
}
if (hWndBkg)
SendMessage(hWndBkg, WM_COMMAND, IDM_ACME_STARTING, 0);
if (!FExecAndWait(rgchWinExecLine, hwndBoot))
{
DispErrBrc(brcMem, TRUE, MB_OK | MB_ICONSTOP, NULL, NULL, NULL);
goto LCleanupAndExit;
}
fExeced = fTrue;
LCleanupAndExit:
if (fCleanupTemp && szDstDirSlash != szNull)
CleanUpTempDir(rgchDstDir, szDstDirSlash);
FreeFileLists();
eelExitErrorLevel = eelBootstrapperFailed;
if (fExeced && !FGetAcmeErrorLevel(&eelExitErrorLevel))
{
#ifdef UNUSED
/* NOTE: Removed to avoid the message on WinNT. On NT, Acme can
* exit and the bootstrapper can kick in before the restart
* actually happens, causing this message (since Acme has already
* removed the reg key as part of its reboot cleanup). We'll
* leave the eelFailed value, though, since no one should be
* relying on it at reboot anyway, and it may help catch other
* problems down the road.
*/
DispErrBrc(brcRegDb, TRUE, MB_OK | MB_ICONSTOP, NULL, NULL, NULL);
#endif /* UNUSED */
Assert(eelExitErrorLevel == eelBootstrapperFailed);
}
if (hwndBoot != NULL)
DestroyWindow(hwndBoot);
if (hWndBkg && IsWindow(hWndBkg))
{
SendMessage(hWndBkg, WM_COMMAND, eelExitErrorLevel == eelSuccess ?
IDM_ACME_COMPLETE : IDM_ACME_FAILURE, 0);
if (IsWindow(hWndBkg))
PostMessage(hWndBkg, WM_QUIT, 0, 0);
}
return (eelExitErrorLevel);
}
/*
** Purpose:
** Creates and temporary subdirectory at the given path,
** appends it to the given path, and copies the Setup files
** into it.
** Arguments:
** szModule: Full path to bootstrapper's directory (ANSI chars).
** rgchDstDir: Full path to destination directory (OEM chars).
** Returns:
** One of the following Bootstrapper return codes:
** brcMem out of memory
** brcDS out of disk space
** brcMemDS out of memory or disk space
** brcFile expected source file missing
** brcOkay completed without error
**************************************************************************/
BRC BrcInstallFiles ( char * szModule, char * rgchDstDir,
char * szDstDirSlash )
{
BRC brc;
if (LcbFreeDrive(*rgchDstDir - 'A' + 1) < lcbDiskFreeMin)
return (brcDS);
if (!FCreateTempDir(rgchDstDir, szDstDirSlash))
return (brcMemDS);
if ((brc = BrcCopyFiles(szModule, rgchDstDir, szDstDirSlash)) != brcOkay)
{
CleanUpTempDir(rgchDstDir, szDstDirSlash);
return (brc);
}
SetFileAttributes(rgchDstDir, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY);
Assert(szDstDirSlash);
Assert(*szDstDirSlash == '\\');
*szDstDirSlash = '\0';
SetFileAttributes(rgchDstDir, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY);
*szDstDirSlash = '\\';
return (brcOkay);
}
/*
** Purpose:
** Removes the temporary files and directory.
** Arguments:
** rgchDstDir: Full path to temp directory (OEM chars).
** Returns:
** None.
**************************************************************************/
VOID CleanUpTempDir ( char * rgchDstDir, char * szDstDirSlash )
{
char rgchRoot[] = " :\\";
int i;
RemoveFiles(rgchDstDir);
rgchRoot[0] = *rgchDstDir;
_chdir(rgchRoot);
SetFileAttributes(rgchDstDir, FILE_ATTRIBUTE_NORMAL);
/* Try to remove the directory up to cRetryMax times.
*/
for (i = 0; i < cRetryMax; i++)
{
if (_rmdir(rgchDstDir) == 0)
break;
}
Assert(szDstDirSlash);
Assert(*szDstDirSlash == '\\');
*szDstDirSlash = '\0';
SetFileAttributes(rgchDstDir, FILE_ATTRIBUTE_NORMAL);
/* Try to remove the directory up to cRetryMax times.
*/
for (i = 0; i < cRetryMax; i++)
{
if (_rmdir(rgchDstDir) == 0)
break;
}
*szDstDirSlash = '\\';
}
/*
** Purpose:
** Creates a temporary subdirectory at the given path,
** and appends it to the given path.
** Arguments:
** rgchDir: Full path to destination directory (OEM chars).
** Returns:
** TRUE if directory was successfully created,
** FALSE if not.
**************************************************************************/
BOOL FCreateTempDir ( char * rgchDir, char * szDstDirSlash )
{
char rgchTmp[cchFullPathMax];
FILE * fp;
char * pch;
int fErr;
int i = 0;
pch = (char *)(&rgchDir[lstrlen(rgchDir)]);
Assert(*pch == '\0');
_chdrive(*rgchDir - 'A' + 1);
Assert(szDstDirSlash);
Assert(*szDstDirSlash == '\\');
*szDstDirSlash = '\0';
_mkdir(rgchDir);
*szDstDirSlash = '\\';
while (!_access(rgchDir, 0))
{
if (!_chdir(rgchDir))
{
/* verify dir is write-able */
lstrcpy(rgchTmp, rgchDir);
lstrcat(rgchTmp, "\\tXXXXXX");
Assert(lstrlen(rgchTmp) < cchFullPathMax);
if (_mktemp(rgchTmp) != NULL
&& (fp = fopen(rgchTmp, "w")) != NULL)
{
fErr = fclose(fp);
Assert(!fErr);
fErr = remove(rgchTmp);
#ifdef DBCS // [J2] Fixed DBCS raid #28.
if (fErr) // Keep the directory name
*pch = '\0';
#else
*pch = '\0';
#endif
return (!fErr);
}
}
if (++i > 9)
break;
_itoa(i, pch, 10);
Assert(lstrlen(rgchDir) < cchFullPathMax);
}
if (i <= 9 && !_mkdir(rgchDir))
{
fErr = _chdir(rgchDir);
Assert(!fErr);
#ifdef DBCS // [J2] Fixed DBCS raid #28.
// Keep the directory name
#else
*pch = '\0';
#endif
return (TRUE);
}
*pch = '\0';
return (FALSE);
}
/*
** Purpose:
** Reopens BAT file and writes DEL or RMDIR line.
** Arguments:
** of: OFSTRUCT to REOPEN.
** szCmd: Command (ANSI chars). ["DEL" or "RMDIR"]
** szArg: Fully qualified pathname for argument (OEM chars).
** Returns:
** TRUE or FALSE.
**************************************************************************/
BOOL FWriteBatFile ( OFSTRUCT of, char * szCmd, char * szArg )
{
int fhBat = -1;
BOOL fRet = TRUE;
if ((fhBat = OpenFile("a", &of, OF_REOPEN | OF_WRITE)) == -1
|| _llseek(fhBat, 0L, 2) == -1L
|| _lwrite(fhBat, szCmd, lstrlen(szCmd)) != (UINT)lstrlen(szCmd)
|| _lwrite(fhBat, (LPSTR)" ", 1) != 1
|| _lwrite(fhBat, szArg, lstrlen(szArg)) != (UINT)lstrlen(szArg)
|| _lwrite(fhBat, (LPSTR)"\r\n", 2) != 2)
{
fRet = FALSE;
}
if (fhBat != -1)
{
int fErr = _lclose(fhBat);
Assert(fErr != -1);
}
return (fRet);
}
#ifdef DEBUG
/*
** Purpose:
** Checks if destination filename is a valid 8.3 name with no path
*/
BOOL FValidFATFileName ( char* szName )
{
int iLen, ch;
for (iLen = 0; (ch = *szName++) != '\0'; iLen++)
{
if (ch <= ' ' || ch == '\\' || ch == ':' || ch == ',')
return fFalse;
if (ch == '.')
{
if (iLen == 0 || iLen > 8)
return fFalse;
iLen = 9;
}
if (iLen == 8 || iLen == 13)
return fFalse;
}
return (iLen > 0);
}
#endif /* DEBUG */
/*
** Purpose:
** Copies the source files into the given destination dir.
** Arguments:
** szModule: Source path (ANSI chars).
** szDstDir: Destination path (OEM chars).
** Returns:
** One of the following bootstrapper return codes:
** brcMem out of memory
** brcDS out of disk space
** brcMemDS out of memory or disk space
** brcFile expected source file missing
** brcOkay completed without error
**************************************************************************/
BRC BrcCopyFiles ( char * szModule, char * szDstDir, char * szDstDirSlash )
{
char rgchSrcFullPath[cchFullPathMax];
char rgchDstFullPath[cchFullPathMax];
char rgchTmpDirPath[cchFullPathMax];
char * szSrc;
char * szDst;
int cbSrc;
BRC brc = brcOkay;
int fhBat = -1;
OFSTRUCT ofBat;
int fErr;
BOOL fCabinetFiles = FALSE;
lstrcpy(rgchDstFullPath, szDstDir);
lstrcat(rgchDstFullPath, "\\_MSSETUP._Q_");
Assert(lstrlen(rgchDstFullPath) < cchFullPathMax);
_chmod(rgchDstFullPath, S_IREAD | S_IWRITE);
remove(rgchDstFullPath);
OemToAnsi(rgchDstFullPath, rgchDstFullPath);
fhBat = OpenFile(rgchDstFullPath, &ofBat, OF_CREATE | OF_WRITE);
AnsiToOem(rgchDstFullPath, rgchDstFullPath);
if (fhBat == -1)
return (brcMemDS);
fErr = _lclose(fhBat);
Assert(!fErr);
szSrc = (char *)LocalLock(hSrcLst);
if (szSrc == NULL)
return (brcMem);
szDst = (char *)LocalLock(hDstLst);
if (szDst == NULL) {
LocalUnlock (hSrcLst);
return (brcMem);
}
for (;
(cbSrc = lstrlen(szSrc)) != 0;
szSrc += cbSrc + 1, szDst += lstrlen(szDst) + 1)
{
//
// This code has been added so that we can detect a path
// in setup.lst for the right hand side of the equals sign. This
// allows us flexiblity in specifying where files like setup.inf
// should be pulled from, otherwise we always use the files from
// the original source location. If we detect "<anything>:\" or
// "\\" then we assume it is a path.
//
if( ((':' == szSrc[1]) && ('\\' == szSrc[2])) ||
(('\\' == szSrc[0]) && ('\\' == szSrc[1])) )
{
rgchSrcFullPath[0] = '\0';
}
else
{
lstrcpy(rgchSrcFullPath, szModule);
}
lstrcat(rgchSrcFullPath, szSrc);
lstrcpy(rgchDstFullPath, szDstDir);
lstrcat(rgchDstFullPath, "\\");
lstrcat(rgchDstFullPath, szDst);
#ifdef DEBUG
if (!FValidFATFileName(szDst))
{
wsprintf(szDebugBuf, "Invalid destination file, must be 8.3: %s",
szDst);
MessageBox(NULL, szDebugBuf, szDebugMsg, MB_OK | MB_ICONSTOP);
continue;
}
#endif /* DEBUG */
Assert(lstrlen(rgchSrcFullPath) < cchFullPathMax);
Assert(lstrlen(rgchDstFullPath) < cchFullPathMax);
if ( !FWriteBatFile(ofBat, "ATTRIB -R", rgchDstFullPath)
|| !FWriteBatFile(ofBat, "DEL", rgchDstFullPath))
{
brc = brcDS;
break;
}
if (*szSrc == '@') /* cabinet file */
{
if (*rgchCabinetFName == '\0')
{
brc = brcFile;
#ifdef DEBUG
lstrcpy(rgchErrorFile, ". Missing CABINET= line");
#endif //DEBUG
break;
}
fCabinetFiles = TRUE;
continue;
}
if ((brc = BrcCopy(rgchSrcFullPath, rgchDstFullPath)) != brcOkay)
break;
_chmod(rgchDstFullPath, S_IREAD);
}
LocalUnlock(hSrcLst);
LocalUnlock(hDstLst);
lstrcpy(rgchDstFullPath, szDstDir);
lstrcat(rgchDstFullPath, "\\_MSSETUP._Q_");
Assert(lstrlen(rgchDstFullPath) < cchFullPathMax);
Assert(szDstDirSlash != szNull);
Assert(*szDstDirSlash == chDirSep);
*szDstDirSlash = chEos;
lstrcpy(rgchTmpDirPath, szDstDir);
*szDstDirSlash = chDirSep;
if (brc == brcOkay
&& (!FWriteBatFile(ofBat, "DEL", rgchDstFullPath)
|| !FWriteBatFile(ofBat, "RMDIR", szDstDir)
|| !FWriteBatFile(ofBat, "RMDIR", rgchTmpDirPath)))
{
return (brcDS);
}
if (fCabinetFiles && brc == brcOkay)
{
szSrc = (char *)LocalLock(hSrcLst);
if(szSrc == NULL)
return (brcMem);
szDst = (char *)LocalLock(hDstLst);
if( szDst == NULL) {
LocalUnlock (hSrcLst);
return (brcMem);
}
#ifdef DEBUG
if (!FValidFATFileName(rgchCabinetFName))
{
wsprintf(szDebugBuf, "Invalid cabinet file, must be 8.3: %s",
rgchCabinetFName);
MessageBox(NULL, szDebugBuf, szDebugMsg, MB_OK | MB_ICONSTOP);
}
else
#endif /* DEBUG */
brc = BrcHandleCabinetFiles(hwndBoot, rgchCabinetFName,
cFirstCabinetNum, cLastCabinetNum, szModule, szDstDir,
szSrc, szDst, rgchErrorFile, rgchDstFullPath);
LocalUnlock(hSrcLst);
LocalUnlock(hDstLst);
}
return (brc);
}
/*
** Purpose:
** Removes the files previously copied to the temp dest dir.
** Arguments:
** szDstDir: full path to destination directory (OEM chars).
** Returns:
** None.
**************************************************************************/
VOID RemoveFiles ( char * szDstDir )
{
char rgchDstFullPath[cchFullPathMax];
char * szDst;
int cbDst;
int i;
OFSTRUCT ofs;
UINT fModeSav;
fModeSav = SetErrorMode(fNoErrMes);
szDst = (char *)LocalLock(hDstLst);
if (szDst == NULL)
return;
for (; (cbDst = lstrlen(szDst)) != 0; szDst += cbDst + 1)
{
lstrcpy(rgchDstFullPath, szDstDir);
lstrcat(rgchDstFullPath, "\\");
lstrcat(rgchDstFullPath, szDst);
Assert(lstrlen(rgchDstFullPath) < cchFullPathMax);
/* Don't try to remove the file if it doesn't exist */
if (OpenFile(rgchDstFullPath, &ofs, OF_EXIST) == HFILE_ERROR)
continue;
/* Try to _chmod the file up to cRetryMax times.
*/
for (i = 0; i < cRetryMax; i++)
{
if (_chmod(rgchDstFullPath, S_IWRITE) == 0)
break;
FYield();
}
/* Try to remove the file up to cRetryMax times.
*/
for (i = 0; i < cRetryMax; i++)
{
if (remove(rgchDstFullPath) == 0)
break;
FYield();
}
}
LocalUnlock(hDstLst);
SetErrorMode(fModeSav);
lstrcpy(rgchDstFullPath, szDstDir);
lstrcat(rgchDstFullPath, "\\_MSSETUP._Q_");
Assert(lstrlen(rgchDstFullPath) < cchFullPathMax);
_chmod(rgchDstFullPath, S_IWRITE);
remove(rgchDstFullPath);
}
/*
** Purpose:
** Copies the given source file to the given destination.
** Arguments:
** szFullPathSrc: full path name of source file (ANSI chars).
** szFullPathDst: full path name of destination file (OEM chars).
** Returns:
** One of the following bootstrapper return codes:
** brcMem out of memory
** brcDS out of disk space
** brcMemDS out of memory or disk space
** brcFile expected source file missing
** brcOkay completed without error
**************************************************************************/
BRC BrcCopy ( char * szFullPathSrc, char * szFullPathDst )
{
int fhSrc = -1;
int fhDst = -1;
OFSTRUCT ofSrc, ofDst;
BRC brc = brcMemDS;
int fErr;
#ifdef APPCOMP
if ((fhSrc = OpenFile(szFullPathSrc, &ofSrc, OF_READ)) == -1)
{
brc = brcFile;
lstrcpy(rgchErrorFile, szFullPathSrc);
goto CopyFailed;
}
#endif /* APPCOMP */
/* REVIEW: BUG: if szFullPathDst is an existing subdirectory
** instead of a file, we'll fail trying to open it, think we're
** out of disk space, and go back up to try another disk.
** This is acceptable for now.
*/
_chmod(szFullPathDst, S_IREAD | S_IWRITE);
OemToAnsi(szFullPathDst, szFullPathDst);
fhDst = OpenFile(szFullPathDst, &ofDst, OF_CREATE | OF_WRITE);
AnsiToOem(szFullPathDst, szFullPathDst);
if (fhDst == -1)
goto CopyFailed;
#ifdef APPCOMP
if (WReadHeaderInfo(fhSrc) > 0)
{
LONG lRet;
lRet = LcbDecompFile(fhSrc, fhDst, -1, 0, TRUE, NULL, 0L, NULL, 0,
NULL);
if (lRet < 0L)
{
if (lRet == (LONG)rcOutOfMemory)
brc = brcMem;
if (lRet == (LONG)rcWriteError)
brc = brcDS;
goto CopyFailed;
}
FFreeHeaderInfo();
}
else /* copy the file using LZExpand */
#endif /* APPCOMP */
{
HFILE hSrcLZ;
DWORD dwRet;
#ifdef APPCOMP
fErr = _lclose(fhSrc);
Assert(!fErr);
fhSrc = -1;
#endif /* APPCOMP */
if ((hSrcLZ = LZOpenFile(szFullPathSrc, &ofSrc, OF_READ)) == -1)
{
brc = brcFile;
lstrcpy(rgchErrorFile, szFullPathSrc);
goto CopyFailed;
}
/* We would like to yield more often, but LZCopy has no callbacks */
FYield();
dwRet = LZCopy(hSrcLZ, fhDst);
LZClose(hSrcLZ);
if (dwRet >= LZERROR_UNKNOWNALG)
{
if (dwRet == LZERROR_GLOBALLOC)
brc = brcMem;
if (dwRet == LZERROR_WRITE)
brc = brcDS;
goto CopyFailed;
}
}
brc = brcOkay;
CopyFailed:
#ifdef APPCOMP
if (fhSrc != -1)
{
fErr = _lclose(fhSrc);
Assert(!fErr);
}
#endif /* APPCOMP */
if (fhDst != -1)
{
fErr = _lclose(fhDst);
Assert(!fErr);
}
return (brc);
}
/*
** Purpose:
** Determine the storage space remaining on disk.
** Arguments:
** nDrive: drive number (1='A', 2='B', etc.)
** Returns:
** Number of bytes free on disk,
** or 0 if not a valid drive.
+++
** Implementation:
** Calls DOS interrupt 21h, funct 36h.
**************************************************************************/
LONG LcbFreeDrive ( int nDrive )
{
LONG lcbRet;
CHAR achRoot[4];
ULARGE_INTEGER freeBytes;
achRoot[0] = 'A'+nDrive-1;
achRoot[1] = ':';
achRoot[2] = '\\';
achRoot[3] = 0;
memset(&freeBytes, 0, sizeof(freeBytes));
GetDiskFreeSpaceEx(achRoot, &freeBytes, 0, 0);
lcbRet = freeBytes.LowPart;
/* KLUDGE: Drives bigger than 2 GB can return zero total space!
*/
if (lcbRet < 0L || lcbRet > (999999L * 1024L))
{
return (999999L * 1024L);
}
return (lcbRet);
}
/*
** Purpose:
** Creates and displays bootstrapper window.
** Arguments:
** hInstance: process instance handle
** Returns:
** Window handle to bootstrapper window, or
** NULL if the window could not be created.
**************************************************************************/
HWND HwndInitBootWnd ( HANDLE hInstance )
{
WNDCLASS wc;
HWND hwnd;
int cx, cy;
wc.style = 0;
wc.lpfnWndProc = BootWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = szBootClass;
if (!RegisterClass(&wc))
return (NULL);
cx = GetSystemMetrics(SM_CXSCREEN) / 2;
cy = GetSystemMetrics(SM_CYSCREEN) / 3;
hwnd = CreateWindow(szBootClass, rgchBootTitle,
WS_DLGFRAME, cx / 2, cy, cx, cy, NULL, NULL, hInstance, NULL);
if (hwnd == NULL)
return (NULL);
if (!fQuietMode)
{
ShowWindow(hwnd, SW_SHOWNORMAL);
UpdateWindow(hwnd);
}
return (hwnd);
}
// ripped off from mvdm\wow32\wgtext.c
ULONG GetTextExtent(HDC hdc, LPSTR lpstr, int cbString)
{
ULONG ul = 0;
SIZE size4;
if ((GetTextExtentPoint(
hdc,
lpstr,
cbString,
&size4
)))
{
// check if either cx or cy are bigger than SHRT_MAX == 7fff
// but do it in ONE SINGLE check
if ((size4.cx | size4.cy) & ~SHRT_MAX)
{
if (size4.cx > SHRT_MAX)
ul = SHRT_MAX;
else
ul = (ULONG)size4.cx;
if (size4.cy > SHRT_MAX)
ul |= (SHRT_MAX << 16);
else
ul |= (ULONG)(size4.cy << 16);
}
else
{
ul = (ULONG)(size4.cx | (size4.cy << 16));
}
}
return (ul);
}
/*
** Purpose:
** WndProc for bootstrapper window.
** Arguments:
** Standard Windows WndProc arguments.
** Returns:
** Result of call DefWindowProc, or zero if WM_PAINT message.
**************************************************************************/
LRESULT CALLBACK BootWndProc ( HWND hwnd, UINT wMsgID, WPARAM wParam,
LPARAM lParam )
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
UINT iMargin;
switch (wMsgID)
{
#ifdef DBCS // [J3] Fixed KK raid #12.
case WM_CREATE:
{
if (!fQuietMode)
{
int x, y, cx, cy;
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
cx = (LOWORD(GetTextExtent(hdc, rgchBootMess, lstrlen(rgchBootMess))) + 13) / 14 * 16 + 2;
if (cx > rect.right)
{
if (cx > GetSystemMetrics(SM_CXSCREEN))
cx = GetSystemMetrics(SM_CXSCREEN);
x = (GetSystemMetrics(SM_CXSCREEN) - cx) / 2;
y = cy = GetSystemMetrics(SM_CYSCREEN) / 3;
SetWindowPos(hwnd, NULL, x, y, cx, cy, SWP_NOZORDER);
}
EndPaint(hwnd, &ps);
}
break;
}
#endif
case WM_PAINT:
if (!fQuietMode)
{
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
iMargin = rect.right / 16;
rect.top = rect.bottom / 2 - GetSystemMetrics(SM_CYCAPTION);
rect.left = iMargin;
rect.right -= iMargin;
SetBkMode(hdc, TRANSPARENT);
DrawText(hdc, rgchBootMess, -1, &rect,
DT_WORDBREAK | DT_CENTER | DT_NOPREFIX);
EndPaint(hwnd, &ps);
}
break;
default:
return (DefWindowProc(hwnd, wMsgID, wParam, lParam));
}
return (0L);
}
/*
** Purpose:
** Get size of file.
** Arguments:
** szFile: List file name (full path, ANSI).
** pcbSize: Pointer to variable to receive file size.
** Returns:
** FALSE if file found and size >= 64K.
** TRUE otherwise.
**************************************************************************/
BOOL FGetFileSize ( char * szFile, UINT * pcbSize )
{
int fh;
int fErr;
LONG lcb;
*pcbSize = 0;
if ((fh = _lopen(szFile, OF_READ)) == -1)
{
return (TRUE);
}
if ((lcb = _llseek(fh, 0L, 2)) > 65535)
{
#pragma warning(disable:4127) /* conditional expression is constant */
Assert(FALSE);
#pragma warning(default:4127)
_lclose(fh);
return (FALSE);
}
*pcbSize = (UINT)lcb;
fErr = _lclose(fh);
Assert(!fErr);
return (TRUE);
}
/*
** Purpose:
** Build file Src and Dst lists from LST file.
** Arguments:
** szFile: List file name (full path, ANSI).
** cbFile: Size of list file
** Note:
** Sets globals: hSrcLst, hDstLst.
** Returns:
** One of the following Bootstrapper return codes:
** brcMem out of memory
** brcLst list file is corrupted
** brcOkay completed without error
**************************************************************************/
BRC BrcBuildFileLists ( char * szFile, UINT cbFile )
{
char rgchDst[cchLstLineMax];
char * szSrc;
char * szDst;
char * pchDstStart;
int cbSrc;
UINT i;
/* Build Src List */
if ((hSrcLst = LocalAlloc(LMEM_MOVEABLE, cbFile)) == NULL)
return (brcMem);
szSrc = (char *)LocalLock(hSrcLst);
if(szSrc == (char *)NULL)
return (brcMem);
i = GetPrivateProfileString(szFilesSect, NULL, "", szSrc, cbFile, szFile);
if (i <= 0)
{
LocalUnlock(hSrcLst);
hSrcLst = LocalFree(hSrcLst);
Assert(hSrcLst == NULL);
return (brcLst);
}
Assert(i+1 < cbFile);
szSrc[i++] = '\0'; /* force double zero at end */
szSrc[i++] = '\0';
LocalUnlock(hSrcLst);
hSrcLst = LocalReAlloc(hSrcLst, i, LMEM_MOVEABLE);
if(hSrcLst == NULL)
return (brcMem);
/* Build Dst List */
if ((hDstLst = LocalAlloc(LMEM_MOVEABLE, cbFile)) == NULL)
{
hSrcLst = LocalFree(hSrcLst);
Assert(hSrcLst == NULL);
return (brcMem);
}
szSrc = (char *)LocalLock(hSrcLst);
if (szSrc == (char *)NULL)
return (brcMem);
szDst = pchDstStart = (char *)LocalLock(hDstLst);
if (szDst == (char *)NULL) {
LocalUnlock (hDstLst);
return (brcMem);
}
for (;
(cbSrc = lstrlen(szSrc)) != 0;
szSrc += cbSrc + 1, szDst += lstrlen(szDst) + 1)
{
if (GetPrivateProfileString(szFilesSect, szSrc, "", rgchDst,
cchLstLineMax, szFile) <= 0)
{
LocalUnlock(hSrcLst);
LocalUnlock(hDstLst);
FreeFileLists();
return (brcLst);
}
AnsiToOem(rgchDst, rgchDst);
lstrcpy(szDst, rgchDst);
}
*szDst = '\0'; /* force double zero at end */
LocalUnlock(hSrcLst);
LocalUnlock(hDstLst);
hDstLst = LocalReAlloc(hDstLst, (int)(szDst - pchDstStart) + 1,
LMEM_MOVEABLE);
if (hDstLst == NULL)
return (brcMem);
return (brcOkay);
}
/*
** Purpose:
** Frees file list buffers with non-NULL handles
** and sets them to NULL.
** Arguments:
** none.
** Returns:
** none.
**************************************************************************/
VOID FreeFileLists ()
{
if (hSrcLst != NULL)
hSrcLst = LocalFree(hSrcLst);
if (hDstLst != NULL)
hDstLst = LocalFree(hDstLst);
Assert(hSrcLst == NULL);
Assert(hDstLst == NULL);
}
/*
** Purpose:
** Spawns off a process with WinExec and waits for it to complete.
** Arguments:
** szCmdLn: Line passed to WinExec (cannot have leading spaces).
** Returns:
** TRUE if successful, FALSE if not.
+++
** Implementation:
** GetModuleUsage will RIP under Win 3.0 Debug if module count is
** zero (okay to ignore and continue), but the GetModuleHandle
** check will catch all zero cases for single instances of the
** driver, the usual case. [Under Win 3.1 we will be able to
** replace both checks with just an IsTask(hMod) check.]
**************************************************************************/
BOOL FExecAndWait ( char * szCmdLn, HWND hwndHide )
{
UINT hMod;
MSG msg;
Assert(!isspace(*szCmdLn)); /* leading space kills WinExec */
if ((hMod = WinExec(szCmdLn, SW_SHOWNORMAL)) > 32)
{
UINT i;
UINT_PTR idTimer;
/* KLUDGE: Give the app some time to create its main window.
*
* On newer versions of NT, we were exiting the while loop
* (below) and cleaning up the temp dir before the app had
* even put up its window.
*
* NOTE: In trials, we only had to retry once, so cRetryMax
* may be overkill, but it should be pretty rare that this
* would fail in shipping products anyway.
*/
for (i = 0; i < cRetryMax; i++)
{
if(FindWindow(rgchDrvWinClass, NULL) != NULL)
break;
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
/* Set the timer to fire every 1/10 of a second. This is
necessary because we might never return from GetMessage */
idTimer = SetTimer(NULL, 0, 100, NULL);
/*
** REVIEW - FindWindow() will wait until the LAST setup quits (not
** necessarily this setup. If exec'ing a 16-bit app we could
** use the old code:
** while (GetModuleHandle(rgchDrvModName) && GetModuleUsage(hMod))
** but on NT this fails so for 32-bit apps we could attempt to
** remove one of the executable files (slow?).
**
** REVIEW - This loop becomes a busy wait under NT, which is bad.
** However, it doesn't appear to affect ACME's performance
** noticeably.
*/
while (FindWindow(rgchDrvWinClass, NULL) != NULL)
{
if (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (msg.message == WM_TIMER && hwndHide != (HWND)NULL)
{
ShowWindow(hwndHide, SW_HIDE);
hwndHide = (HWND)NULL;
}
}
if (idTimer != 0)
KillTimer(0, idTimer);
return (TRUE);
}
#ifdef DEBUG
wsprintf(szDebugBuf, "WinExec Error: %d", hMod);
MessageBox(NULL, szDebugBuf, szDebugMsg, MB_OK | MB_ICONSTOP);
#endif /* DEBUG */
return (FALSE);
}
/*
** Purpose: Processes messages that may be in the queue.
** Arguments: none
** Returns: none
**************************************************************************/
void PUBLIC FYield ( VOID )
{
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
/*
**************************************************************************/
BOOL FLstSectionExists ( char * szLstFileName, char * szSect )
{
return (GetPrivateProfileString(szSect, "CmdLine", "", rgchCmdLine,
cchLstLineMax, szLstFileName) > 0);
}
/*
**************************************************************************/
DWORD GetCpuArchitecture ()
{
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
return sysInfo.wProcessorArchitecture;
}
static CSZC cszcBootstrapperKey = "MS Setup (ACME)\\Bootstrapper\\Exit Level";
static CSZC cszcEelRunning = "Running";
/*
** Purpose:
** Lets Acme know the bootstrapper launched it. So Acme will let
** us know its exit error level.
** Arguments:
** none.
** Returns:
** fTrue if successful, fFalse otherwise.
** Notes:
** REVIEW: Probably should use DDE instead of the Registration
** Database.
**************************************************************************/
BOOL FNotifyAcme ( VOID )
{
if (!FCreateRegKey(cszcBootstrapperKey))
{
return (fFalse);
}
if (!FCreateRegKeyValue(cszcBootstrapperKey, cszcEelRunning))
{
return (fFalse);
}
if (!FFlushRegKey())
{
return (fFalse);
}
return (fTrue);
}
/*
** Purpose:
** Get the exit error level set by Acme and clean up the Registration
** Database.
** Arguments:
** peel: Exit error level (to be set).
** Returns:
** fTrue if successful, fFalse otherwise.
**************************************************************************/
BOOL FGetAcmeErrorLevel ( EEL * peel )
{
CHAR rgchValue[cchSzMax];
if (FGetRegKeyValue(cszcBootstrapperKey, rgchValue, sizeof rgchValue))
{
#ifdef DEBUG
/*
* Assert(isdigit(rgchValue[0]));
* Assert(isdigit(rgchValue[1]) || rgchValue[1] == chEos);
*/
UINT i;
BOOL fValidValue = fFalse;
/* Assumes valid values are 1 or 2 digit numbers. */
for (i = 0; rgchValue[i] != chEos; i++)
{
fValidValue = fTrue;
if (!isdigit(rgchValue[i]) || i > 1)
{
fValidValue = fFalse;
break;
}
}
if (!fValidValue)
{
char szBuf[cchSzMax];
wsprintf(szBuf, "RegKeyValue (%s)", rgchValue);
MessageBox(NULL, szBuf, "Debug Assertion in FGetAcmeErrorLevel",
MB_OK | MB_ICONSTOP);
}
#endif /* DEBUG */
*peel = atoi(rgchValue);
DeleteRegKey(cszcBootstrapperKey);
return (fTrue);
}
else
{
if (fWin31)
{
*peel = eelSuccess;
return fTrue;
}
return (fFalse);
}
}
/*
** Purpose:
** Creates a Registration Database key that is a subkey of
** cszcBootstrapperKey.
****************************************************************************/
BOOL FCreateRegKey ( CSZC cszcKey )
{
HKEY hkey;
if (RegCreateKey(HKEY_CLASSES_ROOT, cszcKey, &hkey) != ERROR_SUCCESS)
{
DispErrBrc(brcRegDb, TRUE, MB_OK | MB_ICONSTOP, NULL, NULL, NULL);
return (fFalse);
}
if (RegCloseKey(hkey) != ERROR_SUCCESS)
{
DispErrBrc(brcRegDb, TRUE, MB_OK | MB_ICONSTOP, NULL, NULL, NULL);
return (fFalse);
}
return (fTrue);
}
/*
** Purpose:
** API to check for the existence of the specified key in
** the Registration Database.
****************************************************************************/
BOOL FDoesRegKeyExist ( CSZC cszcKey )
{
HKEY hkey;
if (RegOpenKey(HKEY_CLASSES_ROOT, cszcKey, &hkey) != ERROR_SUCCESS)
return (fFalse);
RegCloseKey(hkey);
return (fTrue);
}
/*
** Purpose:
** Creates a Registration Database key that is a subkey of
** HKEY_CLASSES_ROOT and associates a value with the key.
****************************************************************************/
BOOL FCreateRegKeyValue ( CSZC cszcKey, CSZC cszcValue )
{
if (RegSetValue(HKEY_CLASSES_ROOT, cszcKey, REG_SZ, cszcValue,
lstrlen(cszcKey)) != ERROR_SUCCESS)
{
DispErrBrc(brcRegDb, TRUE, MB_OK | MB_ICONSTOP, NULL, NULL, NULL);
return (fFalse);
}
return (fTrue);
}
/*
** Purpose:
** Determines the value associated with the specified Registration
** Database key.
****************************************************************************/
BOOL FGetRegKeyValue ( CSZC cszcKey, SZ szBuf, CB cbBufMax )
{
LONG lcb = cbBufMax;
if (szBuf != szNull && cbBufMax != 0)
*szBuf = chEos;
if (!FDoesRegKeyExist(cszcKey))
return (fFalse);
if (RegQueryValue(HKEY_CLASSES_ROOT, cszcKey, szBuf, &lcb)
!= ERROR_SUCCESS)
{
DispErrBrc(brcRegDb, TRUE, MB_OK | MB_ICONSTOP, NULL, NULL, NULL);
return (fFalse);
}
Assert(lcb < cbMaxConst);
return (fTrue);
}
/*
** Purpose:
** API to remove the specified Registration Database key,
** its associated values, and subkeys.
****************************************************************************/
VOID DeleteRegKey ( CSZC cszcKey )
{
char rgchKey[cchSzMax], rgchBuffer[cchSzMax];
char *pch;
HKEY hKeyT = NULL;
lstrcpy(rgchKey, cszcKey);
RegDeleteKey(HKEY_CLASSES_ROOT, rgchKey);
pch = rgchKey + 1;
while(pch > rgchKey)
{
pch = rgchKey + lstrlen(rgchKey);
while (pch > rgchKey)
{
if (*pch == '\\')
break;
pch--;
}
if (*pch != '\\')
break;
*pch = '\0';
if (RegOpenKey(HKEY_CLASSES_ROOT, rgchKey, &hKeyT) != ERROR_SUCCESS)
break;
if (RegEnumKey(hKeyT, 0, rgchBuffer, sizeof(rgchBuffer)) == ERROR_SUCCESS)
{
break;
}
RegCloseKey(hKeyT);
hKeyT = NULL;
RegDeleteKey(HKEY_CLASSES_ROOT, rgchKey);
}
if (hKeyT != NULL)
RegCloseKey(hKeyT);
}
/*
** Purpose:
** API to flush the specified Registration Database key.
****************************************************************************/
BOOL FFlushRegKey ( VOID )
{
/* REVIEW: Does 16 bit code need to flush the RegDb? RegFlushKey is
32 bit.
if (RegFlushKey(HKEY_CLASSES_ROOT)) != ERROR_SUCCESS)
{
DispErrBrc(brcRegDb, TRUE, MB_OK | MB_ICONSTOP, NULL, NULL, NULL);
return (fFalse);
}
*/
return (fTrue);
}
/*
** Purpose:
** Write temporary files to restart ini file. So that if Acme reboots,
** the files in the temporary directory will be removed. Win95 only.
** Arguments:
** szTmpDir: Full path to destination directory (OEM chars).
** Returns:
** fTrue if successful, fFalse otherwise.
**
** REVIEW: The files are removed, but not the temp directories.
** There may be a way to do that via the wininit.ini file.
** This should be looked into.
**************************************************************************/
BOOL FWriteToRestartFile ( SZ szTmpDir )
{
char rgchIniFile[_MAX_PATH];
CB cbFrom;
CB cbTo;
HLOCAL hlocalFrom = (HLOCAL)NULL;
HLOCAL hlocalTo = (HLOCAL)NULL;
BOOL fRet = fFalse;
SZ szSection = "rename";
SZ szKey = "NUL";
/* This code is not used under NT. */
if (1)
{
return (fTrue);
}
if (!FCreateIniFileName(rgchIniFile, sizeof rgchIniFile))
{
goto LCleanupAndReturn;
}
if (!FGetFileSize(rgchIniFile, &cbFrom))
{
goto LCleanupAndReturn;
}
if (!FReadIniFile(rgchIniFile, &hlocalFrom, &cbFrom))
{
goto LCleanupAndReturn;
}
if (!FAllocNewBuf(cbFrom, szTmpDir, szSection, szKey, &hlocalTo, &cbTo))
{
goto LCleanupAndReturn;
}
if (!FProcessFile(hlocalFrom, hlocalTo, cbTo, szTmpDir, szSection, szKey))
{
goto LCleanupAndReturn;
}
if (!FWriteIniFile(rgchIniFile, hlocalTo))
{
goto LCleanupAndReturn;
}
fRet = fTrue;
LCleanupAndReturn:
if (hlocalFrom != (HLOCAL)NULL)
{
hlocalFrom = LocalFree(hlocalFrom);
Assert(hlocalFrom == (HLOCAL)NULL);
}
if (hlocalTo != (HLOCAL)NULL)
{
hlocalTo = LocalFree(hlocalTo);
Assert(hlocalTo == (HLOCAL)NULL);
}
return (fRet);
}
/*
** Purpose:
** Create the restart file name.
** Arguments:
** szIniFile: Buffer to hold file name.
** cbBufMax: Size of buffer.
** Returns:
** fTrue if successful, fFalse otherwise.
**************************************************************************/
BOOL FCreateIniFileName ( SZ szIniFile, CB cbBufMax )
{
CB cbWinDir;
cbWinDir = GetWindowsDirectory((LPSTR)szIniFile, cbBufMax);
if (cbWinDir == 0)
{
#pragma warning(disable:4127) /* conditional expression is constant */
Assert(fFalse); /* Unusual if this happens. */
#pragma warning(default:4127)
return (fFalse);
}
Assert(isalpha(*szIniFile));
Assert(*(szIniFile + 1) == ':');
if (*(AnsiPrev((LPSTR)szIniFile, (LPSTR)&szIniFile[cbWinDir])) != '\\')
lstrcat((LPSTR)szIniFile, "\\");
lstrcat((LPSTR)szIniFile, "wininit.ini");
Assert((CB)lstrlen(szIniFile) < cbBufMax);
return (fTrue);
}
/*
** Purpose:
** Read the data from the ini file
** Arguments:
** szIniFile: Ini file name
** phlocal: Pointer to memory handle.
** pcbBuf: Pointer to the number of bytes in the buffer.
** Returns:
** fTrue if successful, fFalse otherwise.
**************************************************************************/
BOOL FReadIniFile ( SZ szIniFile, HLOCAL * phlocal, PCB pcbBuf )
{
UINT fModeSav;
HLOCAL hlocal;
SZ szBuf;
CB cbBuf;
BOOL fRet = fFalse;
Assert(szIniFile != szNull);
Assert(phlocal != (HLOCAL *)NULL);
Assert(pcbBuf != pcbNull);
fModeSav = SetErrorMode(fNoErrMes);
hlocal = *phlocal;
cbBuf = *pcbBuf;
Assert(hlocal == (HLOCAL)NULL);
if (cbBuf == 0) /* Ini file does not exist or is empty. */
{
/* Alloc room for CR, LF, EOS. */
hlocal = LocalAlloc(LMEM_MOVEABLE, 3);
if (hlocal == NULL)
{
#ifdef DEBUG
MessageBox(NULL, "Out of memory in FReadIniFile.", szDebugMsg,
MB_OK | MB_ICONEXCLAMATION);
#endif /* DEBUG */
}
else
{
szBuf = (SZ)LocalLock(hlocal);
if(szBuf == szNull)
return fFalse;
*szBuf++ = chCR;
*szBuf++ = chEol;
*szBuf = chEos;
*pcbBuf = 2;
fRet = fTrue;
}
}
else
{
HFILE hfile;
OFSTRUCT ofs;
CB cbRead;
/* Flush cache before calling OpenFile() */
WritePrivateProfileString(szNull, szNull, szNull, szIniFile);
hfile = OpenFile(szIniFile, &ofs, OF_READWRITE | OF_SHARE_EXCLUSIVE);
if (hfile == HFILE_ERROR)
{
#ifdef DEBUG
wsprintf(szDebugBuf, "Can't open file: %s.", szIniFile);
MessageBox(NULL, szDebugBuf, szDebugMsg,
MB_OK | MB_ICONEXCLAMATION);
#endif /* DEBUG */
goto LCleanupAndReturn;
}
hlocal = LocalAlloc(LMEM_MOVEABLE, cbBuf + 1);
if (hlocal == NULL)
{
#ifdef DEBUG
MessageBox(NULL, "Out of memory in FReadIniFile.", szDebugMsg,
MB_OK | MB_ICONEXCLAMATION);
#endif /* DEBUG */
}
else
{
szBuf = (SZ)LocalLock(hlocal);
if(szBuf == szNull)
return fFalse;
cbRead = (CB)_lread(hfile, szBuf, cbBuf + 1);
if (cbRead == HFILE_ERROR)
{
#ifdef DEBUG
wsprintf(szDebugBuf, "Can't read file: %s.", szIniFile);
MessageBox(NULL, szDebugBuf, szDebugMsg,
MB_OK | MB_ICONEXCLAMATION);
#endif /* DEBUG */
}
else
{
Assert(cbRead == cbBuf);
*(szBuf + cbBuf) = chEos;
fRet = fTrue;
}
}
hfile = _lclose(hfile);
Assert(hfile != HFILE_ERROR);
}
LCleanupAndReturn:
if (hlocal != NULL)
{
LocalUnlock(hlocal);
}
*phlocal = hlocal;
SetErrorMode(fModeSav);
return (fRet);
}
/*
** Purpose:
** Allocate buffer for new file.
** Arguments:
** cbOld: Size of existing file
** szTmpDir: Full path to destination directory (OEM chars).
** szSection: Ini section name
** szKey: Ini key name
** phlocal: Pointer to memory handle.
** pcbToBuf: Pointer to total size of new buffer.
** Returns:
** fTrue if successful, fFalse if LocalAlloc failed.
**************************************************************************/
BOOL FAllocNewBuf ( CB cbOld, SZ szTmpDir, SZ szSection, SZ szKey,
HLOCAL * phlocal, PCB pcbToBuf )
{
UINT fModeSav;
SZ szDst;
CB cbDst;
CB cbOverhead;
CB cbNew;
BOOL fRet = fFalse;
fModeSav = SetErrorMode(fNoErrMes);
szDst = (SZ)LocalLock(hDstLst);
if(szDst == szNull)
return fFalse;
/*
* Added to the old file will be one line per temporary file
* and (possibly) a section line. cbNew is initialized with
* the size of the section line, plus enough for the file
* (_MSSETUP._Q_) which is not in the DstLst.
*
* Each line will look like:
* <szKey>=<szTmpDir>\<szFile><CR><LF>
*/
cbOverhead = lstrlen(szKey) + 1 + lstrlen(szTmpDir) + 1 + 2;
cbNew = lstrlen(szSection) + 5 + _MAX_PATH;
for (; (cbDst = lstrlen(szDst)) != 0; szDst += cbDst + 1)
{
cbNew += cbOverhead + cbDst;
}
LocalUnlock(hDstLst);
*pcbToBuf = cbOld + cbNew;
*phlocal = LocalAlloc(LMEM_MOVEABLE, *pcbToBuf);
if (*phlocal == NULL)
{
#ifdef DEBUG
MessageBox(NULL, "Out of memory in FAllocNewBuf.", szDebugMsg,
MB_OK | MB_ICONEXCLAMATION);
#endif /* DEBUG */
}
else
fRet = fTrue;
SetErrorMode(fModeSav);
return (fRet);
}
/*
** Purpose:
** Add the new lines to the ini file.
** Arguments:
** hlocalFrom: Handle to Src memory.
** hlocalTo: Handle to Dst memory.
** cbToBuf: Total size of Dst memory.
** szTmpDir: Full path to destination directory (OEM chars).
** szSection: Ini section name
** szKey: Ini key name
** Returns:
** fTrue if successful, fFalse otherwise.
**
** REVIEW: DBCS writes out different order. See DBCS J6 code and
** comments in sysinicm.c.
**************************************************************************/
BOOL FProcessFile ( HLOCAL hlocalFrom, HLOCAL hlocalTo, CB cbToBuf,
SZ szTmpDir, SZ szSection, SZ szKey )
{
UINT fModeSav;
SZ szFromBuf;
SZ szToBuf;
SZ szToStart;
SZ szCur;
SZ szDst;
CB cbSect;
CB cbDst;
Unused(cbToBuf); /* Used in debug only */
fModeSav = SetErrorMode(fNoErrMes);
szFromBuf = (SZ)LocalLock(hlocalFrom);
if(szFromBuf == szNull)
return fFalse;
szToBuf = (SZ)LocalLock(hlocalTo);
if(szToBuf != szNull) {
LocalUnlock (hlocalFrom);
return fFalse;
}
szToStart = szToBuf;
cbSect = lstrlen(szSection);
for (szCur = szFromBuf; *szCur != chEos; szCur = AnsiNext(szCur))
{
if (*szCur == '[' && *((szCur + cbSect + 1)) == ']'
&& _memicmp(szSection, AnsiNext(szCur), cbSect) == 0)
{
/* Found section. Copy up to section line. */
CB cbCopy = (CB)(szCur - szFromBuf);
memcpy(szToBuf, szFromBuf, cbCopy);
szToBuf += cbCopy;
break;
}
}
/* Copy section line. */
*szToBuf++ = '[';
memcpy(szToBuf, szSection, cbSect);
szToBuf += cbSect;
*szToBuf++ = ']';
*szToBuf++ = chCR;
*szToBuf++ = chEol;
/* Copy new lines. */
szDst = (SZ)LocalLock(hDstLst);
if (szDst == szNull) {
LocalUnlock(hlocalFrom);
LocalUnlock(hlocalTo);
return fFalse;
}
for (; (cbDst = lstrlen(szDst)) != 0; szDst += cbDst + 1)
{
CopyIniLine(szKey, szTmpDir, szDst, &szToBuf);
}
LocalUnlock(hDstLst);
CopyIniLine(szKey, szTmpDir, "_MSSETUP._Q_", &szToBuf);
/* Copy rest of file. */
if (*szCur == '[')
{
/*
* Skip section line in From buffer. Allow room for '[', section,
* ']', CR, LF.
*/
szCur += cbSect + 4;
}
else
{
szCur = szFromBuf;
}
szToBuf = _memccpy(szToBuf, szCur, chEos, UINT_MAX);
Assert(szToBuf != szNull);
Assert((CB)lstrlen(szToStart) < cbToBuf);
LocalUnlock(hlocalFrom);
LocalUnlock(hlocalTo);
SetErrorMode(fModeSav);
return (fTrue);
}
/*
** Purpose:
** Constructs and copies an ini line to a buffer.
** Arguments:
** szKey: Ini key name
** szTmpDir: Full path to destination directory (OEM chars).
** szFile: Name of file in temporary directory.
** pszToBuf: Pointer to new buffer.
** Returns:
** none
**************************************************************************/
VOID CopyIniLine ( SZ szKey, SZ szTmpDir, SZ szFile, PSZ pszToBuf )
{
char rgchSysIniLine[256];
CB cbCopy;
lstrcpy(rgchSysIniLine, szKey);
lstrcat(rgchSysIniLine, "=");
lstrcat(rgchSysIniLine, szTmpDir);
lstrcat(rgchSysIniLine, "\\");
lstrcat(rgchSysIniLine, szFile);
Assert(lstrlen(rgchSysIniLine) < sizeof rgchSysIniLine);
cbCopy = lstrlen(rgchSysIniLine);
memcpy(*pszToBuf, rgchSysIniLine, cbCopy);
(*pszToBuf) += cbCopy;
*(*pszToBuf)++ = chCR;
*(*pszToBuf)++ = chEol;
}
/*
** Purpose:
** Writes out the new ini file.
** Arguments:
** szIniFile: Buffer to hold file name.
** hlocalTo: Handle to Src memory.
** Returns:
** fTrue if successful, fFalse otherwise.
**************************************************************************/
BOOL FWriteIniFile ( SZ szIniFile, HLOCAL hlocalTo )
{
UINT fModeSav;
SZ szToBuf;
HFILE hfile;
OFSTRUCT ofs;
CB cbWrite;
BOOL fRet = fFalse;
fModeSav = SetErrorMode(fNoErrMes);
szToBuf = (SZ)LocalLock(hlocalTo);
if(szToBuf == szNull)
return fFalse;
hfile = OpenFile(szIniFile, &ofs, OF_CREATE | OF_WRITE);
if (hfile == HFILE_ERROR)
{
#ifdef DEBUG
wsprintf(szDebugBuf, "Can't open file: %s.", szIniFile);
MessageBox(NULL, szDebugBuf, szDebugMsg,
MB_OK | MB_ICONEXCLAMATION);
#endif /* DEBUG */
goto LUnlockAndReturn;
}
cbWrite = _lwrite(hfile, szToBuf, lstrlen(szToBuf));
if (cbWrite == HFILE_ERROR)
{
#ifdef DEBUG
wsprintf(szDebugBuf, "Can't write to file: %s.", szIniFile);
MessageBox(NULL, szDebugBuf, szDebugMsg,
MB_OK | MB_ICONEXCLAMATION);
#endif /* DEBUG */
}
else
{
fRet = fTrue;
}
hfile = _lclose(hfile);
Assert(hfile != HFILE_ERROR);
LUnlockAndReturn:
LocalUnlock(hlocalTo);
SetErrorMode(fModeSav);
return (fRet);
}
CHAR szcStfSrcDir[] = "Source Directory\t";
#define cchStfSrcDir (sizeof(szcStfSrcDir)-1)
/* Finds the source directory for the installation, asks the user
to insert the disk. And returns */
BRC BrcInsertDisk(CHAR *pchStf, CHAR *pchSrcDrive)
{
CHAR rgbBuf[_MAX_PATH];
BYTE rgbFileBuf[32];
UINT iFileBuf = sizeof(rgbFileBuf), cFileBuf = sizeof(rgbFileBuf);
CHAR *pchBuf = rgbBuf;
CHAR *pchMsg;
int iStf = 0;
HFILE hFile;
BRC brc = brcLst;
char chDrv;
BOOL fQuote = FALSE;
int drvType;
BOOL fFirst = TRUE;
BOOL fOpen = FALSE;
HFILE hFileT;
BOOL fRenameStf = fFalse;
if ((hFile = _lopen(pchStf, OF_READ)) == HFILE_ERROR)
return brcNoStf;
/* Find the path to the original setup. This is stored in the .stf file on the
Source Directory line */
while (pchBuf < rgbBuf + sizeof(rgbBuf))
{
BYTE ch;
if (iFileBuf == cFileBuf)
{
if ((cFileBuf = _lread(hFile, rgbFileBuf, sizeof(rgbFileBuf))) == 0)
goto LDone;
iFileBuf = 0;
}
ch = rgbFileBuf[iFileBuf++];
if (iStf < cchStfSrcDir)
{
if (ch == szcStfSrcDir[iStf])
iStf++;
else
iStf = 0;
continue;
}
if(fQuote)
fQuote = FALSE;
else if (ch == '"')
{
fQuote = TRUE;
continue;
}
else if (ch == '\x0d' || ch == '\t')
break;
*pchBuf++ = (CHAR)ch;
/* Case of having the last character be a DBCS character */
if (IsDBCSLeadByte(ch))
{
if (iFileBuf == cFileBuf)
{
_lread(hFile, &ch, 1);
*pchBuf++ = (CHAR) ch;
}
else
*pchBuf++ = rgbFileBuf[iFileBuf++];
}
}
LDone:
*pchBuf = 0;
if (rgbBuf[0] == 0)
{
fRenameStf = fTrue;
goto LClose;
}
chDrv = (char)toupper(rgbBuf[0]);
if (rgbBuf[1] != ':' || chDrv < 'A' || chDrv > 'Z')
{
/* We know this is a network drive - UNC Name */
drvType = EX_DRIVE_REMOTE;
Assert(rgbBuf[0] == '\\' && rgbBuf[1] == '\\');
}
else
{
drvType = GetDriveTypeEx(chDrv - 'A');
}
lstrcpy(pchSrcDrive, rgbBuf);
if (*AnsiPrev(rgbBuf, pchBuf) != '\\')
{
*pchBuf++ = '\\';
*pchBuf = 0;
}
lstrcat(rgbBuf, "Setup.ini");
while (!fOpen)
{
switch (drvType)
{
case EX_DRIVE_FIXED:
case EX_DRIVE_REMOTE:
case EX_DRIVE_RAMDISK:
case EX_DRIVE_INVALID:
default:
if (!fFirst)
{
/* We've been here before */
DispErrBrc(brcConnectToSource, TRUE, MB_OK | MB_ICONSTOP, pchSrcDrive, NULL, NULL);
brc = brcMax;
goto LClose;
}
/* The setup stuff should be available, change directories and go for it */
break;
case EX_DRIVE_FLOPPY:
case EX_DRIVE_REMOVABLE:
/* Ask to insert disk */
pchMsg = rgchInsertDiskMsg;
goto LAskUser;
break;
case EX_DRIVE_CDROM:
/* Ask to insert their CD */
pchMsg = rgchInsertCDMsg;
LAskUser:
if (fFirst)
{
if (DispErrBrc(brcString, FALSE, MB_ICONEXCLAMATION|MB_OKCANCEL,
pchMsg, NULL, NULL) != IDOK)
{
brc = brcUserQuit;
goto LClose;
}
}
else
{
if (DispErrBrc(brcInsCDRom2, FALSE, MB_ICONEXCLAMATION|MB_OKCANCEL,
rgbBuf, pchMsg, NULL) != IDOK)
{
brc = brcUserQuit;
goto LClose;
}
}
break;
}
if ((hFileT = _lopen(rgbBuf, OF_READ)) != HFILE_ERROR)
{
_lclose(hFileT);
fOpen = fTrue;
}
fFirst = FALSE;
}
brc = brcOkay;
LClose:
_lclose(hFile);
/* If we can't find the source path in the maintenance mode .STF,
* assume it's corrupted and rename it, so when the user runs again
* from the source image, we will just run in 'floppy' mode,
* avoiding the bad .STF file.
* (NOTE: Assumes /W is only used in maint mode!!)
*/
if (fRenameStf)
{
FRenameBadMaintStf(pchStf);
brc = brcNoStf;
}
return brc;
}
/*
****************************************************************************/
BOOL FRenameBadMaintStf ( SZ szStf )
{
CHAR rgch[_MAX_FNAME];
_splitpath(szStf, szNull, szNull, rgch, szNull);
if (*rgch == '\0')
lstrcpy(rgch, "stf");
Assert(lstrlen(rgch) + 4 < sizeof rgch);
lstrcat(rgch, ".000");
rename(szStf, rgch);
/* Remove the original .STF in case the rename failed
* (probably due to a previously renamed .STF file).
*/
remove(szStf);
return (fTrue); /* Always returns true */
}