windows-nt/Source/XPSP1/NT/base/ntsetup/win95upg/w95upgnt/migmain/stf.c
2020-09-26 16:20:57 +08:00

2221 lines
58 KiB
C

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
stf.c
Abstract:
Applications that use the ACME Setup toolkit leave .STF files with their
installation, so ACME can reinstall or uninstall the application. During
the upgrade, we move paths around, and we confuse ACME Setup to the point
where it won't run.
The routines in this file update all STF files found on the system. Each
STF has an associated INF file, and the code here parses the STF and INF
files into memory structures, and then enumerates the structures in
various ways, updating all the paths.
Entry points:
ProcessStfFiles - Enumerates all STF files and processes those that have
not already been handled in another way. The older
STF files are overwritten.
See the ACME Setup specification for more details on the format of STF
files and their associated INFs.
Author:
Jim Schmidt (jimschm) 12-Sep-1997
Revision History:
jimschm 28-Sep-1998 Updated to change all altered dirs
--*/
#include "pch.h"
#include "migmainp.h"
#include "stftable.h"
#include "fileops.h"
#include "stfp.h"
#define DBG_STF "STF"
#define S_SECTIONNAME_SPRINTF TEXT("Win9xUpg_%u")
#define COLUMN_OBJECT_ID 0
#define COLUMN_COMMAND 4
#define COLUMN_OBJECT_DATA 5
#define COLUMN_DEST_DIR 10
#define COLUMN_INSTALL_DESTDIR 14
PVOID
pBuildObjectIdTable (
IN PSETUPTABLE TablePtr,
OUT PUINT FirstLinePtr, OPTIONAL
OUT PUINT LastLinePtr OPTIONAL
);
BOOL
pIsObjIdValid (
IN PVOID ObjIdTable,
IN UINT ObjId,
OUT PUINT Line OPTIONAL
);
BOOL
ProcessStfFiles (
VOID
)
/*++
Routine Description:
ProcessStfFiles enumerates the memdb category Stf and converts
the paths in the STF to the new locations.
Arguments:
none
Return Value:
TRUE if success, FALSE if failure.
--*/
{
MEMDB_ENUM e;
DWORD ops;
if (MemDbGetValueEx (&e, MEMDB_CATEGORY_STF, NULL, NULL)) {
do {
//
// Is file handled? If so, skip it.
//
ops = GetOperationsOnPath (e.szName);
if (ops & (OPERATION_MIGDLL_HANDLED|ALL_DELETE_OPERATIONS|OPERATION_FILE_DISABLED)) {
continue;
}
//
// Process the file
//
DEBUGMSG ((DBG_STF, "Processing %s", e.szName));
if (!pProcessSetupTableFile (e.szName)) {
//
// Log the failure
//
LOG ((LOG_INFORMATION, (PCSTR)MSG_COULD_NOT_PROCESS_STF_LOG, e.szName));
} else {
TickProgressBar ();
}
} while (MemDbEnumNextValue (&e));
}
return TRUE;
}
BOOL
pProcessSetupTable (
IN OUT PSETUPTABLE TablePtr
)
/*++
Routine Description:
pProcessSetupTable scans the entire setup table file specified, looking
for CopyFile, CopySection, RemoveFile or RemoveSection lines. If any are
found, any paths that point to moved or deleted files are adjusted, and
any STF group references are updated.
Arguments:
TablePtr - Specifies the setup table file to process
Return Value:
TRUE if success, FALSE if failure.
--*/
{
UINT MaxObj;
UINT Line;
UINT Obj;
PCTSTR EntryStr;
PCTSTR DataStr;
PCTSTR InstallDestDir;
PCTSTR *ArgArray;
PCTSTR p;
UINT ArgCount;
PTABLEENTRY Entry;
TCHAR SystemDir[MAX_TCHAR_PATH];
UINT SystemDirLen;
PCTSTR UpdatedDir;
PVOID ObjTable;
TCHAR MovedPath[MAX_TCHAR_PATH];
DWORD MovedPathLen;
PCTSTR NewPath;
PTSTR q;
DWORD FileStatus;
PTSTR *ListOfWacks;
PTSTR *WackPos;
BOOL Result = TRUE;
MaxObj = TablePtr->MaxObj;
if (TablePtr->SourceInfFile != INVALID_HANDLE_VALUE) {
//
// If an INF is specified, scan the STF/INF pair for references to moved files.
// If found, correct the files.
//
for (Line = 0 ; Line < TablePtr->LineCount ; Line++) {
if (!pGetNonEmptyTableEntry (TablePtr, Line, COLUMN_OBJECT_ID, NULL, &EntryStr)) {
continue;
}
Obj = _ttoi (EntryStr);
if (Obj < 1 || Obj > MaxObj) {
continue;
}
//
// CopySection or RemoveSection: Data column has INF section name
//
if (!pGetNonEmptyTableEntry (TablePtr, Line, COLUMN_COMMAND, NULL, &EntryStr)) {
continue;
}
if (!pGetNonEmptyTableEntry (TablePtr, Line, COLUMN_OBJECT_DATA, NULL, &DataStr)) {
continue;
}
InstallDestDir = GetDestDir (TablePtr, Line);
if (!InstallDestDir) {
continue;
}
ArgArray = ParseCommaList (TablePtr, DataStr);
if (!ArgArray) {
continue;
}
for (ArgCount = 0 ; ArgArray[ArgCount] ; ArgCount++) {
// empty
}
__try {
if (StringIMatch (EntryStr, TEXT("CopySection")) ||
StringIMatch (EntryStr, TEXT("RemoveSection"))
) {
if (ArgCount != 1) {
continue;
}
if (!pProcessSectionCommand (TablePtr, Line, ArgArray[0], InstallDestDir)) {
DEBUGMSG ((DBG_STF, "%s [%s] could not be processed", EntryStr, ArgArray[0]));
Result = FALSE;
__leave;
}
}
else if (StringIMatch (EntryStr, TEXT("CopyFile")) ||
StringIMatch (EntryStr, TEXT("RemoveFile")) ||
StringIMatch (EntryStr, TEXT("InstallSysFile"))
) {
if (ArgCount != 2) {
continue;
}
if (!pProcessLineCommand (TablePtr, Line, ArgArray[0], ArgArray[1], InstallDestDir)) {
DEBUGMSG ((DBG_STF, "%s [%s] %s could not be processed", EntryStr, ArgArray[0], ArgArray[1]));
Result = FALSE;
__leave;
}
}
else if (StringIMatch (EntryStr, TEXT("CompanionFile"))) {
if (ArgCount != 2) {
continue;
}
// First arg has a colon -- skip past it
p = _tcschr (ArgArray[0], TEXT(':'));
if (!p) {
continue;
}
p = SkipSpace (_tcsinc (p));
if (!pProcessLineCommand (TablePtr, Line, p, ArgArray[1], InstallDestDir)) {
DEBUGMSG ((DBG_STF, "%s [%s] %s could not be processed", EntryStr, ArgArray[0], ArgArray[1]));
Result = FALSE;
__leave;
}
}
else if (StringIMatch (EntryStr, TEXT("InstallShared"))) {
if (ArgCount != 5) {
continue;
}
if (!pProcessLineCommand (TablePtr, Line, ArgArray[0], ArgArray[1], InstallDestDir)) {
DEBUGMSG ((DBG_STF, "%s [%s] %s could not be processed", EntryStr, ArgArray[0], ArgArray[1]));
Result = FALSE;
__leave;
}
}
}
__finally {
FreeDestDir (TablePtr, InstallDestDir);
FreeCommaList (TablePtr, ArgArray);
}
}
}
//
// Perform STF-only processing
//
SystemDirLen = wsprintf (SystemDir, TEXT("%s\\system\\"), g_WinDir);
ObjTable = pBuildObjectIdTable (TablePtr, NULL, NULL);
__try {
for (Line = 0 ; Line < TablePtr->LineCount ; Line++) {
//
// Get InstallDestDir and Entry from the line that needs to be modified.
//
if (!pGetNonEmptyTableEntry (
TablePtr,
Line,
COLUMN_INSTALL_DESTDIR,
&Entry,
&InstallDestDir
)) {
continue;
}
//
// If InstallDestDir has a %windir%\system in it, we must adjust the path
// to point to system32.
//
if (StringIMatchCharCount (InstallDestDir, SystemDir, SystemDirLen)) {
UpdatedDir = JoinPaths (
g_System32Dir,
CharCountToPointer (InstallDestDir, SystemDirLen - 1)
);
if (!ReplaceTableEntryStr (TablePtr, Entry, UpdatedDir)) {
LOG ((LOG_ERROR, "Could not replace a %%M path"));
Result = FALSE;
}
FreePathString (UpdatedDir);
if (!Result) {
__leave;
}
}
//
// If InstallDestDir points to a moved dir, we must fix it
//
else if (*InstallDestDir && _tcsnextc (_tcsinc (InstallDestDir)) == TEXT(':')) {
//
// Build list of wacks in the path
//
ListOfWacks = (PTSTR *) MemAlloc (g_hHeap, 0, sizeof (PTSTR) * MAX_TCHAR_PATH);
MYASSERT (ListOfWacks);
StringCopy (MovedPath, InstallDestDir);
q = _tcschr (MovedPath, TEXT('\\'));
WackPos = ListOfWacks;
if (q) {
while (*q) {
if (_tcsnextc (q) == TEXT('\\')) {
*WackPos = q;
WackPos++;
}
q = _tcsinc (q);
}
//
// We assume the STF always has an extra wack at the end
// of the path.
//
//
// Test each path from longest to shortest, skipping the root
//
FileStatus = FILESTATUS_UNCHANGED;
while (WackPos > ListOfWacks) {
WackPos--;
q = *WackPos;
*q = 0;
FileStatus = GetFileStatusOnNt (MovedPath);
if (FileStatus == FILESTATUS_MOVED) {
break;
}
DEBUGMSG_IF ((
FileStatus != FILESTATUS_UNCHANGED,
DBG_WARNING,
"STF may point to changed dir: %s",
MovedPath
));
}
if (FileStatus == FILESTATUS_MOVED) {
//
// Adjust the STF path
//
NewPath = GetPathStringOnNt (MovedPath);
if (NewPath) {
MovedPathLen = (PBYTE) q - (PBYTE) MovedPath;
UpdatedDir = JoinPaths (
NewPath,
ByteCountToPointer (InstallDestDir, MovedPathLen)
);
DEBUGMSG ((
DBG_STF,
"Line %u has a new install destination: %s",
Line,
UpdatedDir
));
if (!ReplaceTableEntryStr (TablePtr, Entry, UpdatedDir)) {
LOG ((LOG_ERROR, "Could not replace a moved path"));
Result = FALSE;
}
FreePathString (UpdatedDir);
FreePathString (NewPath);
if (!Result) {
__leave;
}
}
}
}
MemFree (g_hHeap, 0, ListOfWacks);
}
}
}
__finally {
if (ObjTable) {
pSetupStringTableDestroy (ObjTable);
}
}
//
// Update all the lines that reference other OBJs. This includes GROUP,
// DEPEND and COMPANIONFILE lines.
//
return Result && pUpdateObjReferences (TablePtr);
}
PVOID
pBuildObjectIdTable (
IN PSETUPTABLE TablePtr,
OUT PUINT FirstLinePtr, OPTIONAL
OUT PUINT LastLinePtr OPTIONAL
)
{
PVOID ObjIds;
UINT FirstLine, LastLine;
UINT Line;
UINT Obj;
UINT MaxObj;
PCTSTR EntryStr;
TCHAR NumBuf[32];
MaxObj = TablePtr->MaxObj;
//
// Alloc string table
//
ObjIds = pSetupStringTableInitializeEx (sizeof (DWORD), 0);
if (!ObjIds) {
LOG ((LOG_ERROR, "STF: Can't init string table"));
return NULL;
}
//
// Fill string table with list of ObjIDs
//
FirstLine = 0;
LastLine = TablePtr->LineCount;
for (Line = 0 ; Line < LastLine ; Line++) {
if (!pGetNonEmptyTableEntry (TablePtr, Line, COLUMN_OBJECT_ID, NULL, &EntryStr)) {
continue;
}
Obj = _ttoi (EntryStr);
if (Obj < 1 || Obj > MaxObj) {
continue;
}
if (!FirstLine) {
FirstLine = Line;
}
wsprintf (NumBuf, TEXT("%u"), Obj);
if (-1 == pSetupStringTableAddStringEx (
ObjIds,
NumBuf,
STRTAB_CASE_SENSITIVE,
(PBYTE) &Line,
sizeof (DWORD)
)) {
LOG ((LOG_ERROR, "STF: Can't add to string table"));
break;
}
}
if (FirstLinePtr) {
*FirstLinePtr = FirstLine;
}
if (LastLinePtr) {
*LastLinePtr = LastLine;
}
return ObjIds;
}
BOOL
pIsObjIdValid (
IN PVOID ObjIdTable,
IN UINT ObjId,
OUT PUINT Line OPTIONAL
)
{
TCHAR NumBuf[32];
LONG rc;
DWORD LineData;
wsprintf (NumBuf, TEXT("%u"), ObjId);
rc = pSetupStringTableLookUpStringEx (
ObjIdTable,
NumBuf,
STRTAB_CASE_SENSITIVE|STRTAB_BUFFER_WRITEABLE,
(PBYTE) &LineData,
sizeof (DWORD)
);
if (Line && rc != -1) {
*Line = LineData;
}
return rc != -1;
}
PCTSTR
pValidateGroup (
IN PSETUPTABLE TablePtr,
IN PCTSTR EntryStr,
IN PVOID ObjIds
)
/*++
Routine Description:
pValidateGroup parses all object IDs in the EntryStr, compares them against
the string table ObjIds, and adds only those IDs that are in both EntryStr
and ObjIds. The caller receives a text pool string, which may be
empty.
The buffer must be freed by the caller with FreeText.
Arguments:
TablePtr - Specifies the setup table being processed
EntryStr - Specifies the entry string that contains zero or more numeric
object ID references, sparated by spaces.
ObjIds - Specifies the string table of valid object IDs.
Return Value:
A pointer to the validated object ID string, or NULL if memory allocation
failed.
--*/
{
PTSTR Buffer;
PTSTR p;
PCTSTR q;
CHARTYPE ch;
UINT Obj;
TCHAR NumBuf[32];
//
// Validate EntryStr
//
Buffer = AllocText (CharCount (EntryStr) + 1);
if (!Buffer) {
return NULL;
}
p = Buffer;
*p = 0;
q = EntryStr;
while (*q) {
ch = (CHARTYPE)_tcsnextc (q);
if (ch >= TEXT('0') && ch <= TEXT('9')) {
//
// Extract object ID reference
//
Obj = 0;
for (;;) {
ch = (CHARTYPE)_tcsnextc (q);
if (ch >= TEXT('0') && ch <= TEXT('9')) {
Obj = Obj * 10 + (ch - TEXT('0'));
} else {
break;
}
q = _tcsinc (q);
}
//
// If match found, add obj ID to data
//
if (pIsObjIdValid (ObjIds, Obj, NULL)) {
wsprintf (NumBuf, TEXT("%u"), Obj);
p = _tcsappend (p, NumBuf);
}
} else {
_copytchar (p, q);
p = _tcsinc (p);
*p = 0;
q = _tcsinc (q);
}
}
return Buffer;
}
BOOL
pUpdateObjReferences (
IN PSETUPTABLE TablePtr
)
/*++
Routine Description:
pUpdateObjReferences scans the specified table for GROUP, DEPEND and COMPANIONFILE
lines, and for each line found, the line is updated.
In the case of a GROUP line, the data argument is updated if it points to one or
more invalid object IDs. If the cleanup operation causes a group to have zero
items, the group line itself is deleted, and the update is restarted.
In the case of a DEPEND line, if the first object ID no longer exists, then
the line is deleted, and the update is restarted. If an obj in the group
following the ? no longer exists, then the obj reference is deleted. If the
delete causes no objects to be listed, then the line is deleted.
In the case of a COMPANIONFILE line, the object ID is extracted from the data
argument, and the line is deleted if its original line is gone.
NOTE: This routine has a lot of exit conditions that cause leaks, but all of them
can only be hit by memory allocation failures
Arguments:
TablePtr - Specifies the setup table file to process
Return Value:
TRUE if processing was successful, or FALSE if an error occured. FALSE will cause
STF processing to fail for the current STF, and will likely generate an error log
entry.
--*/
{
UINT Line;
PVOID ObjIds;
PCTSTR EntryStr;
UINT Obj;
BOOL b = FALSE;
UINT FirstLine, LastLine;
PTSTR Buffer;
PTSTR DependBuf;
PTSTR p;
BOOL StartOver;
PTABLEENTRY Entry;
BOOL GroupMode;
BOOL CompanionFileMode;
BOOL DependMode;
do {
StartOver = FALSE;
ObjIds = pBuildObjectIdTable (TablePtr, &FirstLine, &LastLine);
if (!ObjIds) {
return FALSE;
}
Line = TablePtr->LineCount;
if (!FirstLine) {
//
// Small table -- no object IDs at all! Return TRUE to caller.
//
b = TRUE;
Line = 0;
}
//
// Look for lines that have object ID references
//
if (Line == TablePtr->LineCount) {
for (Line = FirstLine ; !StartOver && Line < LastLine ; Line++) {
if (!pGetNonEmptyTableEntry (TablePtr, Line, COLUMN_COMMAND, NULL, &EntryStr)) {
continue;
}
GroupMode = StringIMatch (EntryStr, TEXT("Group"));
CompanionFileMode = StringIMatch (EntryStr, TEXT("CompanionFile"));
DependMode = StringIMatch (EntryStr, TEXT("Depend"));
if (!GroupMode && !CompanionFileMode && !DependMode) {
continue;
}
if (!pGetNonEmptyTableEntry (TablePtr, Line, COLUMN_OBJECT_DATA, NULL, &EntryStr)) {
continue;
}
if (GroupMode) {
Buffer = (PTSTR) pValidateGroup (TablePtr, EntryStr, ObjIds);
if (!Buffer) {
break;
}
//
// If Buffer is empty, delete the group line, then start over
//
if (*Buffer == 0) {
pDeleteStfLine (TablePtr, Line);
StartOver = TRUE;
DEBUGMSG ((
DBG_STF,
"Group line %u references only deleted lines, so it was deleted as well.",
Line
));
}
//
// If Buffer is not empty, replace the data on the current line
//
else if (!StringMatch (EntryStr, Buffer)) {
DEBUGMSG ((
DBG_STF,
"Group has reference to one or more deleted objects. Original: %s New: %s",
EntryStr,
Buffer
));
Entry = GetTableEntry (TablePtr, Line, COLUMN_OBJECT_DATA, NULL);
MYASSERT (Entry);
if (Entry) {
if (!ReplaceTableEntryStr (TablePtr, Entry, Buffer)) {
break;
}
}
}
FreeText (Buffer);
}
if (!StartOver && (DependMode || CompanionFileMode)) {
//
// Extract the obj ID from the data arg
//
Obj = _ttoi (EntryStr);
if (Obj || EntryStr[0] == TEXT('0')) {
if (!pIsObjIdValid (ObjIds, Obj, NULL)) {
//
// CompanionFile/Depend is for a line that was deleted. Delete
// the line and start over.
//
pDeleteStfLine (TablePtr, Line);
StartOver = TRUE;
DEBUGMSG_IF ((
CompanionFileMode,
DBG_STF,
"CompanionFile line %u references a deleted line %u, so it was deleted as well.",
Line,
Obj
));
DEBUGMSG_IF ((
DependMode,
DBG_STF,
"Depend line %u references a deleted line %u, so it was deleted as well.",
Line,
Obj
));
}
}
}
if (!StartOver && DependMode) {
//
// Go beyond question mark, then validate group
//
p = _tcschr (EntryStr, TEXT('?'));
if (p) {
p = _tcsinc (p);
while (*p == TEXT(' ')) {
p++;
}
Buffer = (PTSTR) pValidateGroup (TablePtr, p, ObjIds);
if (!Buffer) {
break;
}
if (*Buffer == 0) {
pDeleteStfLine (TablePtr, Line);
StartOver = TRUE;
DEBUGMSG ((
DBG_STF,
"Depend line %u references only deleted lines, so it was deleted as well.",
Line
));
}
//
// If Buffer is not empty, replace the data on the current line
//
else if (!StringMatch (p, Buffer)) {
DependBuf = AllocText (ByteCount (Buffer) + 32);
if (!DependBuf) {
break;
}
StringCopyAB (DependBuf, EntryStr, p);
StringCat (DependBuf, Buffer);
DEBUGMSG ((
DBG_STF,
"Depend line has reference to one or more deleted objects. Original: %s New: %s",
EntryStr,
DependBuf
));
Entry = GetTableEntry (TablePtr, Line, COLUMN_OBJECT_DATA, NULL);
MYASSERT (Entry);
if (Entry) {
if (!ReplaceTableEntryStr (TablePtr, Entry, DependBuf)) {
break;
}
}
FreeText (DependBuf);
}
FreeText (Buffer);
}
}
}
//
// If we managed to get through the loop, we are done! Return TRUE to caller.
//
if (Line == LastLine) {
b = TRUE;
}
}
pSetupStringTableDestroy (ObjIds);
} while (StartOver);
return b;
}
BOOL
pGetNonEmptyTableEntry (
IN PSETUPTABLE TablePtr,
IN UINT Line,
IN UINT Col,
OUT PTABLEENTRY *EntryPtr, OPTIONAL
OUT PCTSTR *EntryStr
)
/*++
Routine Description:
pGetNonEmptyTableEntry is a wrapper routine that gets an entry in the
STF table and returns TRUE only if the string actually exists and is
not empty. If a non-empty string is found, the pointer is returned to
the caller.
Arguments:
TablePtr - Specifies the setup table file to process
Line - Specifies the line to get the entry for
Col - Specifies the column on the line to get the entry for
EntryPtr - Receives a pointer to the entry struct
EntryStr - Receives a pointer to the entry string
Return Value:
TRUE if entry exists and is not empty, FALSE if the entry does not exist
or is empty.
--*/
{
PCTSTR String;
PTABLEENTRY Entry;
Entry = GetTableEntry (TablePtr, Line, Col, &String);
if (!Entry) {
return FALSE;
}
if (!String || !String[0]) {
return FALSE;
}
if (EntryPtr) {
*EntryPtr = Entry;
}
if (EntryStr) {
*EntryStr = String;
}
return TRUE;
}
PCTSTR
pQuoteThis (
IN PCTSTR String
)
{
static TCHAR Buffer[128];
MYASSERT (ByteCount (String) < (sizeof (Buffer) - 2));
Buffer[0] = TEXT('\"');
StringCopy (&Buffer[1], String);
StringCat (Buffer, TEXT("\""));
return Buffer;
}
BOOL
pProcessSectionCommand (
IN OUT PSETUPTABLE TablePtr,
IN UINT StfLine,
IN PCTSTR InfSection,
IN PCTSTR InstallDestDir
)
/*++
Routine Description:
pProcessSectionCommand scans an INF section, determining which files
are deleted, moved or unchanged. If a file is moved or unchanged,
it is added to memdb. After the INF section is completely scanned,
the memdb structure is processed, causing additional INF sections
to be generated if any changes were made to the file paths.
Arguments:
TablePtr - Specifies the setup table file to process
StfLine - Specifies the STF line that has a CopySection or RemoveSection
command.
InfSection - Specifies the INF section that lists files to be processed
InstallDestDir - Specifies destination directory specified by STF line
Return Value:
TRUE if success, FALSE if failure.
--*/
{
BOOL DeletedOrMoved = FALSE;
PSTFINFLINE InfLine;
PCTSTR * InfFields;
UINT Fields;
TCHAR FileName[MAX_TCHAR_PATH];
TCHAR FullPath[MAX_TCHAR_PATH * 2];
MEMDB_ENUM e;
CONVERTPATH_RC rc;
BOOL FirstSectionDone;
BOOL CreatedFlag;
PSTFINFSECTION NewInfSection;
PSTFINFLINE SrcInfLine;
PTSTR UpdatedData;
TCHAR DirPart[MAX_TCHAR_PATH];
PCTSTR FilePart;
PTSTR p, q;
PTABLEENTRY DataEntry;
PCTSTR *Array;
//
// Step one: scan all files in the corresponding INF section,
// moving them to memdb.
//
InfLine = StfGetFirstLineInSectionStr (TablePtr, InfSection);
if (!InfLine) {
//
// Workaround: sometimes the people who write the STFs embed all kinds of
// quotes in a section name.
//
Array = ParseCommaList (TablePtr, InfSection);
if (Array) {
if (Array[0]) {
InfLine = StfGetFirstLineInSectionStr (TablePtr, Array[0]);
}
FreeCommaList (TablePtr, Array);
}
}
if (!InfLine) {
MYASSERT(InfSection);
DEBUGMSG ((DBG_STF, "STF file references section %s that does not exist", InfSection));
return TRUE;
}
__try {
do {
//
// Parse the INF line into fields
//
InfFields = ParseCommaList (TablePtr, InfLine->Data);
if (!InfFields) {
MYASSERT(InfLine->Data);
DEBUGMSG ((DBG_WARNING, "INF file has non-parsable data", InfLine->Data));
} else {
for (Fields = 0 ; InfFields[Fields] ; Fields++) {
}
if (Fields < 19) {
MYASSERT(InfLine->Data);
DEBUGMSG ((DBG_WARNING, "INF file line %s has less than 19 fields", InfLine->Data));
} else {
//
// Get the file name from this INF line (field #2)
//
pGetFileNameFromInfField (FileName, InfFields[1]);
StringCopy (FullPath, InstallDestDir);
StringCat (AppendPathWack (FullPath), FileName);
rc = ConvertWin9xPath (FullPath);
if (rc != CONVERTPATH_NOT_REMAPPED) {
DeletedOrMoved = TRUE;
}
if (rc != CONVERTPATH_DELETED) {
//
// Add this file to memdb
//
if (!MemDbSetValueEx (
MEMDB_CATEGORY_STF_TEMP,
FullPath,
NULL,
NULL,
(DWORD) InfLine,
NULL
)) {
LOG ((LOG_ERROR, "STF: MemDbSetValueEx failed"));
return FALSE;
}
}
}
FreeCommaList (TablePtr, InfFields);
}
InfLine = StfGetNextLineInSection (InfLine);
} while (InfLine);
if (!DeletedOrMoved) {
//
// No changes necessary
//
return TRUE;
}
//
// Now write out each unique directory to the INF. Update
// the STF line to point to the first new INF section.
//
FirstSectionDone = FALSE;
if (MemDbGetValueEx (&e, MEMDB_CATEGORY_STF_TEMP, NULL, NULL)) {
do {
//
// Name gives full path of new file location.
// Value points to INF line that is to be copied.
//
NewInfSection = pGetNewInfSection (TablePtr, e.szName, &CreatedFlag);
if (!NewInfSection) {
LOG ((LOG_ERROR, "Process Section Command failed because Get New Inf Section failed"));
return FALSE;
}
SrcInfLine = (PSTFINFLINE) e.dwValue;
_tcssafecpy (DirPart, e.szName, MAX_TCHAR_PATH);
FilePart = GetFileNameFromPath (DirPart);
MYASSERT (FilePart && FilePart > DirPart);
*_tcsdec2 (DirPart, FilePart) = 0;
//
// File name may have changed. If so, specify the file name in the
// angle brackets.
//
UpdatedData = DuplicatePathString (
SrcInfLine->Data,
SizeOfString (FilePart) + 2 * sizeof (TCHAR)
);
p = _tcschr (SrcInfLine->Data, TEXT(','));
MYASSERT(p);
p = _tcsinc (p);
q = _tcschr (p, TEXT(','));
MYASSERT(q);
p = _tcschr (p, TEXT('<'));
if (!p || p > q) {
p = q;
}
StringCopyAB (UpdatedData, SrcInfLine->Data, q);
wsprintf (_tcschr (UpdatedData, 0), TEXT("<%s>"), FilePart);
StringCat (UpdatedData, q);
DEBUGMSG ((DBG_STF, "INF changed from %s to %s", SrcInfLine->Data, UpdatedData));
StfAddInfLineToTable (TablePtr, NewInfSection, SrcInfLine->Key, UpdatedData, SrcInfLine->LineFlags);
//
// If first section, update STF line to use new section
//
if (!FirstSectionDone) {
DataEntry = GetTableEntry (TablePtr, StfLine, COLUMN_OBJECT_DATA, NULL);
if (!ReplaceTableEntryStr (TablePtr, DataEntry, pQuoteThis (NewInfSection->Name))) {
LOG ((LOG_ERROR, "Could not update table entry"));
return FALSE;
}
FirstSectionDone = TRUE;
}
//
// If not first section and CreateFlag is TRUE, create a new STF line
// and point it to new INF section.
//
else if (CreatedFlag) {
if (!pCreateNewStfLine (TablePtr, StfLine, pQuoteThis (NewInfSection->Name), DirPart)) {
LOG ((LOG_ERROR, "Could not create a new line"));
return FALSE;
}
}
} while (MemDbEnumNextValue (&e));
} else {
//
// All files were deleted, and this STF line is no longer needed.
//
DEBUGMSG ((DBG_STF, "STF Line %u is no longer needed", StfLine));
if (!pReplaceDirReferences (TablePtr, StfLine, InstallDestDir)) {
return FALSE;
}
if (!pDeleteStfLine (TablePtr, StfLine)) {
return FALSE;
}
}
}
__finally {
MemDbDeleteTree (MEMDB_CATEGORY_STF_TEMP);
MemDbDeleteTree (MEMDB_CATEGORY_STF_SECTIONS);
}
return TRUE;
}
VOID
pGenerateUniqueKeyName (
IN PSETUPTABLE TablePtr,
IN PSTFINFSECTION Section,
IN PCTSTR Root,
OUT PTSTR UniqueKey
)
{
UINT Sequencer = 0;
PTSTR p;
UniqueKey[0] = 0;
p = _tcsappend (UniqueKey, Root);
for (;;) {
Sequencer++;
wsprintf (p, TEXT("%03u"), Sequencer);
if (!StfFindLineInInfSection (TablePtr, Section, UniqueKey)) {
break;
}
}
}
BOOL
pProcessLineCommand (
IN OUT PSETUPTABLE TablePtr,
IN UINT StfLine,
IN PCTSTR InfSection,
IN PCTSTR InfKey,
IN PCTSTR InstallDestDir
)
/*++
Routine Description:
pProcessLineCommand determinins if the file assoicated with the command
was deleted, moved or unchanged. If the file was deleted, the STF line
is deleted. If the file was moved, the STF line is adjusted. If the
file has no change, the routine does not modify the STF table.
Arguments:
TablePtr - Specifies the setup table file to process
StfLine - Specifies the STF line that has a CopySection or RemoveSection
command.
InfSection - Specifies the INF section that lists the file to be processed
InfKey - Specifies the INF key in InfSection identifing the file
InstallDestDir - Specifies destination directory specified by STF line
Return Value:
TRUE if success, FALSE if failure.
--*/
{
PSTFINFSECTION Section;
PSTFINFLINE InfLine;
PCTSTR *InfFields;
UINT Fields;
TCHAR FileName[MAX_TCHAR_PATH];
TCHAR FullPath[MAX_TCHAR_PATH * 2];
CONVERTPATH_RC rc;
TCHAR OrgDirPart[MAX_TCHAR_PATH];
PCTSTR OrgFilePart;
TCHAR DirPart[MAX_TCHAR_PATH];
PCTSTR FilePart;
PTABLEENTRY DataEntry;
PTABLEENTRY FileEntry;
PCTSTR *Array;
TCHAR NewKeyName[MAX_TCHAR_PATH];
PTSTR NewLine;
PTSTR p;
UINT Size;
PCTSTR OldField;
Section = StfFindInfSectionInTable (TablePtr, InfSection);
if (!Section) {
//
// Workaround: sometimes the people who write the STFs embed all kinds of
// quotes in a section name.
//
Array = ParseCommaList (TablePtr, InfSection);
if (Array) {
if (Array[0]) {
Section = StfFindInfSectionInTable (TablePtr, Array[0]);
}
FreeCommaList (TablePtr, Array);
}
}
if (!Section) {
MYASSERT(InfSection);
DEBUGMSG ((
DBG_STF,
"STF has reference to non-existent INF section ([%s])",
InfSection
));
return TRUE;
}
InfLine = StfFindLineInInfSection (TablePtr, Section, InfKey);
if (!InfLine) {
MYASSERT(InfSection && InfKey);
DEBUGMSG ((
DBG_STF,
"STF has reference to non-existent INF key ([%s], %s)",
InfSection,
InfKey
));
return TRUE;
}
//
// Build full path
//
InfFields = ParseCommaList (TablePtr, InfLine->Data);
__try {
if (!InfFields) {
MYASSERT(InfLine->Data);
DEBUGMSG ((DBG_WARNING, "INF file has non-parsable data", InfLine->Data));
return TRUE;
}
for (Fields = 0 ; InfFields[Fields] ; Fields++) {
/* empty */
}
if (Fields < 19) {
MYASSERT(InfLine->Data);
DEBUGMSG ((DBG_WARNING, "INF file line %s has less than 19 fields", InfLine->Data));
return TRUE;
}
//
// Get the file name from this INF line (field #2)
//
pGetFileNameFromInfField (FileName, InfFields[1]);
StringCopy (FullPath, InstallDestDir);
StringCopy (AppendPathWack (FullPath), FileName);
}
__finally {
FreeCommaList (TablePtr, InfFields);
}
//
// Determine mapping
//
_tcssafecpy (OrgDirPart, FullPath, MAX_TCHAR_PATH);
OrgFilePart = GetFileNameFromPath (OrgDirPart);
if (OrgFilePart <= OrgDirPart) {
// File probably wasn't installed
return TRUE;
}
*_tcsdec2 (OrgDirPart, OrgFilePart) = 0;
rc = ConvertWin9xPath (FullPath);
_tcssafecpy (DirPart, FullPath, MAX_TCHAR_PATH);
FilePart = GetFileNameFromPath (DirPart);
MYASSERT (FilePart && FilePart > DirPart);
*_tcsdec2 (DirPart, FilePart) = 0;
//
// Deleted? Delete the STF line.
//
if (rc == CONVERTPATH_DELETED) {
DEBUGMSG ((DBG_STF, "STF Line %u is no longer needed", StfLine));
if (!pReplaceDirReferences (TablePtr, StfLine, InstallDestDir)) {
return FALSE;
}
if (!pDeleteStfLine (TablePtr, StfLine)) {
return FALSE;
}
}
//
// Moved? Update the STF line.
//
else if (rc == CONVERTPATH_REMAPPED) {
//
// Has the file name changed? If so, point it to the new location.
//
if (!StringIMatch (OrgFilePart, FilePart)) {
//
// Update INI file by duplicating the INI line
//
// Generate a unique key name
pGenerateUniqueKeyName (TablePtr, Section, TEXT("WIN9XUPG_"), NewKeyName);
// Compute size needed
Size = 0;
for (Fields = 0 ; InfFields[Fields] ; Fields++) {
if (Fields != 1) {
Size += ByteCount (InfFields[Fields]);
Size += sizeof (TCHAR);
} else {
Size += ByteCount (FilePart);
}
}
// Generate the INF line
NewLine = AllocText (Size);
if (NewLine) {
p = NewLine;
*p = 0;
for (Fields = 0 ; InfFields[Fields] ; Fields++) {
if (Fields) {
p = _tcsappend (p, TEXT(","));
}
if (Fields == 1) {
p = _tcsappend (p, FilePart);
} else {
p = _tcsappend (p, InfFields[Fields]);
}
}
// Write the new line
StfAddInfLineToTable (
TablePtr,
Section,
NewKeyName,
NewLine,
LINEFLAG_KEY_QUOTED
);
FreeText (NewLine);
// Update the STF
FileEntry = GetTableEntry (TablePtr, StfLine, COLUMN_OBJECT_DATA, NULL);
MYASSERT (FileEntry);
OldField = GetTableEntryStr (TablePtr, FileEntry);
NewLine = AllocText (
ByteCount (FilePart) +
ByteCount (OldField) +
ByteCount (InfSection)
);
StringCopy (NewLine, OldField);
p = _tcschr (NewLine, TEXT(':'));
if (!p) {
p = NewLine;
} else {
p = _tcsinc (p);
MYASSERT (*p == TEXT(' '));
p = _tcsinc (p);
}
*p = 0;
p = _tcsappend (p, InfSection);
p = _tcsappend (p, TEXT(", "));
p = _tcsappend (p, NewKeyName);
// ignore memory failure, it will be picked up below
ReplaceTableEntryStr (TablePtr, FileEntry, NewLine);
FreeText (NewLine);
}
}
//
// Store the directory change in the STF table.
//
DataEntry = GetTableEntry (TablePtr, StfLine, COLUMN_INSTALL_DESTDIR, NULL);
AppendWack (DirPart);
if (!ReplaceTableEntryStr (TablePtr, DataEntry, DirPart)) {
LOG ((
LOG_ERROR,
"Could not update table entry for single command"
));
return FALSE;
}
}
return TRUE;
}
PSTFINFSECTION
pGetNewInfSection (
IN PSETUPTABLE TablePtr,
IN PCTSTR FileSpec,
OUT PBOOL CreatedFlag
)
/*++
Routine Description:
pGetNewInfSection determines if a section already exists for the specified
file specification, and if so it returns the pointer to the existing
section. If the section does not exist, it creates a new section (making
sure there are no other sections with the same name), and returns a
pointer to the new section.
This routine is used by the code that splits one section into several.
Arguments:
TablePtr - Specifies the setup table file to process
FileSpec - Specifies the full file path of the file being processed
CreatedFlag - Receives TRUE if a new section had to be created
Return Value:
A pointer to the INF section in which the file should be added to, or
NULL if an error occurred.
--*/
{
TCHAR DirName[MAX_TCHAR_PATH];
TCHAR Node[MEMDB_MAX];
DWORD SectionNum;
PTSTR p;
TCHAR SectionName[64];
static DWORD SectionSeq = 0;
*CreatedFlag = FALSE;
//
// See if section already exists, and if it does, return the section
// pointer.
//
_tcssafecpy (DirName, FileSpec, MAX_TCHAR_PATH);
p = _tcsrchr (DirName, TEXT('\\'));
if (p) {
*p = 0;
}
MemDbBuildKey (Node, MEMDB_CATEGORY_STF_SECTIONS, DirName, NULL, NULL);
if (MemDbGetValue (Node, &SectionNum)) {
wsprintf (SectionName, S_SECTIONNAME_SPRINTF, SectionNum);
return StfFindInfSectionInTable (TablePtr, SectionName);
}
//
// The section does not exist. Find an unused section, write the
// reference to memdb and return the section pointer.
//
while (TRUE) {
SectionSeq++;
wsprintf (SectionName, S_SECTIONNAME_SPRINTF, SectionSeq);
if (!StfFindInfSectionInTable (TablePtr, SectionName)) {
break;
}
}
*CreatedFlag = TRUE;
MemDbSetValue (Node, SectionSeq);
return StfAddInfSectionToTable (TablePtr, SectionName);
}
VOID
pGetFileNameFromInfField (
OUT PTSTR FileName,
IN PCTSTR InfField
)
/*++
Routine Description:
pGetFileNameFromInfField extracts a long file name, enclosed between
angle-brackets. According to the STF spec, the syntax is shortname<longname>.
This routine returns longname.
Arguments:
FileName - Supplies a MAX_TCHAR_PATH buffer that receives the long file name.
InfField - Specifies the text from the INF field conforming to the
shortname<longname> syntax.
Return Value:
None.
--*/
{
PTSTR LongName;
PCTSTR p;
LongName = _tcschr (InfField, TEXT('<'));
if (LongName) {
_tcssafecpy (FileName, _tcsinc (LongName), MAX_TCHAR_PATH);
LongName = _tcschr (FileName, TEXT('>'));
if (LongName) {
*LongName = 0;
}
} else {
p = _tcsrchr (InfField, TEXT('\\'));
if (!p) {
p = InfField;
} else {
p = _tcsinc (p);
}
_tcssafecpy (FileName, p, MAX_TCHAR_PATH);
}
}
BOOL
pDeleteStfLine (
IN OUT PSETUPTABLE TablePtr,
IN UINT StfLine
)
/*++
Routine Description:
pDeleteStfLine removes an STF line from the table. It first checks to
see if a destination directory is specified, and if one is, it moves it
to the next line, unless the next line also has a destination directory.
Arguments:
TablePtr - Specifies the setup table file to process
StfLine - Specifies the STF line to delete
Return Value:
TRUE if success, FALSE if failure.
--*/
{
PTABLEENTRY TitleEntry;
PTABLEENTRY DataEntry;
BOOL b;
//
// We simply replace the command with CreateIniLine, which is harmless
//
TitleEntry = GetTableEntry (TablePtr, StfLine, COLUMN_COMMAND, NULL);
DataEntry = GetTableEntry (TablePtr, StfLine, COLUMN_OBJECT_DATA, NULL);
if (!TitleEntry || !DataEntry) {
MYASSERT (FALSE);
return TRUE;
}
b = ReplaceTableEntryStr (TablePtr, TitleEntry, TEXT("CreateIniLine"));
if (b) {
b = ReplaceTableEntryStr (
TablePtr,
DataEntry,
TEXT("\"WIN.INI\", \"Old Win9x Setting\", \"DummyKey\", \"unused\"")
);
}
return b;
#if 0
PTABLEENTRY NextLineEntry;
PCTSTR InstallDestDir;
PCTSTR NextInstallDestDir;
__try {
//
// Test for last line
//
if (StfLine + 1 >= TablePtr->LineCount) {
__leave;
}
//
// Obtain StfLine dest dir (column 10)
//
if (!GetTableEntry (TablePtr, StfLine, COLUMN_DEST_DIR, &InstallDestDir)) {
//
// StfLine is not valid (unexpected)
//
DEBUGMSG ((DBG_STF, "Line %u does not have column 10", StfLine));
__leave;
}
//
// If no dest dir, do not modify the next line
//
if (!InstallDestDir || !InstallDestDir[0]) {
__leave;
}
//
// Obtain next line's dest dir (column 10)
//
NextLineEntry = GetTableEntry (TablePtr, StfLine + 1, COLUMN_DEST_DIR, &NextInstallDestDir);
if (!NextLineEntry) {
//
// Next StfLine is not valid (unexpected)
//
DEBUGMSG ((DBG_WHOOPS, "pDeleteStfLine: Next line %u does not have column 10", StfLine+1));
__leave;
}
//
// If next line's dest dir is not empty, do not modify the line
//
if (NextInstallDestDir && NextInstallDestDir[0]) {
__leave;
}
//
// Now set InstallDestDir on NextLineEntry line
//
if (!ReplaceTableEntryStr (TablePtr, NextLineEntry, InstallDestDir)) {
DEBUGMSG ((
DBG_ERROR,
"Cannot replace a destination dir in STF file. "
"Line=%u, InstallDestDir=%s",
StfLine + 1,
InstallDestDir
));
return FALSE;
}
}
__finally {
}
return DeleteLineInTable (TablePtr, StfLine);
#endif
}
BOOL
pReplaceDirReferences (
IN OUT PSETUPTABLE TablePtr,
IN UINT StfLine,
IN PCTSTR DirSpec
)
/*++
Routine Description:
pReplaceDirReferences scans column 14, looking for all references
to StfLine, and replaces the reference with DirSpec.
Arguments:
TablePtr - Specifies the setup table file to process
StfLine - Specifies the STF line to substitute
DirSpec - Specifies the effective directory for the STF line.
This directory is used if the STF line has to be
deleted.
Return Value:
TRUE if success, FALSE if failure.
--*/
{
UINT Line, Count;
PTABLEENTRY InstallDestDirEntry;
PCTSTR InstallDestDir;
TCHAR NumStr[32];
UINT NumStrLen;
PCTSTR AfterPercentNum;
CHARTYPE c;
PCTSTR NewInstallDestDir;
NumStrLen = wsprintf (NumStr, TEXT("%%%u"), StfLine);
Count = TablePtr->LineCount;
for (Line = 0 ; Line < Count ; Line++) {
InstallDestDirEntry = GetTableEntry (TablePtr, Line, COLUMN_DEST_DIR, &InstallDestDir);
if (!InstallDestDirEntry) {
continue;
}
//
// Does InstallDestDir have %<n> (where <n> equals StfLine)?
//
if (StringIMatchCharCount (InstallDestDir, NumStr, NumStrLen)) {
//
// The next character must be a wack or nul
//
AfterPercentNum = CharCountToPointer (InstallDestDir, NumStrLen);
c = (CHARTYPE)_tcsnextc (AfterPercentNum);
if (c == 0 || c == TEXT('\\')) {
//
// Create new dest dir
//
if (c) {
NewInstallDestDir = JoinPaths (DirSpec, _tcsinc (AfterPercentNum));
} else {
NewInstallDestDir = DuplicatePathString (DirSpec, 0);
}
__try {
if (!ReplaceTableEntryStr (TablePtr, InstallDestDirEntry, NewInstallDestDir)) {
LOG ((
LOG_ERROR,
"Cannot replace a destination dir in STF file. "
"Line=%u, NewInstallDestDir=%s",
Line,
NewInstallDestDir
));
return FALSE;
}
ELSE_DEBUGMSG ((
DBG_STF,
"Line %u: Dest dir %s replaced with %s",
Line,
InstallDestDir,
NewInstallDestDir
));
}
__finally {
FreePathString (NewInstallDestDir);
}
}
}
}
return TRUE;
}
BOOL
pCreateNewStfLine (
IN OUT PSETUPTABLE TablePtr,
IN UINT StfLine,
IN PCTSTR ObjectData,
IN PCTSTR InstallDestDir
)
/*++
Routine Description:
pCreateNewStfLine inserts a new line immediately following
StfLine, using the maximum object number. It copies the
STF line specified and modifies the ObjectData (column 5)
and InstallDestDir (column 14).
Arguments:
TablePtr - Specifies the setup table file to process
StfLine - Specifies the prototype STF line
ObjectData - Specifies the replacement for the ObjectData
column
InstallDestDir - Specifies the replacement for the DestDir
column
Return Value:
TRUE if success, FALSE if failure.
--*/
{
UINT NewLine;
UINT NewObj;
PTABLEENTRY CopyEntry, NewEntry;
UINT Col;
TCHAR Buf[32], ReplaceBuf[32];
//
// Copy StfLine to StfLine+1, updating fields as necessary
//
NewLine = StfLine + 1;
TablePtr->MaxObj++;
NewObj = TablePtr->MaxObj;
if (!InsertEmptyLineInTable (TablePtr, NewLine)) {
LOG ((LOG_ERROR, "Unable to insert new line in STF table"));
return FALSE;
}
Col = 0;
while (TRUE) {
CopyEntry = GetTableEntry (TablePtr, StfLine, Col, NULL);
if (!CopyEntry) {
break;
}
if (!AppendTableEntry (TablePtr, NewLine, CopyEntry)) {
LOG ((LOG_ERROR, "Unable to append all entries to line"));
return FALSE;
}
NewEntry = GetTableEntry (TablePtr, NewLine, Col, NULL);
MYASSERT(NewEntry);
if (!NewEntry) {
return FALSE;
}
if (Col == 0) {
wsprintf (Buf, TEXT("%u"), NewObj);
if (!ReplaceTableEntryStr (TablePtr, NewEntry, Buf)) {
LOG ((LOG_ERROR, "Unable to replace ObjID on line"));
return FALSE;
}
} else if (Col == COLUMN_OBJECT_DATA) {
if (!ReplaceTableEntryStr (TablePtr, NewEntry, ObjectData)) {
LOG ((LOG_ERROR, "Unable to replace ObjectData on line"));
return FALSE;
}
} else if (Col == COLUMN_INSTALL_DESTDIR) {
if (!ReplaceTableEntryStr (TablePtr, NewEntry, InstallDestDir)) {
LOG ((LOG_ERROR, "Unable to replace ObjectData on line"));
return FALSE;
}
}
Col++;
}
//
// Find all lines with references to StfLine and add in NewLine
//
wsprintf (Buf, TEXT("%u"), StfLine);
wsprintf (ReplaceBuf, TEXT("%u %u"), StfLine, NewLine);
return pSearchAndReplaceObjectRefs (TablePtr, Buf, ReplaceBuf);
}
BOOL
pSearchAndReplaceObjectRefs (
IN OUT PSETUPTABLE TablePtr,
IN PCTSTR SrcStr,
IN PCTSTR DestStr
)
/*++
Routine Description:
pSearchAndReplaceObjectRefs scans column 5 of the setup table,
looking for any occurance of SrcStr and replacing it with
DestStr.
Arguments:
TablePtr - Specifies the STF table to process
SrcStr - Specifies the string to locate and replace
DestStr - Specifies the replacement string
Return Value:
TRUE if the STF file was converted, FALSE if failure.
--*/
{
UINT Line;
UINT Count;
PTABLEENTRY Entry;
PCTSTR LineString;
PCTSTR UpdatedString;
Count = TablePtr->LineCount;
for (Line = 0 ; Line < Count ; Line++) {
Entry = GetTableEntry (TablePtr, Line, COLUMN_OBJECT_DATA, &LineString);
if (!Entry || !LineString || !LineString[0]) {
continue;
}
UpdatedString = StringSearchAndReplace (LineString, SrcStr, DestStr);
if (UpdatedString) {
__try {
if (!ReplaceTableEntryStr (TablePtr, Entry, UpdatedString)) {
LOG ((LOG_ERROR, "Unable to replace text on line"));
return FALSE;
}
}
__finally {
FreePathString (UpdatedString);
}
}
}
return TRUE;
}
BOOL
pProcessSetupTableFile (
IN PCTSTR StfFileSpec
)
/*++
Routine Description:
pProcessSetupTableFile performs all processing on the file specified.
Here are the steps involved in converting an STF file:
- Determine the associated INF
- Prepare the SETUPTABLE structure
- Scan the table for file-based actions
- Convert file paths used by the actions
- Convert group references when STF lines are split
- Write the modifications to disk
- Replace the original INF and STF with the new versions
Arguments:
StfFileSpec - Specifies the full file path to the STF file needing processing.
The associated INF must be in the same directory as the STF
file referencing it.
Return Value:
TRUE if the STF file was converted, FALSE if failure.
--*/
{
SETUPTABLE Table;
DWORD StfAttribs, InfAttribs;
BOOL b;
TCHAR SourceStf[MAX_TCHAR_PATH];
TCHAR DestStf[MAX_TCHAR_PATH];
TCHAR SourceInf[MAX_TCHAR_PATH];
TCHAR DestInf[MAX_TCHAR_PATH];
if (!CreateSetupTable (StfFileSpec, &Table)) {
DEBUGMSG ((DBG_STF, "ProcessSetupTableFile: Error parsing file %s.", StfFileSpec));
return FALSE;
}
__try {
//
// Process the table
//
if (!pProcessSetupTable (&Table)) {
DEBUGMSG ((DBG_STF, "ProcessSetupTableFile: Error processing table for %s.", StfFileSpec));
return FALSE;
}
//
// Write changes to temporary files
//
if (!WriteSetupTable (&Table)) {
LOG ((LOG_ERROR, "Cannot write setup table for %s.", StfFileSpec));
return FALSE;
}
//
// Copy paths before we destroy the table struct
//
_tcssafecpy (SourceStf, Table.SourceStfFileSpec, MAX_TCHAR_PATH);
_tcssafecpy (DestStf, Table.DestStfFileSpec, MAX_TCHAR_PATH);
_tcssafecpy (SourceInf, Table.SourceInfFileSpec, MAX_TCHAR_PATH);
if (Table.DestInfFileSpec) {
_tcssafecpy (DestInf, Table.DestInfFileSpec, MAX_TCHAR_PATH);
} else {
*DestInf = 0;
}
}
__finally {
DestroySetupTable (&Table);
}
//
// Replace the original files with temporary files
//
StfAttribs = GetFileAttributes (SourceStf);
if (StfAttribs != 0xffffffff) {
LONG rc;
SetFileAttributes (SourceStf, FILE_ATTRIBUTE_NORMAL);
DeleteFile (SourceStf);
rc = GetLastError();
b = OurMoveFile (DestStf, SourceStf);
if (!b) {
return FALSE;
}
SetFileAttributes (SourceStf, StfAttribs);
}
InfAttribs = GetFileAttributes (SourceInf);
if (InfAttribs != 0xffffffff && *DestInf) {
SetFileAttributes (SourceInf, FILE_ATTRIBUTE_NORMAL);
DeleteFile (SourceInf);
b = OurMoveFile (DestInf, SourceInf);
if (!b) {
return FALSE;
}
SetFileAttributes (SourceInf, InfAttribs);
}
return TRUE;
}