1030 lines
26 KiB
C
1030 lines
26 KiB
C
//----------------------------------------------------------------------------
|
||
//
|
||
// Copyright (c) 1997-1999 Microsoft Corporation
|
||
// All rights reserved.
|
||
//
|
||
// File Name:
|
||
// settngs.c
|
||
//
|
||
// Description:
|
||
//
|
||
// This file contains the support routines to deal with settings
|
||
// in answer files.
|
||
//
|
||
// This wizard does not do in-place editting of answer files.
|
||
// These apis must be used to write to the answer file.
|
||
//
|
||
// For most settings, the job is easy. Call SettingQueue_AddSetting,
|
||
// and specify the [SectionName] KeyName=Value and which queue (the
|
||
// answer file or the .udf for the case of multiple computer names).
|
||
// Check common\savefile.c for tons of examples.
|
||
//
|
||
// Be aware that on an edit, these queues are initialized with the
|
||
// settings loaded from the original file.
|
||
//
|
||
// When the user edits a script and pushes NEXT on the NewOrEdit
|
||
// page, the settings in the existing answer file and .udf are loaded
|
||
// onto the OrignalAnswerFileQueue and the OriginalUdfQueue.
|
||
//
|
||
// When the user pushes NEXT on the SaveScript page, the following
|
||
// ocurrs. (the below code is in common\save.c)
|
||
//
|
||
// Empty(AnswerFileQueue)
|
||
// Empty(UdfQueue)
|
||
//
|
||
// Copy original answer file settings to the AnswerFileQueue
|
||
// Copy original .udf settings to the UdfQueue
|
||
//
|
||
// Call common\savefile.c to enqueue all new settings
|
||
//
|
||
// Flush(AnswerFileQueue)
|
||
// Flush(UdfQueue)
|
||
//
|
||
// To support "Do not specify this setting", you must call
|
||
// SettingQueue_AddSetting with an lpValue of "". SettingQueue_Flush
|
||
// writes nothing if lpValue == "". Failure to do this results in
|
||
// the original setting being preserved in the outputed answer file.
|
||
//
|
||
// You must also ensure that mutually excluded settings are cleared
|
||
// by setting the lpValue to "". For example, if JoinWorkGroup=workgroup,
|
||
// then, make sure to set JoinDomain="", CreateComputerAccount="" etc.
|
||
//
|
||
// A section can be marked as volatile. This is needed for the network
|
||
// pages because many sections should be removed if (for example) the
|
||
// user changes from CustomNet to TypicalNet. When the queue is flushed,
|
||
// any sections still marked as volatile will not be written to the file.
|
||
// Use SettingQueue_MarkVolatile to mark a section as such. Check
|
||
// common\loadfile.c for examples.
|
||
//
|
||
// In contrast to in-place editing, being able to mark a section
|
||
// as volatile at load time means that the answer file does not have
|
||
// to be re-read to determine what should be removed at save time.
|
||
//
|
||
//----------------------------------------------------------------------------
|
||
|
||
#include "pch.h"
|
||
#include "settypes.h"
|
||
|
||
//
|
||
// Declare the queues
|
||
//
|
||
// AnswerFileQueue holds the settings to write
|
||
// UdfQueue holds the settings in case of mulitple computers
|
||
//
|
||
// OrigAnswerFileQueue holds the settings loaded on an edit
|
||
// OrigUdfFileQueue holds the settings loaded on an edit in the .udf
|
||
//
|
||
// The first 2 are "output queues"
|
||
// The next 2 are "input queues"
|
||
// The last is a queue for the HAL and SCSI OEM settings.
|
||
//
|
||
// First, we read the answer file and .udf at the NewOrEdit page and
|
||
// place each setting on the Orig*Queue's.
|
||
//
|
||
// When user is way at the end of the wizard (SaveScript page), we
|
||
// empty the AnswerFileQueue and UdfQueue and initialize it with a
|
||
// copy of the original settings. This is all necessary because we
|
||
// don't want to merge with garbage we previously put on the queue
|
||
// if the user went back and forth in the wizard alot.
|
||
//
|
||
|
||
static LINKED_LIST AnswerFileQueue = { 0 };
|
||
static LINKED_LIST UdfQueue = { 0 };
|
||
|
||
static LINKED_LIST OrigAnswerFileQueue = { 0 };
|
||
static LINKED_LIST OrigUdfQueue = { 0 };
|
||
|
||
static LINKED_LIST TxtSetupOemQueue = { 0 };
|
||
|
||
//
|
||
// Local prototypes
|
||
//
|
||
|
||
SECTION_NODE *
|
||
SettingQueue_AddSection(LPTSTR lpSection, QUEUENUM dwWhichQueue);
|
||
|
||
static SECTION_NODE *FindQueuedSection(LPTSTR lpSection,
|
||
QUEUENUM dwWhichQueue);
|
||
|
||
VOID InsertNode(LINKED_LIST *pList, PVOID pNode);
|
||
|
||
KEY_NODE* FindKey(LINKED_LIST *ListHead,
|
||
LPTSTR lpKeyName);
|
||
|
||
LINKED_LIST *SelectSettingQueue(QUEUENUM dwWhichQueue);
|
||
|
||
static BOOL IsNecessaryToQuoteString(LPTSTR p);
|
||
|
||
BOOL DoesSectionHaveKeys( SECTION_NODE *pSection );
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
//
|
||
// This section has the entry points for the wizard
|
||
//
|
||
//----------------------------------------------------------------------------
|
||
|
||
//----------------------------------------------------------------------------
|
||
//
|
||
// Function: SettingQueue_AddSetting
|
||
//
|
||
// Purpose: Queues [section] key=value in internal structs.
|
||
//
|
||
// Arguments:
|
||
// LPTSTR lpSection - name of section in .ini
|
||
// LPTSTR lpKey - name of key in section
|
||
// LPTSTR lpValue - value of setting
|
||
// QUEUENUM dwWhichQueue - which settings queue
|
||
//
|
||
// Returns:
|
||
// BOOL - fails only because of no memory
|
||
//
|
||
// Notes:
|
||
//
|
||
// - A lpValue = "" is interpreted to mean 'Do not write this setting'.
|
||
//
|
||
// - A lpKey = "" creates a [SectionName] header with no settings.
|
||
//
|
||
// - If the setting existed in the original answer file, it's value
|
||
// is updated. An assert fires if the wizard tries to set the same
|
||
// key twice.
|
||
//
|
||
//----------------------------------------------------------------------------
|
||
|
||
BOOL SettingQueue_AddSetting(LPTSTR lpSection,
|
||
LPTSTR lpKey,
|
||
LPTSTR lpValue,
|
||
QUEUENUM dwWhichQueue)
|
||
{
|
||
SECTION_NODE *pSectionNode;
|
||
KEY_NODE *pKeyNode;
|
||
|
||
//
|
||
// You have to pass a section key and value. Section name cannot
|
||
// be empty.
|
||
//
|
||
|
||
Assert(lpSection != NULL);
|
||
Assert(lpKey != NULL);
|
||
Assert(lpValue != NULL);
|
||
Assert(lpSection[0]);
|
||
|
||
//
|
||
// See if a node for this section already exists. If not, create one.
|
||
//
|
||
|
||
pSectionNode = SettingQueue_AddSection(lpSection, dwWhichQueue);
|
||
if ( pSectionNode == NULL )
|
||
return FALSE;
|
||
|
||
//
|
||
// See if this key has already been set. If not, alloc a node and
|
||
// set all of its fields except for the lpValue.
|
||
//
|
||
// If the node already exist, free the lpValue to make room for
|
||
// the new value.
|
||
//
|
||
|
||
if ( lpKey[0] == _T('\0') ||
|
||
(pKeyNode = FindKey( &pSectionNode->key_list, lpKey) ) == NULL ) {
|
||
|
||
if ( (pKeyNode=malloc(sizeof(KEY_NODE))) == NULL )
|
||
return FALSE;
|
||
|
||
if ( (pKeyNode->lpKey = lstrdup(lpKey)) == NULL )
|
||
{
|
||
free(pKeyNode);
|
||
return FALSE;
|
||
}
|
||
InsertNode(&pSectionNode->key_list, pKeyNode);
|
||
|
||
} else {
|
||
|
||
#if DBG
|
||
//
|
||
// If the wizard has already set this key once, assert.
|
||
//
|
||
|
||
if ( pKeyNode->bSetOnce ) {
|
||
AssertMsg2(FALSE,
|
||
"Section \"%S\" Key \"%S\" has already been set",
|
||
lpSection, lpKey);
|
||
}
|
||
#endif
|
||
|
||
free(pKeyNode->lpValue);
|
||
}
|
||
|
||
#if DBG
|
||
//
|
||
// If this is going to an output queue, mark this setting as
|
||
// having already been set by the wizard.
|
||
//
|
||
// Note that when the input queue is copied to the output queue,
|
||
// the copy function preserves this setting.
|
||
//
|
||
|
||
pKeyNode->bSetOnce = ( (dwWhichQueue == SETTING_QUEUE_ANSWERS) |
|
||
(dwWhichQueue == SETTING_QUEUE_UDF) );
|
||
#endif
|
||
|
||
//
|
||
// Put the (possibly new) value in
|
||
//
|
||
|
||
if ( (pKeyNode->lpValue = lstrdup(lpValue)) == NULL )
|
||
return FALSE;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------
|
||
//
|
||
// Function: SettingQueue_AddSection
|
||
//
|
||
// Purpose: Adds a section (by name) to either the answer file setting
|
||
// queue, or the .udf queue.
|
||
//
|
||
// Returns: FALSE if out of memory
|
||
//
|
||
// Notes:
|
||
//
|
||
// - If the section is already on the given list, a pointer to it
|
||
// is returned. Else a new one is created and put at the end of
|
||
// the list.
|
||
//
|
||
//----------------------------------------------------------------------------
|
||
|
||
SECTION_NODE *
|
||
SettingQueue_AddSection(LPTSTR lpSection, QUEUENUM dwWhichQueue)
|
||
{
|
||
SECTION_NODE *pSectionNode;
|
||
LINKED_LIST *pList;
|
||
|
||
//
|
||
// If it already exists, return a pointer to it.
|
||
//
|
||
// If we're modifying a section (or any of it's settings) on one
|
||
// of the output queues, we must make sure that this section is not
|
||
// marked volatile anymore.
|
||
//
|
||
|
||
pSectionNode = FindQueuedSection(lpSection, dwWhichQueue);
|
||
|
||
if ( pSectionNode != NULL ) {
|
||
|
||
if ( dwWhichQueue == SETTING_QUEUE_ANSWERS ||
|
||
dwWhichQueue == SETTING_QUEUE_UDF ) {
|
||
|
||
pSectionNode->bVolatile = FALSE;
|
||
}
|
||
|
||
return pSectionNode;
|
||
}
|
||
|
||
//
|
||
// Create the new section node. They always begin not volatile.
|
||
// Callers use MarkVolatile to mark a volatile section on in input
|
||
// queue at answer file load time.
|
||
//
|
||
|
||
if ( (pSectionNode=malloc(sizeof(SECTION_NODE))) == NULL )
|
||
return FALSE;
|
||
|
||
if ( (pSectionNode->lpSection = lstrdup(lpSection)) == NULL )
|
||
{
|
||
free(pSectionNode);
|
||
return FALSE;
|
||
}
|
||
pSectionNode->bVolatile = FALSE;
|
||
|
||
memset(&pSectionNode->key_list, 0, sizeof(pSectionNode->key_list));
|
||
|
||
//
|
||
// Put this node at the tail of the correct list.
|
||
//
|
||
pList = SelectSettingQueue(dwWhichQueue);
|
||
|
||
if ( pList != NULL )
|
||
InsertNode(pList, pSectionNode);
|
||
|
||
return pSectionNode;
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
//
|
||
// Function: SettingQueue_RemoveSection
|
||
//
|
||
// Purpose:
|
||
//
|
||
// Returns:
|
||
//
|
||
// Notes:
|
||
//
|
||
//----------------------------------------------------------------------------
|
||
VOID
|
||
SettingQueue_RemoveSection( LPTSTR lpSection, QUEUENUM dwWhichQueue )
|
||
{
|
||
|
||
LINKED_LIST *pList;
|
||
KEY_NODE *pKeyNode;
|
||
SECTION_NODE *pSectionNode;
|
||
SECTION_NODE *pPreviousSectionNode = NULL;
|
||
|
||
pList = SelectSettingQueue( dwWhichQueue );
|
||
if (pList == NULL)
|
||
return;
|
||
pSectionNode = (SECTION_NODE *) pList->Head;
|
||
|
||
//
|
||
// Iterate through all the sections.
|
||
//
|
||
while( pSectionNode ) {
|
||
|
||
KEY_NODE *pTempKeyNode;
|
||
|
||
//
|
||
// If this section matches the one we are looking for, delete it.
|
||
// Else advance to the next section.
|
||
//
|
||
if( lstrcmpi( pSectionNode->lpSection, lpSection ) == 0 ) {
|
||
|
||
for( pKeyNode = (KEY_NODE *) pSectionNode->key_list.Head; pKeyNode; ) {
|
||
|
||
free( pKeyNode->lpKey );
|
||
|
||
free( pKeyNode->lpValue );
|
||
|
||
pTempKeyNode = (KEY_NODE *) pKeyNode->Header.next;
|
||
|
||
free( pKeyNode );
|
||
|
||
pKeyNode = pTempKeyNode;
|
||
|
||
}
|
||
|
||
//
|
||
// Special case if we are at the head of the list
|
||
//
|
||
if( pPreviousSectionNode == NULL ) {
|
||
pList->Head = pSectionNode->Header.next;
|
||
|
||
free( pSectionNode->lpSection );
|
||
|
||
pSectionNode = (SECTION_NODE *) pList->Head;
|
||
}
|
||
else {
|
||
pPreviousSectionNode->Header.next = pSectionNode->Header.next;
|
||
|
||
free( pSectionNode->lpSection );
|
||
|
||
pSectionNode = (SECTION_NODE *) pPreviousSectionNode->Header.next;
|
||
}
|
||
|
||
}
|
||
else {
|
||
|
||
pPreviousSectionNode = pSectionNode;
|
||
|
||
pSectionNode = (SECTION_NODE *) pSectionNode->Header.next;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//----------------------------------------------------------------------------
|
||
//
|
||
// Function: SettingQueue_MarkVolatile
|
||
//
|
||
// Purpose: Marks or clears the Volatile flag for a section. Typically
|
||
// one marks volatile sections at load time on the "Orig" queues.
|
||
//
|
||
// Later, at save time, the volatile flag gets cleared if you
|
||
// ever call *_AddSetting() *_AddSection().
|
||
//
|
||
//----------------------------------------------------------------------------
|
||
|
||
VOID
|
||
SettingQueue_MarkVolatile(LPTSTR lpSection,
|
||
QUEUENUM dwWhichQueue)
|
||
{
|
||
SECTION_NODE *p = FindQueuedSection(lpSection, dwWhichQueue);
|
||
|
||
if ( p == NULL )
|
||
return;
|
||
|
||
p->bVolatile = TRUE;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------
|
||
//
|
||
// Function: SettingQueue_Empty
|
||
//
|
||
// Purpose: This function emptys the queue of settings. Since the user
|
||
// can go BACK and re-save a file, it must be emptied before
|
||
// trying to save it again.
|
||
//
|
||
// Arguments: VOID
|
||
//
|
||
// Returns: VOID
|
||
//
|
||
//----------------------------------------------------------------------------
|
||
|
||
VOID SettingQueue_Empty(QUEUENUM dwWhichQueue)
|
||
{
|
||
LINKED_LIST *pList;
|
||
SECTION_NODE *p, *pn;
|
||
KEY_NODE *q, *qn;
|
||
|
||
//
|
||
// Point to the proper queue to empty and start at the head of it
|
||
//
|
||
|
||
pList = SelectSettingQueue(dwWhichQueue);
|
||
if (pList == NULL)
|
||
return;
|
||
|
||
p = (SECTION_NODE *) pList->Head;
|
||
|
||
//
|
||
// For each SECTION_NODE, walk down each KEY_NODE. Unlink and free all.
|
||
//
|
||
|
||
while ( p ) {
|
||
for ( q = (KEY_NODE *) p->key_list.Head; q; ) {
|
||
free(q->lpKey);
|
||
free(q->lpValue);
|
||
qn=(KEY_NODE *) q->Header.next;
|
||
free(q);
|
||
q=qn;
|
||
}
|
||
free(p->lpSection);
|
||
pn=(SECTION_NODE *) p->Header.next;
|
||
free(p);
|
||
p=pn;
|
||
}
|
||
|
||
//
|
||
// Zero out the head & tail pointers
|
||
//
|
||
|
||
pList->Head = NULL;
|
||
pList->Tail = NULL;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------
|
||
//
|
||
// Function: SettingQueue_Flush
|
||
//
|
||
// Purpose: This function is called (by the wizard) once all the settings
|
||
// have been queued.
|
||
//
|
||
// Arguments:
|
||
// LPTSTR lpFileName - name of file to create/edit
|
||
// DWORD dwWhichQueue - which queue, answers file, .udf, ...
|
||
//
|
||
// Returns:
|
||
// BOOL - success
|
||
//
|
||
//----------------------------------------------------------------------------
|
||
|
||
BOOL
|
||
SettingQueue_Flush(LPTSTR lpFileName,
|
||
QUEUENUM dwWhichQueue)
|
||
{
|
||
LINKED_LIST *pList;
|
||
SECTION_NODE *pSection;
|
||
KEY_NODE *pKey;
|
||
TCHAR Buffer[MAX_INILINE_LEN];
|
||
FILE *fp;
|
||
INT BufferSize = sizeof(Buffer) / sizeof(TCHAR);
|
||
HRESULT hrPrintf;
|
||
|
||
//
|
||
// Point to the proper queue to flush
|
||
//
|
||
|
||
pList = SelectSettingQueue(dwWhichQueue);
|
||
if (pList == NULL)
|
||
return FALSE;
|
||
|
||
pSection = (SECTION_NODE *) pList->Head;
|
||
|
||
//
|
||
// Start writing the file
|
||
//
|
||
|
||
if( ( fp = My_fopen( lpFileName, _T("w") ) ) == NULL ) {
|
||
|
||
return( FALSE );
|
||
|
||
}
|
||
|
||
if( My_fputs( _T(";SetupMgrTag\n"), fp ) == _TEOF ) {
|
||
|
||
My_fclose( fp );
|
||
|
||
return( FALSE );
|
||
}
|
||
|
||
//
|
||
// For each section ...
|
||
//
|
||
|
||
for ( pSection = (SECTION_NODE *) pList->Head;
|
||
pSection;
|
||
pSection = (SECTION_NODE *) pSection->Header.next ) {
|
||
|
||
//
|
||
// We don't write out sections that are still marked volatile.
|
||
//
|
||
|
||
if ( pSection->bVolatile )
|
||
continue;
|
||
|
||
//
|
||
// Write the section name only if we will write keys below it
|
||
//
|
||
// ISSUE-2002/02/28-stelo- this causes problems because we want to write out
|
||
// some sections without keys, like:
|
||
//
|
||
//[NetServices]
|
||
// MS_SERVER=params.MS_SERVER
|
||
//
|
||
//[params.MS_SERVER]
|
||
//
|
||
// How can we get around this?
|
||
//
|
||
if( DoesSectionHaveKeys( pSection ) ) {
|
||
|
||
hrPrintf=StringCchPrintf(Buffer,
|
||
AS(Buffer),
|
||
_T("[%s]\n"),
|
||
pSection->lpSection);
|
||
|
||
}
|
||
else {
|
||
|
||
continue;
|
||
|
||
}
|
||
|
||
if( My_fputs( Buffer, fp ) == _TEOF ) {
|
||
|
||
My_fclose( fp );
|
||
|
||
return( FALSE );
|
||
|
||
}
|
||
|
||
//
|
||
// Write out each key=value
|
||
//
|
||
|
||
for ( pKey = (KEY_NODE *) pSection->key_list.Head;
|
||
pKey;
|
||
pKey = (KEY_NODE *) pKey->Header.next ) {
|
||
|
||
BOOL bQuoteKey = FALSE;
|
||
BOOL bQuoteValue = FALSE;
|
||
TCHAR *p;
|
||
|
||
//
|
||
// An empty value means to not write it
|
||
//
|
||
|
||
if ( pKey->lpValue[0] == _T('\0') )
|
||
continue;
|
||
|
||
//
|
||
// Double-quote the value if it has white-space and is not
|
||
// already quoted
|
||
//
|
||
|
||
bQuoteKey = IsNecessaryToQuoteString( pKey->lpKey );
|
||
|
||
bQuoteValue = IsNecessaryToQuoteString( pKey->lpValue );
|
||
|
||
//
|
||
// Put the key we want into Buffer
|
||
|
||
// ISSUE-2002/02/28-stelo- text might get truncated here, should we show a warning?
|
||
|
||
Buffer[0] = _T('\0');
|
||
|
||
if( pKey->lpKey[0] != _T('\0') ) {
|
||
if ( bQuoteKey ) {
|
||
|
||
hrPrintf=StringCchPrintf(Buffer,
|
||
AS(Buffer),
|
||
_T(" \"%s\"="),
|
||
pKey->lpKey);
|
||
|
||
}
|
||
else {
|
||
|
||
hrPrintf=StringCchPrintf(Buffer,
|
||
AS(Buffer),
|
||
_T(" %s="),
|
||
pKey->lpKey);
|
||
|
||
}
|
||
}
|
||
|
||
//
|
||
// Put the value we want into Buffer paying attention to
|
||
// whether we want quotes around the it or not.
|
||
//
|
||
|
||
if ( bQuoteValue ) {
|
||
|
||
lstrcatn( Buffer, _T("\""), BufferSize );
|
||
lstrcatn( Buffer, pKey->lpValue, BufferSize );
|
||
lstrcatn( Buffer, _T("\""), BufferSize );
|
||
lstrcatn( Buffer, _T("\n"), BufferSize );
|
||
|
||
}
|
||
else {
|
||
|
||
lstrcatn( Buffer, pKey->lpValue, BufferSize );
|
||
lstrcatn( Buffer, _T("\n"), BufferSize );
|
||
|
||
}
|
||
|
||
if( My_fputs( Buffer, fp ) == _TEOF ) {
|
||
|
||
My_fclose( fp );
|
||
|
||
return( FALSE );
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Write a blank line at the end of the section
|
||
//
|
||
|
||
hrPrintf=StringCchPrintf(Buffer, AS(Buffer), _T("\n"));
|
||
|
||
if( My_fputs( Buffer, fp ) == _TEOF ) {
|
||
|
||
My_fclose( fp );
|
||
|
||
return( FALSE );
|
||
|
||
}
|
||
|
||
}
|
||
|
||
My_fclose( fp );
|
||
|
||
return( TRUE );
|
||
}
|
||
|
||
//----------------------------------------------------------------------------
|
||
//
|
||
// Function: SettingQueue_Copy
|
||
//
|
||
// Purpose: Copies one settings queue to another. Used to copy the
|
||
// input queues to the output queues.
|
||
//
|
||
// Look at common\save.c
|
||
//
|
||
//----------------------------------------------------------------------------
|
||
|
||
VOID
|
||
SettingQueue_Copy(QUEUENUM dwFrom, QUEUENUM dwTo)
|
||
{
|
||
LINKED_LIST *pListFrom = SelectSettingQueue(dwFrom);
|
||
|
||
SECTION_NODE *p, *pSectionNode;
|
||
KEY_NODE *q;
|
||
|
||
#if DBG
|
||
KEY_NODE *pKeyNode;
|
||
#endif
|
||
|
||
if (pListFrom == NULL)
|
||
return;
|
||
|
||
for ( p = (SECTION_NODE *) pListFrom->Head;
|
||
p;
|
||
p = (SECTION_NODE *) p->Header.next ) {
|
||
|
||
//
|
||
// Add the section to the output queue
|
||
//
|
||
|
||
SettingQueue_AddSetting(p->lpSection,
|
||
_T(""),
|
||
_T(""),
|
||
dwTo);
|
||
|
||
pSectionNode = FindQueuedSection(p->lpSection, dwTo);
|
||
|
||
for ( q = (KEY_NODE *) p->key_list.Head;
|
||
q;
|
||
q = (KEY_NODE *) q->Header.next ) {
|
||
|
||
//
|
||
// Add the key=value
|
||
//
|
||
|
||
SettingQueue_AddSetting(p->lpSection,
|
||
q->lpKey,
|
||
q->lpValue,
|
||
dwTo);
|
||
#if DBG
|
||
//
|
||
// Retain the bSetOnce flag
|
||
//
|
||
|
||
pKeyNode = FindKey(&pSectionNode->key_list, q->lpKey);
|
||
|
||
if ( pKeyNode != NULL ) {
|
||
pKeyNode->bSetOnce = q->bSetOnce;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
//
|
||
// Retain the bVolatile flag on the section node.
|
||
//
|
||
|
||
if ( pSectionNode != NULL ) {
|
||
pSectionNode->bVolatile = p->bVolatile;
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
//
|
||
// Internal support routines
|
||
//
|
||
//----------------------------------------------------------------------------
|
||
|
||
//----------------------------------------------------------------------------
|
||
//
|
||
// Function: DoesSectionHaveKeys
|
||
//
|
||
// Purpose: Determines if a section has keys to be written out or not
|
||
//
|
||
// Arguments:
|
||
// SECTION_NODE *pSection - the section to determine if it has keys or not
|
||
//
|
||
// Returns:
|
||
// TRUE - if this section contains keys
|
||
// FALSE - if this section does not contain keys
|
||
//
|
||
//----------------------------------------------------------------------------
|
||
BOOL
|
||
DoesSectionHaveKeys( SECTION_NODE *pSection ) {
|
||
|
||
KEY_NODE *pKey;
|
||
|
||
for ( pKey = (KEY_NODE *) pSection->key_list.Head;
|
||
pKey;
|
||
pKey = (KEY_NODE *) pKey->Header.next ) {
|
||
|
||
if ( pKey->lpValue[0] != _T('\0') ) {
|
||
|
||
return( TRUE );
|
||
|
||
}
|
||
|
||
}
|
||
|
||
return( FALSE );
|
||
|
||
}
|
||
|
||
//----------------------------------------------------------------------------
|
||
//
|
||
// Function: IsNecessaryToQuoteString
|
||
//
|
||
// Purpose: Determines if a string is already quoted and if it is not quoted,
|
||
// if a string has white space or not
|
||
//
|
||
// Arguments:
|
||
// LPTSTR p - the string to be scanned
|
||
//
|
||
// Returns:
|
||
// TRUE - if the string needs to be quoted
|
||
// FALSE - if the string does not need to be quoted
|
||
//
|
||
//----------------------------------------------------------------------------
|
||
static BOOL
|
||
IsNecessaryToQuoteString( LPTSTR p )
|
||
{
|
||
|
||
LPTSTR pCommaSearch;
|
||
|
||
//
|
||
// See if it is already quoted
|
||
// We only check if the first char is a quote because the last char may
|
||
// not be a quote. Example: ComputerType = "HAL Friendly Name", OEM
|
||
//
|
||
if( *p == _T('"') )
|
||
{
|
||
return( FALSE );
|
||
}
|
||
|
||
//
|
||
// If it contains a comma, then don't quote it except for the printer
|
||
// command that contains rundll32. This is kind of a hack.
|
||
// This prevents keys like:
|
||
//
|
||
// ComputerType = "HAL Friendly Name", OEM
|
||
//
|
||
// from being quoted.
|
||
//
|
||
|
||
if( ! _tcsstr( p, _T("rundll32") ) )
|
||
{
|
||
|
||
for( pCommaSearch = p; *pCommaSearch; pCommaSearch++ )
|
||
{
|
||
|
||
if( *pCommaSearch == _T(',') )
|
||
{
|
||
|
||
return( FALSE );
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Look for white space
|
||
//
|
||
for ( ; *p; p++ )
|
||
{
|
||
|
||
if( iswspace(*p) )
|
||
{
|
||
|
||
return( TRUE );
|
||
|
||
}
|
||
|
||
}
|
||
|
||
return( FALSE );
|
||
|
||
}
|
||
|
||
//----------------------------------------------------------------------------
|
||
//
|
||
// Function: FindQueuedSection (static)
|
||
//
|
||
// Purpose: Finds the SECTION_NODE on the global settings queue with
|
||
// the given name.
|
||
//
|
||
// Arguments:
|
||
// LPTSTR lpSection - name of section in .ini
|
||
//
|
||
// Returns:
|
||
// SECTION_NODE * or NULL if it does not exist
|
||
//
|
||
// Notes:
|
||
// - Searches are case insensitive
|
||
//
|
||
//----------------------------------------------------------------------------
|
||
|
||
static SECTION_NODE *FindQueuedSection(LPTSTR lpSection,
|
||
QUEUENUM dwWhichQueue)
|
||
{
|
||
SECTION_NODE *p;
|
||
LINKED_LIST *pList;
|
||
|
||
pList = SelectSettingQueue(dwWhichQueue);
|
||
if (pList == NULL)
|
||
return NULL;
|
||
|
||
p = (SECTION_NODE *) pList->Head;
|
||
if ( p == NULL )
|
||
return NULL;
|
||
|
||
do {
|
||
if ( _tcsicmp(p->lpSection, lpSection) == 0 )
|
||
break;
|
||
p = (SECTION_NODE *) p->Header.next;
|
||
} while ( p );
|
||
|
||
return p;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------
|
||
//
|
||
// Function: InsertNode
|
||
//
|
||
// Purpose: Puts the given node at the tail of the given list. All nodes
|
||
// must begin with a NODE_HEADER.
|
||
//
|
||
// Returns: VOID
|
||
//
|
||
// Notes:
|
||
// - Allocates no memory, only links the node in.
|
||
//
|
||
//----------------------------------------------------------------------------
|
||
|
||
VOID InsertNode(LINKED_LIST *pList, PVOID pNode)
|
||
{
|
||
NODE_HEADER *pNode2 = (NODE_HEADER *) pNode;
|
||
|
||
//
|
||
// Put it at the tail
|
||
//
|
||
|
||
pNode2->next = NULL;
|
||
if ( pList->Tail )
|
||
pList->Tail->next = pNode2;
|
||
pList->Tail = pNode2;
|
||
|
||
//
|
||
// In case its the first one onto the list, fixup the head
|
||
//
|
||
|
||
if ( ! pList->Head )
|
||
pList->Head = pNode2;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------
|
||
//
|
||
// Function: FindKey
|
||
//
|
||
// Purpose: Searches the given list of keynodes and finds one with the
|
||
// given name.
|
||
//
|
||
// Arguments:
|
||
// LPTSTR lpSection - name of section in .ini
|
||
//
|
||
// Returns:
|
||
// SECTION_NODE * or NULL if it does not exist
|
||
//
|
||
// Notes:
|
||
// - Searches are case insensitive
|
||
//
|
||
//----------------------------------------------------------------------------
|
||
|
||
KEY_NODE* FindKey(LINKED_LIST *ListHead,
|
||
LPTSTR lpKeyName)
|
||
{
|
||
KEY_NODE *p = (KEY_NODE *) ListHead->Head;
|
||
|
||
if ( p == NULL )
|
||
return NULL;
|
||
|
||
do {
|
||
if ( _tcsicmp(p->lpKey, lpKeyName) == 0 )
|
||
break;
|
||
p = (KEY_NODE *) p->Header.next;
|
||
} while ( p );
|
||
|
||
return p;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------
|
||
//
|
||
// Function: SelectSettingQueue
|
||
//
|
||
// Purpose: Translates dwWhichQueue into a LINKED_LIST pointer.
|
||
//
|
||
// Returns: A pointer to one of the 5 settings queues we have.
|
||
//
|
||
//----------------------------------------------------------------------------
|
||
|
||
LINKED_LIST *SelectSettingQueue(QUEUENUM dwWhichQueue)
|
||
{
|
||
switch ( dwWhichQueue ) {
|
||
|
||
case SETTING_QUEUE_ANSWERS:
|
||
return &AnswerFileQueue;
|
||
|
||
case SETTING_QUEUE_UDF:
|
||
return &UdfQueue;
|
||
|
||
case SETTING_QUEUE_ORIG_ANSWERS:
|
||
return &OrigAnswerFileQueue;
|
||
|
||
case SETTING_QUEUE_ORIG_UDF:
|
||
return &OrigUdfQueue;
|
||
|
||
case SETTING_QUEUE_TXTSETUP_OEM:
|
||
return &TxtSetupOemQueue;
|
||
|
||
default:
|
||
AssertMsg(FALSE, "Invalid dwWhichQueue");
|
||
}
|
||
|
||
return NULL;
|
||
}
|