#include #include #include #include "cdinst.h" #include "resource.h" // global variables HINSTANCE g_hInst; CHAR g_szTitle[128]; CHAR g_szSrcDir[MAX_PATH], g_szDstDir[MAX_PATH]; int _stdcall ModuleEntry(void) { int i; STARTUPINFO si; LPSTR pszCmdLine = GetCommandLine(); if ( *pszCmdLine == '\"' ) { /* * Scan, and skip over, subsequent characters until * another double-quote or a null is encountered. */ while ( *++pszCmdLine && (*pszCmdLine != '\"') ) ; /* * If we stopped on a double-quote (usual case), skip * over it. */ if ( *pszCmdLine == '\"' ) pszCmdLine++; } else { while (*pszCmdLine > ' ') pszCmdLine++; } /* * Skip past any white space preceeding the second token. */ while (*pszCmdLine && (*pszCmdLine <= ' ')) { pszCmdLine++; } si.dwFlags = 0; GetStartupInfoA(&si); i = WinMain(GetModuleHandle(NULL), NULL, pszCmdLine, si.dwFlags & STARTF_USESHOWWINDOW ? si.wShowWindow : SW_SHOWDEFAULT); ExitProcess(i); return i; // We never comes here. } INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pszCmdLine, INT iCmdShow) { BOOL bIniCopiedToTemp = FALSE; CHAR szIniFile[MAX_PATH], szSrcDir[MAX_PATH], szDstDir[MAX_PATH]; LPSTR pszSection, pszPtr, pszLine, pszFile, pszSrcSubDir, pszDstSubDir; DWORD dwLen, dwSpaceReq, dwSpaceFree; g_hInst = hInstance; LoadString(g_hInst, IDS_TITLE, g_szTitle, sizeof(g_szTitle)); ParseCmdLine(pszCmdLine); if (*g_szSrcDir == '\0') { if (GetModuleFileName(g_hInst, g_szSrcDir, sizeof(g_szSrcDir))) if ((pszPtr = ANSIStrRChr(g_szSrcDir, '\\')) != NULL) *pszPtr = '\0'; if (*g_szSrcDir == '\0') { ErrorMsg(IDS_SRCDIR_NOT_FOUND); return -1; } } if (*g_szDstDir == '\0') { HKEY hk; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_APPPATHS "\\ieak6wiz.exe", 0, KEY_READ, &hk) == ERROR_SUCCESS) { dwLen = sizeof(g_szDstDir); RegQueryValueEx(hk, "Path", NULL, NULL, (LPBYTE) g_szDstDir, &dwLen); RegCloseKey(hk); } if (*g_szDstDir == '\0') { ErrorMsg(IDS_DESTDIR_NOT_FOUND); return -1; } } // look for cdinst.ini in the dir where the parent module (ieak6cd.exe) is running from *szIniFile = '\0'; lstrcpy(szIniFile, g_szSrcDir); AddPath(szIniFile, "cdinst.ini"); if (!FileExists(szIniFile)) { // not found where ieak6cd.exe is running from; so look for it in the dir where the current // module (cdinst.exe) is running from *szIniFile = '\0'; if (GetModuleFileName(g_hInst, szIniFile, sizeof(szIniFile))) { if ((pszPtr = ANSIStrRChr(szIniFile, '\\')) != NULL) *pszPtr = '\0'; AddPath(szIniFile, "cdinst.ini"); } if (!FileExists(szIniFile)) { *szIniFile = '\0'; GetModuleFileName(g_hInst, szIniFile, sizeof(szIniFile)); ErrorMsg(IDS_INI_NOT_FOUND, g_szSrcDir, szIniFile); return -1; } } // copy cdinst.ini to the temp dir -- need to do this because on Win95, if cdinst.ini // is at the same location as ieak6cd.exe on a read-only media (like CD), then // GetPrivateProfileSection() calls would fail. // NOTE: szSrcDir and szDstDir are used as temp variables below if (GetTempPath(sizeof(szSrcDir), szSrcDir)) if (GetTempFileName(szSrcDir, "cdinst", 0, szDstDir)) if (CopyFile(szIniFile, szDstDir, FALSE)) { bIniCopiedToTemp = TRUE; lstrcpy(szIniFile, szDstDir); SetFileAttributes(szIniFile, FILE_ATTRIBUTE_NORMAL); } // NOTE: If the destination dir is a UNC path, GetFreeDiskSpace() won't return the right value on Win95 Gold. // So we turn off disk space checking if installing to a UNC path. while (!EnoughDiskSpace(g_szSrcDir, g_szDstDir, szIniFile, &dwSpaceReq, &dwSpaceFree)) { if (ErrorMsg(IDS_NOT_ENOUGH_DISK_SPACE, dwSpaceReq, dwSpaceFree) == IDNO) return -1; } // copy files that are specified in the [copy] section // format of a line in the [copy] section is (all the fields should be on one line): // , // , // if (ReadSectionFromInf("Copy", &pszSection, &dwLen, szIniFile)) { for (pszLine = pszSection; dwLen = lstrlen(pszLine); pszLine += dwLen + 1) { ParseIniLine(pszLine, &pszFile, &pszSrcSubDir, &pszDstSubDir); GetDirPath(g_szSrcDir, pszSrcSubDir, szSrcDir, sizeof(szSrcDir), szIniFile); GetDirPath(g_szDstDir, pszDstSubDir, szDstDir, sizeof(szDstDir), szIniFile); CopyFiles(szSrcDir, pszFile, szDstDir, FALSE); } } if (pszSection != NULL) LocalFree(pszSection); // delete files that are specified in the [exclude] section from the destination dir // format of a line in the [exclude] section is (all the fields should be on one line): // , // if (ReadSectionFromInf("Exclude", &pszSection, &dwLen, szIniFile)) { for (pszLine = pszSection; dwLen = lstrlen(pszLine); pszLine += dwLen + 1) { ParseIniLine(pszLine, &pszFile, NULL, &pszDstSubDir); GetDirPath(g_szDstDir, pszDstSubDir, szDstDir, sizeof(szDstDir), szIniFile); DelFiles(pszFile, szDstDir); } } if (pszSection != NULL) LocalFree(pszSection); // extract all the files from cabs that are specified in the [extract] section // format of a line in the [extract] section is (all the fields should be on one line): // , // , // if (ReadSectionFromInf("Extract", &pszSection, &dwLen, szIniFile)) { HINSTANCE hAdvpack; if ((hAdvpack = LoadLibrary("advpack.dll")) != NULL) { EXTRACTFILES pfnExtractFiles; if ((pfnExtractFiles = (EXTRACTFILES) GetProcAddress(hAdvpack, "ExtractFiles")) != NULL) { for (pszLine = pszSection; dwLen = lstrlen(pszLine); pszLine += dwLen + 1) { ParseIniLine(pszLine, &pszFile, &pszSrcSubDir, &pszDstSubDir); GetDirPath(g_szSrcDir, pszSrcSubDir, szSrcDir, sizeof(szSrcDir), szIniFile); GetDirPath(g_szDstDir, pszDstSubDir, szDstDir, sizeof(szDstDir), szIniFile); ExtractFiles(szSrcDir, pszFile, szDstDir, pfnExtractFiles); } } FreeLibrary(hAdvpack); } } if (pszSection != NULL) LocalFree(pszSection); // move files that are specified in the [move] section from a subdir to another subdir under the destination dir // format of a line in the [move] section is (all the fields should be on one line): // , // , // if (ReadSectionFromInf("Move", &pszSection, &dwLen, szIniFile)) { for (pszLine = pszSection; dwLen = lstrlen(pszLine); pszLine += dwLen + 1) { ParseIniLine(pszLine, &pszFile, &pszSrcSubDir, &pszDstSubDir); GetDirPath(g_szDstDir, pszSrcSubDir, szSrcDir, sizeof(szSrcDir), szIniFile); GetDirPath(g_szDstDir, pszDstSubDir, szDstDir, sizeof(szDstDir), szIniFile); MoveFiles(szSrcDir, pszFile, szDstDir); } } if (pszSection != NULL) LocalFree(pszSection); if (bIniCopiedToTemp) DeleteFile(szIniFile); return 0; } BOOL EnoughDiskSpace(LPCSTR pcszSrcRootDir, LPCSTR pcszDstRootDir, LPCSTR pcszIniFile, LPDWORD pdwSpaceReq, LPDWORD pdwSpaceFree) // check if there is enough free disk space to copy all the files { DWORD dwSpaceReq = 0, dwSpaceFree; CHAR szSrcDir[MAX_PATH], szDstDir[MAX_PATH]; LPSTR pszSection, pszLine, pszFile, pszSrcSubDir, pszDstSubDir; DWORD dwLen, dwFlags; if (!GetFreeDiskSpace(pcszDstRootDir, &dwSpaceFree, &dwFlags)) { // if we can't get FreeDiskSpace info, then turn off disk space checking return TRUE; } // total space required = // size of all the files to be copied + // 2 * size of all the files to be extracted if (ReadSectionFromInf("Copy", &pszSection, &dwLen, pcszIniFile)) { for (pszLine = pszSection; dwLen = lstrlen(pszLine); pszLine += dwLen + 1) { ParseIniLine(pszLine, &pszFile, &pszSrcSubDir, &pszDstSubDir); GetDirPath(pcszSrcRootDir, pszSrcSubDir, szSrcDir, sizeof(szSrcDir), pcszIniFile); GetDirPath(pcszDstRootDir, pszDstSubDir, szDstDir, sizeof(szDstDir), pcszIniFile); dwSpaceReq += FindSpaceRequired(szSrcDir, pszFile, szDstDir); } } if (pszSection != NULL) LocalFree(pszSection); if (ReadSectionFromInf("Extract", &pszSection, &dwLen, pcszIniFile)) { for (pszLine = pszSection; dwLen = lstrlen(pszLine); pszLine += dwLen + 1) { ParseIniLine(pszLine, &pszFile, &pszSrcSubDir, NULL); GetDirPath(pcszSrcRootDir, pszSrcSubDir, szSrcDir, sizeof(szSrcDir), pcszIniFile); dwSpaceReq += 2 * FindSpaceRequired(szSrcDir, pszFile, NULL); } } if (pszSection != NULL) LocalFree(pszSection); dwSpaceReq += 1024; // 1MB buffer to account for random stuff if (dwFlags & FS_VOL_IS_COMPRESSED) { // if the destination volume is compressed, the free space returned is only // a guesstimate; for example, if it's a DoubleSpace volume, the system thinks // that it can compress by 50% and so it reports the free space as (actual free space * 2) // it's better to be safe when dealing with compressed volumes; so bump up the space // requirement by a factor 2 dwSpaceReq <<= 1; // multiply by 2 } if (pdwSpaceReq != NULL) *pdwSpaceReq = dwSpaceReq; if (pdwSpaceFree != NULL) *pdwSpaceFree = dwSpaceFree; return dwSpaceFree > dwSpaceReq; } BOOL GetFreeDiskSpace(LPCSTR pcszDir, LPDWORD pdwFreeSpace, LPDWORD pdwFlags) // Return the free disk space (in KBytes) in *pdwFreeSpace { BOOL bRet = FALSE; DWORD dwFreeSpace = 0; DWORD nSectorsPerCluster, nBytesPerSector, nFreeClusters, nTotalClusters; CHAR szDrive[8]; if (pcszDir == NULL || *pcszDir == '\0' || *(pcszDir + 1) != ':') return FALSE; if (pdwFreeSpace == NULL) return FALSE; lstrcpyn(szDrive, pcszDir, 3); AddPath(szDrive, NULL); if (GetDiskFreeSpace(szDrive, &nSectorsPerCluster, &nBytesPerSector, &nFreeClusters, &nTotalClusters)) { // convert size to KBytes; assumption here is that the free space doesn't exceed 4096 gigs if ((*pdwFreeSpace = MulDiv(nFreeClusters, nSectorsPerCluster * nBytesPerSector, 1024)) != (DWORD) -1) { bRet = TRUE; if (pdwFlags != NULL) { *pdwFlags = 0; GetVolumeInformation(szDrive, NULL, 0, NULL, NULL, pdwFlags, NULL, 0); } } } return bRet; } DWORD FindSpaceRequired(LPCSTR pcszSrcDir, LPCSTR pcszFile, LPCSTR pcszDstDir) // Return the difference in size (in KBytes) of pcszFile (can contain wildcards) // under pcszSrcDir and pcszDstDir (if specified) { DWORD dwSizeReq = 0; CHAR szSrcFile[MAX_PATH], szDstFile[MAX_PATH]; LPSTR pszSrcPtr, pszDstPtr; WIN32_FIND_DATA fileData; HANDLE hFindFile; lstrcpy(szSrcFile, pcszSrcDir); AddPath(szSrcFile, NULL); pszSrcPtr = szSrcFile + lstrlen(szSrcFile); if (pcszDstDir != NULL) { lstrcpy(szDstFile, pcszDstDir); AddPath(szDstFile, NULL); pszDstPtr = szDstFile + lstrlen(szDstFile); } lstrcpy(pszSrcPtr, pcszFile); if ((hFindFile = FindFirstFile(szSrcFile, &fileData)) != INVALID_HANDLE_VALUE) { do { if (!(fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { DWORD dwSrcSize, dwDstSize; // assumption here is that the size of the file doesn't exceed 4 gigs dwSrcSize = fileData.nFileSizeLow; dwDstSize = 0; if (pcszDstDir != NULL) { lstrcpy(pszDstPtr, fileData.cFileName); dwDstSize = FileSize(szDstFile); } if (dwSrcSize >= dwDstSize) { // divide the difference by 1024 (we are interested in KBytes) dwSizeReq += ((dwSrcSize - dwDstSize) >> 10); if (dwSrcSize > dwDstSize) dwSizeReq++; // increment by 1 to promote any fraction to a whole number } } } while (FindNextFile(hFindFile, &fileData)); FindClose(hFindFile); } return dwSizeReq; } VOID ParseIniLine(LPSTR pszLine, LPSTR *ppszFile, LPSTR *ppszSrcDir, LPSTR *ppszDstDir) { if (ppszFile != NULL) *ppszFile = Trim(GetNextField(&pszLine, ",", REMOVE_QUOTES)); if (ppszSrcDir != NULL) *ppszSrcDir = Trim(GetNextField(&pszLine, ",", REMOVE_QUOTES)); if (ppszDstDir != NULL) *ppszDstDir = Trim(GetNextField(&pszLine, ",", REMOVE_QUOTES)); } LPSTR GetDirPath(LPCSTR pcszRootDir, LPCSTR pcszSubDir, CHAR szDirPath[], DWORD cchBuffer, LPCSTR pcszIniFile) { *szDirPath = '\0'; if (pcszRootDir == NULL) return NULL; lstrcpyn(szDirPath, pcszRootDir, cchBuffer); if (pcszSubDir != NULL && *pcszSubDir) { CHAR szTemp[MAX_PATH]; // if there are any placeholders in pcszSubDir (%en%, etc), ReplacePlaceholders will replace // them with the actual strings if (ReplacePlaceholders(pcszSubDir, pcszIniFile, szTemp, sizeof(szTemp))) { if ((DWORD) lstrlen(szDirPath) + 1 < cchBuffer) // there is room for '\\' which AddPath // might append to szDirPath (see below) { INT iLen; AddPath(szDirPath, NULL); // we have enough room in szDirPath for '\\' if (cchBuffer > (DWORD) (iLen = lstrlen(szDirPath))) lstrcpyn(szDirPath + iLen, szTemp, cchBuffer - iLen); } } } return szDirPath; } DWORD ReplacePlaceholders(LPCSTR pszSrc, LPCSTR pszIns, LPSTR pszBuffer, DWORD cchBuffer) { LPCSTR pszAux; CHAR szResult[MAX_PATH]; UINT nDestPos, nLeftPos; nDestPos = 0; nLeftPos = (UINT) -1; for (pszAux = pszSrc; *pszAux; pszAux = CharNext(pszAux)) { if (*pszAux != '%') { szResult[nDestPos++] = *pszAux; if (IsDBCSLeadByte(*pszAux)) szResult[nDestPos++] = *(pszAux + 1); // copy the trail byte as well } else if (*(pszAux + 1) == '%') // "%%" is just '%' in the string { if (nLeftPos != (UINT) -1) // REVIEW: (andrewgu) "%%" are not allowed inside tokens. this also means that // tokens can't be like %foo%%bar%, where the intention is for foo and bar to // be tokens. return 0; szResult[nDestPos++] = *pszAux++; } else { UINT nRightPos; nRightPos = (UINT) (pszAux - pszSrc); // initialized, but not necessarily used as such if (nLeftPos == (UINT) -1) nLeftPos = nRightPos; else { CHAR szAux1[MAX_PATH], szAux2[MAX_PATH]; DWORD dwLen; UINT nTokenLen; // "%%" is invalid here nTokenLen = nRightPos - nLeftPos - 1; lstrcpyn(szAux1, pszSrc + nLeftPos + 1, nTokenLen + 1); if ((dwLen = GetPrivateProfileString("Strings", szAux1, "", szAux2, sizeof(szAux2), pszIns))) { lstrcpy(&szResult[nDestPos - nTokenLen], szAux2); nDestPos += dwLen - nTokenLen; } nLeftPos = (UINT) -1; } } } if (nLeftPos != (UINT) -1) // mismatched '%' return 0; if (cchBuffer <= nDestPos) // insufficient buffer size return 0; szResult[nDestPos] = '\0'; // make sure zero terminated lstrcpy(pszBuffer, szResult); return nDestPos; } VOID SetAttribsToNormal(LPCSTR pcszFile, LPCSTR pcszDir) // Set the attribs of pcszFile (can contain wildcards) under pcszDir to NORMAL { CHAR szFile[MAX_PATH]; LPSTR pszPtr; WIN32_FIND_DATA fileData; HANDLE hFindFile; lstrcpy(szFile, pcszDir); AddPath(szFile, NULL); pszPtr = szFile + lstrlen(szFile); lstrcpy(pszPtr, pcszFile); if ((hFindFile = FindFirstFile(szFile, &fileData)) != INVALID_HANDLE_VALUE) { do { if (!(fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { lstrcpy(pszPtr, fileData.cFileName); SetFileAttributes(szFile, FILE_ATTRIBUTE_NORMAL); } } while (FindNextFile(hFindFile, &fileData)); FindClose(hFindFile); } } VOID CopyFiles(LPCSTR pcszSrcDir, LPCSTR pcszFile, LPCSTR pcszDstDir, BOOL fQuiet) { SHFILEOPSTRUCT shfStruc; CHAR szSrcFiles[MAX_PATH + 1]; if (!PathExists(pcszDstDir)) PathCreatePath(pcszDstDir); else { // set the attribs of files under pcszDstDir to NORMAL so that on a reinstall, // SHFileOperation doesn't choke on read-only files SetAttribsToNormal(pcszFile, pcszDstDir); } ZeroMemory(szSrcFiles, sizeof(szSrcFiles)); lstrcpy(szSrcFiles, pcszSrcDir); AddPath(szSrcFiles, pcszFile); ZeroMemory(&shfStruc, sizeof(shfStruc)); shfStruc.hwnd = NULL; shfStruc.wFunc = FO_COPY; shfStruc.pFrom = szSrcFiles; shfStruc.pTo = pcszDstDir; shfStruc.fFlags = FOF_FILESONLY | FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR; if (fQuiet) shfStruc.fFlags |= FOF_SILENT; SHFileOperation(&shfStruc); } VOID DelFiles(LPCSTR pcszFile, LPCSTR pcszDstDir) { SHFILEOPSTRUCT shfStruc; CHAR szDstFiles[MAX_PATH + 1]; // set the attribs of files under pcszDstDir to NORMAL so that // SHFileOperation doesn't choke on read-only files SetAttribsToNormal(pcszFile, pcszDstDir); ZeroMemory(szDstFiles, sizeof(szDstFiles)); lstrcpy(szDstFiles, pcszDstDir); AddPath(szDstFiles, pcszFile); ZeroMemory(&shfStruc, sizeof(shfStruc)); shfStruc.hwnd = NULL; shfStruc.wFunc = FO_DELETE; shfStruc.pFrom = szDstFiles; shfStruc.fFlags = FOF_FILESONLY | FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI; SHFileOperation(&shfStruc); } VOID ExtractFiles(LPCSTR pcszSrcDir, LPCSTR pcszFile, LPCSTR pcszDstDir, EXTRACTFILES pfnExtractFiles) { CHAR szSrcCab[MAX_PATH]; lstrcpy(szSrcCab, pcszSrcDir); AddPath(szSrcCab, pcszFile); // NOTE: ExtractFiles fails if the dest dir doesn't exist if (!PathExists(pcszDstDir)) PathCreatePath(pcszDstDir); else { // set the attribs of all the files under pcszDstDir to NORMAL so that on a reinstall, // ExtractFiles doesn't choke on read-only files SetAttribsToNormal("*.*", pcszDstDir); } pfnExtractFiles(szSrcCab, pcszDstDir, 0, NULL, NULL, 0); } VOID MoveFiles(LPCSTR pcszSrcDir, LPCSTR pcszFile, LPCSTR pcszDstDir) { // Can't use SHFileOperation to move files because on a reinstall, // we get an error saying that the target files already exist. // Workaround is to call CopyFiles and then DelFiles. CopyFiles(pcszSrcDir, pcszFile, pcszDstDir, TRUE); DelFiles(pcszFile, pcszSrcDir); }