windows-nt/Source/XPSP1/NT/base/pnp/setupapi/mru.c
2020-09-26 16:20:57 +08:00

1715 lines
43 KiB
C

/*++
Copyright (c) 1993-1998 Microsoft Corporation
Module Name:
mru.c
Abstract:
Implementation of source list handling routines.
Author:
Ted Miller (tedm) 30-Aug-1995
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
//
// Location in registry where per-system MRU list is stored
// (relative to HKEY_LOCAL_MACHINE).
//
PCTSTR pszPerSystemKey = TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup");
PCTSTR pszPerSystemVal = TEXT("Installation Sources");
//
// Location in registry where per-user MRU list is stored.
// (relative to HKEY_CURRENT_USER).
//
PCTSTR pszPerUserKey = TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup");
PCTSTR pszPerUserVal = TEXT("Installation Sources");
typedef PTSTR *APTSTR;
//
// Platform strings we recognize.
//
PCTSTR PlatformPathComponents[] = { TEXT("\\alpha"),
TEXT("\\nec98"), //NEC98
TEXT("\\i386"),
TEXT("\\x86"),
TEXT("\\mips"),
TEXT("\\ppc"),
TEXT("\\axp64"),
TEXT("\\ia64"),
NULL
};
//
// These are guarded by MruCritSect.
//
PTSTR *TemporarySourceList;
UINT TemporarySourceCount;
BOOL MruNoBrowse;
VOID
pSetupStripTrailingPlatformComponent(
IN OUT PTSTR *Paths,
IN OUT PDWORD NumPaths
);
BOOL LockMruCritSect()
{
BOOL locked = FALSE;
try {
EnterCriticalSection(&MruCritSect);
locked = TRUE;
} except (EXCEPTION_EXECUTE_HANDLER) {
}
if(!locked) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
}
return locked;
}
BOOL
_SetupSetSourceList(
IN DWORD Flags,
IN PCTSTR *SourceList,
IN UINT SourceCount
)
/*++
Routine Description:
This routine allows the caller to set the list of installation
sources for either the current user or the system (common to
all users).
Arguments:
Flags - a combination of the following values:
SRCLIST_SYSTEM - specify that the list is to become the
per-system list. The caller must be administrator.
SRCLIST_USER - specify that the list is to become the per-user
list.
SRCLIST_TEMPORARY - specify that the list is to become the
entire list for the duration of the current process,
or until this routine is called again to change the behavior.
Exactly one of SRCLIST_SYSTEM, SRCLIST_USER, and SRCLIST_TEMPORARY
must be specified.
SRCLIST_NOBROWSE - specify that the user is not allowed to add
or change sources when the SetupPromptForDisk API is used.
Typically used in combination with SRCLIST_TEMPORARY.
SourceList - supplies array of strings that are to become the
source list, as described by the Flags parameter.
SourceCount - specifies number of elements in the SourceList array.
Return Value:
--*/
{
DWORD flags;
DWORD d;
UINT u,v;
//
// Check flags. Only one of system, user, or temporary may be set.
//
flags = Flags & (SRCLIST_SYSTEM | SRCLIST_USER | SRCLIST_TEMPORARY);
if((flags != SRCLIST_SYSTEM) && (flags != SRCLIST_USER) && (flags != SRCLIST_TEMPORARY)) {
SetLastError(ERROR_INVALID_PARAMETER);
return(FALSE);
}
//
// User must be admin for system flag to work.
//
if((flags == SRCLIST_SYSTEM) && !pSetupIsUserAdmin()) {
SetLastError(ERROR_ACCESS_DENIED);
return(FALSE);
}
//
// Only allow one thread at a time in this process to access
// the temporary source list.
//
if(!LockMruCritSect()) {
return FALSE;
}
if(Flags & SRCLIST_NOBROWSE) {
MruNoBrowse = TRUE;
}
d = NO_ERROR;
if(flags == SRCLIST_TEMPORARY) {
if(TemporarySourceList) {
SetupFreeSourceList(&TemporarySourceList,TemporarySourceCount);
}
//
// Duplicate the list the caller passed in.
//
if(TemporarySourceList = MyMalloc(SourceCount * sizeof(PTSTR))) {
TemporarySourceCount = SourceCount;
for(u=0; u<SourceCount; u++) {
TemporarySourceList[u] = DuplicateString(SourceList[u]);
if(!TemporarySourceList[u]) {
for(v=0; v<u; v++) {
MyFree(TemporarySourceList[v]);
}
MyFree(TemporarySourceList);
TemporarySourceList = NULL;
TemporarySourceCount = 0;
d = ERROR_NOT_ENOUGH_MEMORY;
break;
}
}
} else {
d = ERROR_NOT_ENOUGH_MEMORY;
}
} else {
//
// User or system.
//
d = pSetupSetArrayToMultiSzValue(
(flags == SRCLIST_SYSTEM) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
(flags == SRCLIST_SYSTEM) ? pszPerSystemKey : pszPerUserKey,
(flags == SRCLIST_SYSTEM) ? pszPerSystemVal : pszPerUserVal,
(PTSTR *)SourceList,
SourceCount
);
}
//
// Done with protected resource
//
LeaveCriticalSection(&MruCritSect);
SetLastError(d);
return(d == NO_ERROR);
}
#ifdef UNICODE
//
// ANSI version
//
BOOL
SetupSetSourceListA(
IN DWORD Flags,
IN PCSTR *SourceList,
IN UINT SourceCount
)
{
PCWSTR *sourceList;
UINT u;
DWORD rc;
BOOL b;
sourceList = MyMalloc(SourceCount*sizeof(PCWSTR));
if(!sourceList) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return(FALSE);
}
ZeroMemory((PVOID)sourceList,SourceCount*sizeof(PCWSTR));
rc = NO_ERROR;
for(u=0; (rc==NO_ERROR) && (u<SourceCount); u++) {
//
// Try/except guards access to SourceList[u] in case
// SourceList is a bad pointer
//
try {
rc = pSetupCaptureAndConvertAnsiArg(SourceList[u],&sourceList[u]);
} except(EXCEPTION_EXECUTE_HANDLER) {
rc = ERROR_INVALID_PARAMETER;
}
}
if(rc == NO_ERROR) {
b = _SetupSetSourceList(Flags,sourceList,SourceCount);
rc = GetLastError();
} else {
b = FALSE;
}
for(u=0; u<SourceCount; u++) {
if(sourceList[u]) {
MyFree(sourceList[u]);
}
}
MyFree(sourceList);
SetLastError(rc);
return(b);
}
#else
//
// Unicode stub
//
BOOL
SetupSetSourceListW(
IN DWORD Flags,
IN PCWSTR *SourceList,
IN UINT SourceCount
)
{
UNREFERENCED_PARAMETER(Flags);
UNREFERENCED_PARAMETER(SourceList);
UNREFERENCED_PARAMETER(SourceCount);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return(FALSE);
}
#endif
BOOL
SetupSetSourceList(
IN DWORD Flags,
IN PCTSTR *SourceList,
IN UINT SourceCount
)
{
PCTSTR *sourceList;
UINT u;
DWORD rc;
BOOL b;
sourceList = MyMalloc(SourceCount*sizeof(PCTSTR));
if(!sourceList) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return(FALSE);
}
ZeroMemory((PVOID)sourceList,SourceCount*sizeof(PCTSTR));
rc = NO_ERROR;
for(u=0; (rc==NO_ERROR) && (u<SourceCount); u++) {
//
// Try/except guards access to SourceList[u] in case
// SourceList is a bad pointer
//
try {
rc = CaptureStringArg(SourceList[u],&sourceList[u]);
} except(EXCEPTION_EXECUTE_HANDLER) {
rc = ERROR_INVALID_PARAMETER;
}
}
if(rc == NO_ERROR) {
b = _SetupSetSourceList(Flags,sourceList,SourceCount);
rc = GetLastError();
} else {
b = FALSE;
}
for(u=0; u<SourceCount; u++) {
if(sourceList[u]) {
MyFree(sourceList[u]);
}
}
MyFree(sourceList);
SetLastError(rc);
return(b);
}
BOOL
SetupCancelTemporarySourceList(
VOID
)
/*++
Routine Description:
This routine cancels any temporary list and no-browse behavior
and reverts to standard list behavior.
Arguments:
None.
Return Value:
TRUE if a temporary list was in effect; FALSE if otherwise.
--*/
{
BOOL b;
if(!LockMruCritSect()) {
return FALSE;
}
MruNoBrowse = FALSE;
if(TemporarySourceList) {
//
// SetupFreeSourceList zeros out the pointer for us.
//
SetupFreeSourceList(&TemporarySourceList,TemporarySourceCount);
TemporarySourceCount = 0;
b = TRUE;
} else {
b = FALSE;
}
LeaveCriticalSection(&MruCritSect);
return(b);
}
BOOL
_SetupAddToSourceList(
IN DWORD Flags,
IN PCTSTR Source
)
/*++
Routine Description:
This routine allows the caller to append a value to the list
of installation sources for either the current user or the system.
If the value already exists it is removed first.
Arguments:
Flags - a combination of the following values:
SRCLIST_SYSTEM - specify that the source is to added to the
per-system list. The caller must be administrator.
SRCLIST_USER - specify that the list is to be added to the per-user
list.
SRCLIST_SYSIFADMIN - specifies that if the caller is administrator,
then the source is added to the system list; if the caller
is not administrator then the source is added to the per-user
list for the current user.
If a temporary list is currently in use (see SetupSetSourceList),
these 3 flags are ignored and the source is added to the temporary list.
SRCLIST_APPEND - specify that the source is to be added to the end
of the given list. Otherwise it is added to the beginning.
Source - specifies the source to be added to the list.
Return Value:
--*/
{
APTSTR Lists[2];
UINT Counts[2];
UINT NumberOfLists;
DWORD d;
UINT u;
PTSTR p;
PVOID pTmp;
HKEY RootKeys[2];
PCTSTR SubKeys[2];
PCTSTR Vals[2];
BOOL NeedToFree[2];
if(!LockMruCritSect()) {
return FALSE;
}
//
// Remove first, if present. This makes things easier for us later.
// Do this inside the locks to ensure atomicity for the add call as
// a whole.
//
if(!SetupRemoveFromSourceList(Flags,Source)) {
d = GetLastError();
LeaveCriticalSection(&MruCritSect);
SetLastError(d);
return(FALSE);
}
//
// Check Temporary list first.
//
d = NO_ERROR;
if(TemporarySourceList) {
Lists[0] = TemporarySourceList;
Counts[0] = TemporarySourceCount;
NumberOfLists = 1;
NeedToFree[0] = FALSE;
} else {
//
// Check sysifadmin flag and turn on appropriate flag.
//
if(Flags & SRCLIST_SYSIFADMIN) {
Flags |= pSetupIsUserAdmin() ? SRCLIST_SYSTEM : SRCLIST_USER;
}
NumberOfLists = 0;
if(Flags & SRCLIST_SYSTEM) {
if(pSetupIsUserAdmin()) {
d = pSetupQueryMultiSzValueToArray(
HKEY_LOCAL_MACHINE,
pszPerSystemKey,
pszPerSystemVal,
&Lists[0],
&Counts[0],
FALSE
);
if(d == NO_ERROR) {
NumberOfLists = 1;
RootKeys[0] = HKEY_LOCAL_MACHINE;
SubKeys[0] = pszPerSystemKey;
Vals[0] = pszPerSystemVal;
NeedToFree[0] = TRUE;
} else {
Lists[0] = NULL;
}
} else {
d = ERROR_ACCESS_DENIED;
}
}
if((Flags & SRCLIST_USER) && (d == NO_ERROR)) {
d = pSetupQueryMultiSzValueToArray(
HKEY_CURRENT_USER,
pszPerSystemKey,
pszPerSystemVal,
&Lists[NumberOfLists],
&Counts[NumberOfLists],
FALSE
);
if(d == NO_ERROR) {
RootKeys[NumberOfLists] = HKEY_CURRENT_USER;
SubKeys[NumberOfLists] = pszPerUserKey;
Vals[NumberOfLists] = pszPerUserVal;
NeedToFree[NumberOfLists] = TRUE;
NumberOfLists++;
} else {
Lists[NumberOfLists] = NULL;
}
}
}
if(d == NO_ERROR) {
//
// Do each list.
//
for(u=0; (d==NO_ERROR) && (u<NumberOfLists); u++) {
if(p = DuplicateString(Source)) {
if(pTmp = MyRealloc(Lists[u],(Counts[u]+1)*sizeof(PTSTR))) {
Lists[u] = pTmp;
if(Flags & SRCLIST_APPEND) {
Lists[u][Counts[u]] = p;
} else {
MoveMemory(&Lists[u][1],Lists[u],Counts[u] * sizeof(PTSTR));
Lists[u][0] = p;
}
Counts[u]++;
//
// Put back in registry if necessary.
//
if(TemporarySourceList) {
TemporarySourceList = Lists[u];
TemporarySourceCount = Counts[0];
} else {
d = pSetupSetArrayToMultiSzValue(
RootKeys[u],
SubKeys[u],
Vals[u],
Lists[u],
Counts[u]
);
if(NeedToFree[u]) {
SetupFreeSourceList(&Lists[u],Counts[u]);
}
}
} else {
d = ERROR_NOT_ENOUGH_MEMORY;
}
} else {
d = ERROR_NOT_ENOUGH_MEMORY;
}
}
}
//
// Done looking at temporary list.
//
//
LeaveCriticalSection(&MruCritSect);
SetLastError(d);
return(d == NO_ERROR);
}
#ifdef UNICODE
//
// ANSI version
//
BOOL
SetupAddToSourceListA(
IN DWORD Flags,
IN PCSTR Source
)
{
BOOL b;
DWORD rc;
PCWSTR source;
rc = pSetupCaptureAndConvertAnsiArg(Source,&source);
if(rc == NO_ERROR) {
b = _SetupAddToSourceList(Flags,source);
rc = GetLastError();
MyFree(source);
} else {
b = FALSE;
}
SetLastError(rc);
return(b);
}
#else
//
// Unicode stub
//
BOOL
SetupAddToSourceListW(
IN DWORD Flags,
IN PCWSTR Source
)
{
UNREFERENCED_PARAMETER(Flags);
UNREFERENCED_PARAMETER(Source);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return(FALSE);
}
#endif
BOOL
SetupAddToSourceList(
IN DWORD Flags,
IN PCTSTR Source
)
{
BOOL b;
DWORD rc;
PCTSTR source;
rc = CaptureStringArg(Source,&source);
if(rc == NO_ERROR) {
b = _SetupAddToSourceList(Flags,source);
rc = GetLastError();
MyFree(source);
} else {
b = FALSE;
}
SetLastError(rc);
return(b);
}
BOOL
_SetupRemoveFromSourceList(
IN DWORD Flags,
IN PCTSTR Source
)
/*++
Routine Description:
This routine allows the caller to remove a value from the list
of installation sources for either the current user or the system.
The system and user lists are merged at run time.
Arguments:
Flags - a combination of the following values:
SRCLIST_SYSTEM - specify that the source is to removed from the
per-system list. The caller must be administrator.
SRCLIST_USER - specify that the list is to be removed from the
per-user list.
SRCLIST_SYSIFADMIN - specifies that if the caller is administrator,
then the source is removed from the system list; if the caller
is not administrator then the source is removed from the per-user
list for the current user.
Any combination of these flags may be specified on a single call.
If a temporary list is currently in use (see SetupSetSourceList),
these 3 flags are ignored and the source is removed from the temporary list.
SRCLIST_SUBDIRS - specify that all subdirectories of Source are also
to be removed. The determination of subdirectories is done based on
a simple prefix scan.
Source - specifies the source to be removed from the list.
Return Value:
--*/
{
APTSTR Lists[2];
UINT Counts[2];
UINT NumberOfLists;
DWORD d;
BOOL NeedToFree;
UINT u,v;
PTSTR p;
BOOL Match;
UINT Len;
PVOID pTmp;
p = DuplicateString(Source);
if(!p) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return(FALSE);
}
CharUpper(p);
Len = lstrlen(p);
if(!LockMruCritSect()) {
MyFree(p);
return FALSE;
}
//
// Check Temporary list first.
//
d = NO_ERROR;
if(TemporarySourceList) {
Lists[0] = TemporarySourceList;
Counts[0] = TemporarySourceCount;
NumberOfLists = 1;
NeedToFree = FALSE;
} else {
//
// Check sysifadmin flag and turn on appropriate flag.
//
if(Flags & SRCLIST_SYSIFADMIN) {
Flags |= pSetupIsUserAdmin() ? SRCLIST_SYSTEM : SRCLIST_USER;
}
NeedToFree = TRUE;
NumberOfLists = 0;
if(Flags & SRCLIST_SYSTEM) {
if(pSetupIsUserAdmin()) {
d = pSetupQueryMultiSzValueToArray(
HKEY_LOCAL_MACHINE,
pszPerSystemKey,
pszPerSystemVal,
&Lists[0],
&Counts[0],
FALSE
);
if(d == NO_ERROR) {
NumberOfLists = 1;
} else {
Lists[0] = NULL;
}
} else {
d = ERROR_ACCESS_DENIED;
}
}
if((Flags & SRCLIST_USER) && (d == NO_ERROR)) {
d = pSetupQueryMultiSzValueToArray(
HKEY_CURRENT_USER,
pszPerSystemKey,
pszPerSystemVal,
&Lists[NumberOfLists],
&Counts[NumberOfLists],
FALSE
);
if(d == NO_ERROR) {
NumberOfLists++;
} else {
Lists[NumberOfLists] = NULL;
}
}
}
if(d == NO_ERROR) {
//
// Go through each list.
//
for(u=0; u<NumberOfLists; u++) {
//
// Go though each item in the current list.
//
for(v=0; v<Counts[u]; v++) {
CharUpper(Lists[u][v]);
//
// See if this item matches the one being deleted.
//
Match = FALSE;
if(Flags & SRCLIST_SUBDIRS) {
//
// See if the source the caller passed in is
// a prefix of the source in the list.
//
Match = (_tcsncmp(Lists[u][v],p,Len) == 0);
} else {
Match = (lstrcmp(Lists[u][v],p) == 0);
}
if(Match) {
//
// Need to remove this item.
//
MyFree(Lists[u][v]);
MoveMemory(
&Lists[u][v],
&Lists[u][v+1],
(Counts[u] - (v+1)) * sizeof(PTSTR)
);
Counts[u]--;
v--;
}
}
}
if(TemporarySourceList) {
//
// Shrink temporary source list down to new size.
// Since we're shrinking we don't expect the realloc to fail
// but it's not an error if it does.
//
if(pTmp = MyRealloc(Lists[0],Counts[0]*sizeof(PTSTR))) {
TemporarySourceList = pTmp;
}
TemporarySourceCount = Counts[0];
} else {
//
// Need to put stuff back in registry.
//
u=0;
if(Flags & SRCLIST_SYSTEM) {
d = pSetupSetArrayToMultiSzValue(
HKEY_LOCAL_MACHINE,
pszPerSystemKey,
pszPerSystemVal,
Lists[0],
Counts[0]
);
u++;
}
if((d == NO_ERROR) && (Flags & SRCLIST_USER)) {
d = pSetupSetArrayToMultiSzValue(
HKEY_CURRENT_USER,
pszPerUserKey,
pszPerUserVal,
Lists[u],
Counts[u]
);
u++;
}
}
}
//
// Done looking at temporary list.
//
//
LeaveCriticalSection(&MruCritSect);
if(NeedToFree) {
for(u=0; u<NumberOfLists; u++) {
if(Lists[u]) {
SetupFreeSourceList(&Lists[u],Counts[u]);
}
}
}
MyFree(p);
SetLastError(d);
return(d == NO_ERROR);
}
#ifdef UNICODE
//
// ANSI version
//
BOOL
SetupRemoveFromSourceListA(
IN DWORD Flags,
IN PCSTR Source
)
{
PCWSTR source;
BOOL b;
DWORD rc;
rc = pSetupCaptureAndConvertAnsiArg(Source,&source);
if(rc == NO_ERROR) {
b = _SetupRemoveFromSourceList(Flags,source);
rc = GetLastError();
MyFree(source);
} else {
b = FALSE;
}
SetLastError(rc);
return(b);
}
#else
//
// Unicode stub
//
BOOL
SetupRemoveFromSourceListW(
IN DWORD Flags,
IN PCWSTR Source
)
{
UNREFERENCED_PARAMETER(Flags);
UNREFERENCED_PARAMETER(Source);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return(FALSE);
}
#endif
BOOL
SetupRemoveFromSourceList(
IN DWORD Flags,
IN PCTSTR Source
)
{
PCTSTR source;
BOOL b;
DWORD rc;
rc = CaptureStringArg(Source,&source);
if(rc == NO_ERROR) {
b = _SetupRemoveFromSourceList(Flags,source);
rc = GetLastError();
MyFree(source);
} else {
b = FALSE;
}
SetLastError(rc);
return(b);
}
#ifdef UNICODE
//
// ANSI version
//
BOOL
SetupQuerySourceListA(
IN DWORD Flags,
OUT PCSTR **List,
OUT PUINT Count
)
{
PCWSTR *list;
UINT count;
BOOL b;
DWORD d;
PSTR *ansilist;
UINT i;
b = SetupQuerySourceListW(Flags,&list,&count);
d = GetLastError();
if(b) {
if(ansilist = MyMalloc(count * sizeof(PCSTR))) {
ZeroMemory(ansilist,count*sizeof(PCSTR));
for(i=0; i<count; i++) {
ansilist[i] = pSetupUnicodeToAnsi(list[i]);
if(!ansilist[i]) {
SetupFreeSourceListA(&ansilist,count);
d = ERROR_NOT_ENOUGH_MEMORY;
b = FALSE;
break;
}
}
if(b) {
//
// Everything's ok, set up caller's out params.
//
try {
*Count = count;
*List = ansilist;
} except(EXCEPTION_EXECUTE_HANDLER) {
SetupFreeSourceListA(&ansilist,count);
d = ERROR_INVALID_PARAMETER;
b = FALSE;
}
}
} else {
d = ERROR_NOT_ENOUGH_MEMORY;
b = FALSE;
}
SetupFreeSourceListW(&list,count);
}
SetLastError(d);
return(b);
}
#else
//
// Unicode stub
//
BOOL
SetupQuerySourceListW(
IN DWORD Flags,
OUT PCWSTR **List,
OUT PUINT Count
)
{
UNREFERENCED_PARAMETER(Flags);
UNREFERENCED_PARAMETER(List);
UNREFERENCED_PARAMETER(Count);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return(FALSE);
}
#endif
BOOL
SetupQuerySourceList(
IN DWORD Flags,
OUT PCTSTR **List,
OUT PUINT Count
)
/*++
Routine Description:
This routine allows the caller to query the current list of installation
sources. The list is built from the system and user-specific lists,
potentially overridden by a temporary list (see SetupSetSourceList).
Arguments:
Flags - a combination of the following values:
SRCLIST_SYSTEM - specify that only the system list is desired.
SRCLIST_USER - specify that only the per-user list is desired.
SRCLIST_SYSIFADMIN - Same as SRCLIST_SYSTEM. Accepted only for
compatibility.
If none of these flags is specified then the current (merged) list is
returned in its entirety.
SRCLIST_NOSTRIPPLATFORM - Normally, all paths are stripped of a platform-
specific component if that component is the final one. IE, a path
stored in the registry as f:\mips will come back as f:\. If this flag
is specified, this behavior is turned off.
List - receives a pointer to an array of sources. The caller must free this
with SetupFreeSourceList.
Count - receives the number of sources.
Return Value:
--*/
{
DWORD d;
PTSTR *Values1 = NULL;
UINT NumVals1 = 0;
PTSTR *Values2 = NULL;
UINT NumVals2 = 0;
UINT TotalVals;
UINT u,v;
BOOL Found;
PTSTR *p;
BOOL StripPlatform;
//
// Either caller wants sysifadmin, or he wants some combination of
// system and user lists.
//
if((Flags & SRCLIST_SYSIFADMIN) && (Flags & (SRCLIST_SYSTEM | SRCLIST_USER))) {
SetLastError(ERROR_INVALID_PARAMETER);
return(FALSE);
}
if(!LockMruCritSect()) {
return FALSE;
}
//
// If sysifadmin, figure out which list to get.
//
if(Flags & SRCLIST_SYSIFADMIN) {
//
// Changed behavior to basically ignore this flag,
// since setup doesn't record the system source in the per-user
// mru list any more since this gets messy for upgrades.
//
//Flags = pSetupIsUserAdmin() ? SRCLIST_SYSTEM : SRCLIST_USER;
Flags = SRCLIST_SYSTEM;
} else {
//
// if no flags are specified, turn on system and user unless
// there's a temporary list.
//
if(!Flags && !TemporarySourceList) {
Flags = SRCLIST_SYSTEM | SRCLIST_USER;
}
}
StripPlatform = ((Flags & SRCLIST_NOSTRIPPLATFORM) == 0);
if(!Flags) {
//
// Temporary list in use.
//
d = NO_ERROR;
if(Values1 = MyMalloc(TemporarySourceCount * sizeof(PTSTR))) {
for(u=0; u<TemporarySourceCount; u++) {
Values1[u] = DuplicateString(TemporarySourceList[u]);
if(!Values1[u]) {
d = ERROR_NOT_ENOUGH_MEMORY;
for(v=0; v<u; v++) {
MyFree(Values1[v]);
}
MyFree(Values1);
break;
}
}
if(d == NO_ERROR) {
try {
*List = Values1;
*Count = TemporarySourceCount;
} except(EXCEPTION_EXECUTE_HANDLER) {
d = ERROR_INVALID_PARAMETER;
}
}
} else {
d = ERROR_NOT_ENOUGH_MEMORY;
}
} else {
//
// Fetch system list if desired.
//
if(Flags & SRCLIST_SYSTEM) {
d = pSetupQueryMultiSzValueToArray(
HKEY_LOCAL_MACHINE,
pszPerSystemKey,
pszPerSystemVal,
&Values1,
&NumVals1,
FALSE
);
//
// If we are supposed to, strip out platform-specific
// trailing components.
//
if((d == NO_ERROR) && StripPlatform) {
pSetupStripTrailingPlatformComponent(Values1,&NumVals1);
} else if (d != NO_ERROR) {
//
// Create dummy array.
//
NumVals1 = 0;
if(Values1 = MyMalloc(0)) {
d = NO_ERROR;
} else {
d = ERROR_NOT_ENOUGH_MEMORY;
}
}
} else {
//
// Create dummy array.
//
NumVals1 = 0;
if(Values1 = MyMalloc(0)) {
d = NO_ERROR;
} else {
d = ERROR_NOT_ENOUGH_MEMORY;
}
}
//
// Fetch user list if desired.
//
if((d == NO_ERROR) && (Flags & SRCLIST_USER)) {
d = pSetupQueryMultiSzValueToArray(
HKEY_CURRENT_USER,
pszPerUserKey,
pszPerUserVal,
&Values2,
&NumVals2,
FALSE
);
if((d == NO_ERROR) && StripPlatform) {
pSetupStripTrailingPlatformComponent(Values2,&NumVals2);
}
} else if(Values1) {
//
// Create dummy array.
//
NumVals2 = 0;
if(Values2 = MyMalloc(0)) {
d = NO_ERROR;
} else {
d = ERROR_NOT_ENOUGH_MEMORY;
}
} else {
NumVals2 = 0;
Values2 = NULL;
d = ERROR_NOT_ENOUGH_MEMORY;
}
TotalVals = NumVals1;
if(d == NO_ERROR) {
//
// Merge lists. Favor the system list.
// We iterate through the user list. For each item in the user list,
// we look for it in the system list. If not found, we append to the system list.
// The system list becomes the final list.
//
for(u=0; (d == NO_ERROR) && (u<NumVals2); u++) {
//
// Look for the current per-user path in the per-system
// list. If not found, append to end of system list.
//
Found = FALSE;
for(v=0; v<NumVals1; v++) {
if(!lstrcmpi(Values1[v],Values2[u])) {
Found = TRUE;
break;
}
}
if(!Found) {
if(p = MyRealloc(Values1,(TotalVals+1)*sizeof(PTSTR))) {
Values1 = p;
if(Values1[TotalVals] = DuplicateString(Values2[u])) {
TotalVals++;
} else {
d = ERROR_NOT_ENOUGH_MEMORY;
}
} else {
d = ERROR_NOT_ENOUGH_MEMORY;
}
}
}
if(d == NO_ERROR) {
//
// Ensure that there's at least one item in the list.
//
if(TotalVals) {
try {
*List = Values1;
*Count = TotalVals;
Values1 = NULL; // no longer ours to free
} except(EXCEPTION_EXECUTE_HANDLER) {
d = ERROR_INVALID_PARAMETER;
}
} else {
try {
if(*List = MyMalloc(sizeof(PTSTR))) {
if(**List = DuplicateString(TEXT("A:\\"))) {
*Count = 1;
} else {
MyFree(*List);
d = ERROR_NOT_ENOUGH_MEMORY;
}
} else {
d = ERROR_NOT_ENOUGH_MEMORY;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// Note there is a tiny window for a memory leak here,
// if List pointer went bad between the MyMalloc
// and the DuplicateString. Oh well.
//
d = ERROR_INVALID_PARAMETER;
}
}
}
}
if (Values1) {
for(u=0; u<TotalVals; u++) {
if(Values1[u]) {
MyFree(Values1[u]);
}
}
MyFree(Values1);
}
if (Values2) {
for(u=0; u<NumVals2; u++) {
MyFree(Values2[u]);
}
MyFree(Values2);
}
}
LeaveCriticalSection(&MruCritSect);
SetLastError(d);
return(d == NO_ERROR);
}
BOOL
SetupFreeSourceListA(
IN OUT PCSTR **List,
IN UINT Count
)
{
//
// Not really ansi/unicode specific
//
return(SetupFreeSourceListW((PCWSTR **)List,Count));
}
BOOL
SetupFreeSourceListW(
IN OUT PCWSTR **List,
IN UINT Count
)
/*++
Routine Description:
This routine frees a source list as returned by SetupQuerySourceList.
Arguments:
Return Value:
--*/
{
UINT u;
BOOL b;
PCWSTR *list;
b = TRUE;
try {
list = *List;
for(u=0; u<Count; u++) {
if(list[u]) {
MyFree(list[u]);
}
}
MyFree(list);
*List = NULL;
} except(EXCEPTION_EXECUTE_HANDLER) {
b = FALSE;
}
return(b);
}
DWORD
pSetupGetList(
IN DWORD Flags,
OUT PCTSTR **List,
OUT PUINT Count,
OUT PBOOL NoBrowse
)
{
DWORD d;
if(!LockMruCritSect()) {
return ERROR_NOT_ENOUGH_MEMORY;
}
*NoBrowse = MruNoBrowse;
d = SetupQuerySourceList(Flags,List,Count) ? NO_ERROR : GetLastError();
LeaveCriticalSection(&MruCritSect);
return(d);
}
PTSTR
pSetupGetDefaultSourcePath(
IN HINF InfHandle,
IN DWORD Flags,
OUT PDWORD InfSourceMediaType
)
/*++
Routine Description:
This routine returns the default path string to be used for the
specified INF. It also returns the type of path, either a normal
file path or a URL.
The caller must free the string returned (if any) via MyFree.
Arguments:
InfHandle - Supplies a handle to the INF whose default source path
is to be retrieved.
Flags
- if SRCPATH_USEINFLOCATION bit is set, then return the directory
where the INF is located (with a source media type of SPOST_PATH)
in the case where either (a) the PNF has no source media information,
or (b) the PNF has SPOST_URL information.
- if SRCPATH_USEPNFINFORMATION bit is set, then the actual PNF
information (whether path or URL) is returned, and if the PNF
has no source media information, then the system source path is
returned.
InfSourceMediaType - Supplies the address of a variable that receives
the type of path returned. May be one of the following values:
SPOST_PATH - Standard file path
SPOST_URL - Internet path
Return Value:
If InfSourceMediaType is returned as SPOST_PATH, then a path will
always be returned, unless we're out of memory (or, if
DefaultPathIsInfLocation is TRUE, another possibility is that we hit an
exception). GetLastError() may be used in this case to indicate the cause of
failure).
If InfSourceMediaType is returned as SPOST_URL, then the return value
will be NULL if the default Code Download Manager URL is used (or if we ran
out of memory), otherwise it will be the specific URL to be used.
In either case, GetLastError() may be called to determine the cause of
failure (in the case of SPOST_URL for a NULL InfSourceMediaType,
GetLastError() will return NO_ERROR if we didn't fail (i.e., we meant to
return NULL because the INF came from the CDM website).
--*/
{
PTSTR InfSourcePath = NULL, p;
DWORD Err;
*InfSourceMediaType = SPOST_PATH;
Err = NO_ERROR;
//
// Lock the INF, so that we can get it's 'InfSourcePath' value, if present.
//
if(LockInf((PLOADED_INF)InfHandle)) {
try {
if(((PLOADED_INF)InfHandle)->InfSourcePath) {
InfSourcePath = DuplicateString(((PLOADED_INF)InfHandle)->InfSourcePath);
if(!InfSourcePath) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
}
*InfSourceMediaType = ((PLOADED_INF)InfHandle)->InfSourceMediaType;
if(Flags & SRCPATH_USEINFLOCATION) {
//
// Caller has requested that we default to the INF's source
// location when there's no SPOST_PATH info.
//
if(*InfSourceMediaType != SPOST_PATH) {
if(InfSourcePath) {
MyFree(InfSourcePath);
InfSourcePath = NULL;
}
*InfSourceMediaType = SPOST_PATH;
}
if(!InfSourcePath) {
//
// Don't have an INF source path--use the INF's present
// location.
//
InfSourcePath = DuplicateString(((PLOADED_INF)InfHandle)->VersionBlock.Filename);
if(InfSourcePath) {
//
// OK, we duplicated the INF's full pathname, now
// truncate it to just the path part.
//
p = (PTSTR)pSetupGetFileTitle(InfSourcePath);
*p = TEXT('\0');
if(((p - InfSourcePath) != 3) ||
lstrcmp(CharNext(InfSourcePath), TEXT(":\\"))) {
//
// The path is not an "A:\" type path, so truncate
//
p = CharPrev(InfSourcePath, p);
MYASSERT(*p == TEXT('\\'));
if(p > InfSourcePath) {
*p = TEXT('\0');
}
}
} else {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
}
}
clean0: ; // nothing to do.
} except(EXCEPTION_EXECUTE_HANDLER) {
if(InfSourcePath) {
MyFree(InfSourcePath);
InfSourcePath = NULL;
}
Err = ERROR_INVALID_PARAMETER;
}
UnlockInf((PLOADED_INF)InfHandle);
}
if((Flags & SRCPATH_USEINFLOCATION) && !InfSourcePath) {
//
// We either hit out of memory or an exception--make sure media type
// specifies SPOST_PATH before returning failure.
//
*InfSourceMediaType = SPOST_PATH;
MYASSERT(Err != NO_ERROR);
SetLastError(Err);
return NULL;
}
if(!InfSourcePath && (*InfSourceMediaType == SPOST_PATH) && (Flags & SRCPATH_USEPNFINFORMATION)) {
//
// There's not an oem location associated with this INF, so use our default
// source path.
//
InfSourcePath = DuplicateString(SystemSourcePath);
if(!InfSourcePath) {
Err = ERROR_NOT_ENOUGH_MEMORY;
}
}
SetLastError(Err);
return InfSourcePath;
}
VOID
pSetupStripTrailingPlatformComponent(
IN OUT PTSTR *Paths,
IN OUT PDWORD NumPaths
)
{
PTSTR Path;
DWORD PathCount;
DWORD NewPathCount;
DWORD PathIndex;
DWORD DupIndex;
DWORD FirstIndex;
DWORD HoleCount;
PCTSTR Component;
UINT ComponentLength;
UINT PathLength;
int ComponentOffset;
UINT ComponentIndex;
//
// Do this for all paths in the array passed in by the caller.
//
PathCount = *NumPaths;
for(PathIndex=0; PathIndex<PathCount; PathIndex++) {
Path = Paths[PathIndex];
if(!Path) {
//
// skip holes
//
continue;
}
//
// See if the final path component matches one of the ones
// we care about.
//
PathLength = lstrlen(Path);
for(ComponentIndex=0; PlatformPathComponents[ComponentIndex]; ComponentIndex++) {
Component = PlatformPathComponents[ComponentIndex];
ComponentLength = lstrlen(Component);
ComponentOffset = PathLength - ComponentLength;
if((ComponentOffset > 0) && (lstrcmpi(Path+ComponentOffset,Component)==0)) {
//
// Got a match. Strip off the final component.
// Leave a trailing backslash if we're dealing with the root.
//
Path[ComponentOffset] = TEXT('\0');
if((Path[1] == TEXT(':')) && !Path[2]) {
Path[2] = TEXT('\\');
Path[3] = 0;
}
//
// Remove duplicate, preserving the first instance
//
for(FirstIndex=0 ; FirstIndex<PathIndex ; FirstIndex++) {
if(lstrcmpi(Paths[FirstIndex],Path) == 0) {
//
// we've found first instance
// and it's earlier than PathIndex
// so we'll end up deleting entry at PathIndex
Path = Paths[FirstIndex];
break;
}
}
for(DupIndex = FirstIndex+1;DupIndex<PathCount;DupIndex++) {
if(lstrcmpi(Paths[DupIndex],Path) == 0) {
//
// eliminate duplicate
//
MyFree(Paths[DupIndex]);
Paths[DupIndex] = NULL; // new hole - handle holes later
}
}
//
// only strip one component
//
break;
}
}
}
//
// now fix up 'holes' preserving order
//
HoleCount = 0;
for(PathIndex=0; PathIndex<PathCount; PathIndex++) {
if(!Paths[PathIndex]) {
//
// count holes
//
HoleCount++;
} else if(HoleCount) {
//
// shift down by number of holes found
//
Paths[PathIndex-HoleCount] = Paths[PathIndex];
}
}
NewPathCount = PathCount-HoleCount;
for(PathIndex = PathCount; PathIndex < NewPathCount; PathIndex++) {
Paths[PathIndex] = NULL;
}
*NumPaths = NewPathCount;
}