Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

966 lines
25 KiB

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
consys.c
Abstract:
This module contains CONFIG.SYS related parsing routines.
Author:
Ofer Porat (oferp) 8-Nov-1992
Environment:
User Mode Only
Revision History:
--*/
#include "os2ssrtl.h"
#define WBLANK(ch) ((ch) == L' ' || (ch) == L'\t')
typedef struct _INDEX_T {
UNICODE_STRING Src;
UNICODE_STRING Dest;
} INDEX_T, *PINDEX_T;
//
// This routine skips over white space in a unicode string
// either forward or backward.
// Str -- address of pointer to string.
// Direction -- either FWD or BWD.
//
VOID
Or2SkipWWS(
IN OUT PWSTR *Str,
IN LONG Direction
)
{
PWSTR q = *Str;
while (WBLANK(*q)) {
q += Direction;
}
*Str = q;
}
//
// This routine converts a null terminated unicode string to upper case.
// It is similar to wcsupr(). It exists because I'm not sure wcsupr()
// actually uses the Unicode convention.
//
// Str -- string to convert.
//
VOID
Or2UnicodeStrupr(
IN OUT PWSTR Str
)
{
while (*Str != UNICODE_NULL) {
*Str = RtlUpcaseUnicodeChar(*Str);
Str++;
}
}
//
// This routine compares 2 null-terminated unicode strings. The comparison
// has a count limiting the number of chars compared, and the comparison is
// case insensitive. It is similar to wcsnicmp(). It exists because I'm not
// sure wcsnicmp() actually uses the Unicode convention.
//
// Str1, Str2 -- strings to compare.
// Count -- max # of chars to compare.
// return value -- TRUE if they're equal to within Count characters. FALSE otherwise.
//
BOOLEAN
Or2UnicodeEqualCI(
IN PWSTR Str1,
IN PWSTR Str2,
IN ULONG Count
)
{
while (Count != 0) {
if (*Str1 == UNICODE_NULL) {
if (*Str2 == UNICODE_NULL) {
return(TRUE);
}
return(FALSE);
}
if (RtlUpcaseUnicodeChar(*Str1) != RtlUpcaseUnicodeChar(*Str2)) {
return(FALSE);
}
Str1++; Str2++; Count--;
}
return(TRUE);
}
//
// This routine appends a source path list to a destination path list. multiple occurences
// of the same path are removed.
//
// HeapHandle -- a handle to a heap for temporary allocations
// SrcPath -- a null terminated unicode string. It must be in uppercase for elimination of
// multiple paths to occur. This path list is appended to DestPath.
// DestPath -- an already existing counted unicode string containing the path list to be appended
// to. The case is unimportant. The result is stored back in this string.
// ExpandIt -- Should be TRUE in DestPath contains unexpanded %...% type strings. FALSE otherwise.
// return value -- TRUE on success, FALSE otherwise.
//
// Possible Errors Effect
// =============== ======
// allocation failure Does nothing, return value FALSE
// can't expand the
// DestPath Does nothing, return value FALSE
// DestPath MaxLen
// exceeded during
// append Stop on the last path that fits, return value TRUE
//
BOOLEAN
Or2AppendPathToPath(
IN PVOID HeapHandle,
IN PWSTR SrcPath,
IN OUT PUNICODE_STRING DestPath,
IN BOOLEAN ExpandIt
)
{
WCHAR wch;
WCHAR wch1;
WCHAR wch2;
PWSTR p;
PWSTR q;
PWSTR r;
PWSTR t;
USHORT l;
USHORT addsemi;
UNICODE_STRING Expanded;
UNICODE_STRING Tmp;
BOOLEAN Found;
NTSTATUS Status;
Expanded.Buffer = (PWSTR) RtlAllocateHeap(HeapHandle, 0, DestPath->MaximumLength + sizeof(WCHAR));
if (Expanded.Buffer == NULL) {
#if DBG
IF_OD2_DEBUG( INIT ) {
KdPrint(("Or2AppendPathPath: can't allocate Expanded from heap\n"));
}
#endif
return(FALSE);
}
Expanded.MaximumLength = DestPath->MaximumLength;
if (ExpandIt) {
Status = RtlExpandEnvironmentStrings_U(NULL,
DestPath,
&Expanded,
NULL);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OD2_DEBUG( INIT ) {
KdPrint(("Or2AppendPathToPath: can't expand environment strings, rc = %lx\n", Status));
}
#endif
RtlFreeHeap(HeapHandle, 0, Expanded.Buffer);
return(FALSE);
}
} else {
RtlCopyUnicodeString(&Expanded, DestPath);
}
Expanded.Buffer[Expanded.Length/sizeof(WCHAR)] = UNICODE_NULL;
Or2UnicodeStrupr(Expanded.Buffer);
addsemi = 2;
if (Expanded.Length == 0 ||
Expanded.Buffer[Expanded.Length/sizeof(WCHAR) - 1] == L';') {
addsemi = 0;
}
while (TRUE) {
Or2SkipWWS(&SrcPath, FWD);
wch = *SrcPath;
if (wch == UNICODE_NULL) {
break;
}
if (wch == L';') {
SrcPath++;
continue;
}
p = wcschr(SrcPath, L';');
if (p == NULL) {
p = SrcPath + wcslen(SrcPath);
q = p - 1;
} else {
q = p - 1;
p++;
}
Or2SkipWWS(&q, BWD);
l = q - SrcPath + 1;
wch = *(q+1);
*(q+1) = UNICODE_NULL;
t = Expanded.Buffer;
Found = FALSE;
while (TRUE) {
r = wcsstr(t, SrcPath);
if (r == NULL) {
break;
}
if (r == Expanded.Buffer) {
wch1 = L';';
} else {
wch1 = *(r-1);
}
wch2 = r[l];
if ((wch1 == L';' || WBLANK(wch1)) &&
(wch2 == L';' || WBLANK(wch2) || wch2 == UNICODE_NULL)) {
Found = TRUE;
break;
}
t = r + l;
}
*(q+1) = wch;
if (Found) {
SrcPath = p;
continue;
}
Tmp.Buffer = SrcPath;
Tmp.Length = l * sizeof(WCHAR);
Tmp.MaximumLength = Tmp.Length;
if (Expanded.Length + addsemi + Tmp.Length > Expanded.MaximumLength ||
DestPath->Length + addsemi + Tmp.Length > DestPath->MaximumLength) {
#if DBG
IF_OD2_DEBUG( INIT ) {
KdPrint(("Or2AppendPathToPath: Path buffer overflow\n"));
}
#endif
break; // out of string space, terminate processing.
}
if (addsemi) {
RtlAppendUnicodeToString(&Expanded, L";");
RtlAppendUnicodeToString(DestPath, L";");
} else {
addsemi = 2;
}
RtlAppendUnicodeStringToString(&Expanded, &Tmp);
RtlAppendUnicodeStringToString(DestPath, &Tmp);
Expanded.Buffer[Expanded.Length/sizeof(WCHAR)] = UNICODE_NULL;
SrcPath = p;
}
DestPath->Buffer[DestPath->Length / sizeof(WCHAR)] = UNICODE_NULL;
RtlFreeHeap(HeapHandle, 0, Expanded.Buffer);
return(TRUE);
}
//
// This routine takes a counted unicode string containing a path-list with %...% type strings in it.
// It prepares a pool of characters containing the expansions of paths with %...% strings in them.
// Also prepared is an index table. Each entry in the index table contains a counted string pointing
// to the point in DestPath where the unexpanded path is, and a counted string pointing to the the pool
// where the expanded path is.
//
// SrcPath -- a counted unicode string containing the path-list to generate an index for.
// Pool -- a buffer containing space for SrcPath.MaximumLength wide characters. This is where
// the expanded strings will be stored.
// Ind -- a buffer containing enough space to allocate as many index entries as necessary to index
// the path list. One way to know how many may be necessary is to count the # of semicolons in
// SrcPath.
// IndSize -- will contain the number of entries placed in Ind on exit.
//
// Possible Errors Effect
// =============== ======
// SrcPath.MaximumLength
// characters are not
// enough to store the
// pool of expanded env
// strings. Stops after the last expanded env string that fits in the pool.
//
static
VOID
Or2IndexPath(
IN PUNICODE_STRING SrcPath,
OUT PWSTR Pool,
OUT PINDEX_T Ind,
OUT PULONG IndSize
)
{
PWSTR CurPool = Pool;
USHORT PoolQuota = SrcPath->MaximumLength;
ULONG IndEx = 0;
PWCHAR wp = SrcPath->Buffer;
PWCHAR wq, wr;
BOOLEAN HasPercent;
NTSTATUS Status;
for (; ; ) {
Or2SkipWWS(&wp, FWD);
if (*wp == UNICODE_NULL) {
break;
}
HasPercent = FALSE;
wq = wp;
while (*wq != L';' && *wq != UNICODE_NULL) {
if (*wq == L'%') {
HasPercent = TRUE;
}
wq++;
}
if (*wq == L';') {
wr = wq + 1;
} else {
wr = wq;
}
if (!HasPercent) {
wp = wr;
continue;
}
wq--;
Or2SkipWWS(&wq, BWD);
Ind[IndEx].Dest.Buffer = wp;
Ind[IndEx].Dest.Length = (wq - wp + 1) * sizeof(WCHAR);
Ind[IndEx].Dest.MaximumLength = Ind[IndEx].Dest.Length;
wp = wr;
Ind[IndEx].Src.Buffer = CurPool;
Ind[IndEx].Src.MaximumLength = PoolQuota;
Status = RtlExpandEnvironmentStrings_U(NULL,
&Ind[IndEx].Dest,
&Ind[IndEx].Src,
NULL);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OD2_DEBUG( INIT ) {
KdPrint(("Or2IndexPath: can't expand environment strings, rc = %lx\n", Status));
}
#endif
break;
}
PoolQuota -= Ind[IndEx].Src.Length;
CurPool += Ind[IndEx].Src.Length / sizeof(WCHAR);
Ind[IndEx].Src.MaximumLength = Ind[IndEx].Src.Length;
IndEx++;
}
*IndSize = IndEx;
}
//
// This routine takes a destination path-list and a source path list. The destination path-list
// is completely replaced with the source path list. The destination list may contain %...% strings.
// The source list is not expected to contain %...% strings. While the replacement is done, any
// paths in the source which already exist in the destination in a form containing %...% strings, are
// retained with their original %...% form.
//
// HeapHandle -- a handle to a heap for temporary allocations
// SrcPath -- a null terminated unicode string containing the replacing path-list.
// DestPath -- an already existing counted unicode string containing the path list to be replaced
// to. The result is stored back in this string.
// return value -- TRUE on success, FALSE otherwise.
//
// Possible Errors Effect
// =============== ======
// allocation failure Does nothing, return value FALSE
// DestPath MaxLen
// exceeded during
// replace Stop on the last path that fits, return value TRUE
//
BOOLEAN
Or2ReplacePathByPath(
IN PVOID HeapHandle,
IN PWSTR SrcPath,
IN OUT PUNICODE_STRING DestPath
)
{
PINDEX_T Ind;
PWSTR Pool;
PWSTR p;
PWSTR q;
WCHAR wch;
ULONG IndSize;
ULONG SemiCount;
ULONG i;
USHORT addsemi;
UNICODE_STRING Target;
UNICODE_STRING Tmp;
Target.Buffer = (PWSTR) RtlAllocateHeap(HeapHandle, 0, DestPath->MaximumLength + sizeof(WCHAR));
if (Target.Buffer == NULL) {
#if DBG
IF_OD2_DEBUG( INIT ) {
KdPrint(("Or2ReplacePathByPath: can't allocate Target from heap\n"));
}
#endif
return(FALSE);
}
Target.MaximumLength = DestPath->MaximumLength;
Target.Length = 0;
Pool = (PWSTR) RtlAllocateHeap(HeapHandle, 0, (ULONG) DestPath->MaximumLength);
if (Pool == NULL) {
#if DBG
IF_OD2_DEBUG( INIT ) {
KdPrint(("Or2ReplacePathByPath: can't allocate Pool from heap\n"));
}
#endif
RtlFreeHeap(HeapHandle, 0, Target.Buffer);
return(FALSE);
}
SemiCount = 1;
for (i = 0; i < DestPath->Length/sizeof(WCHAR); i++) {
if (DestPath->Buffer[i] == L';') {
SemiCount++;
}
}
Ind = (PINDEX_T) RtlAllocateHeap(HeapHandle, 0, SemiCount * sizeof(INDEX_T));
if (Ind == NULL) {
#if DBG
IF_OD2_DEBUG( INIT ) {
KdPrint(("Or2ReplacePathByPath: can't allocate Ind from heap\n"));
}
#endif
RtlFreeHeap(HeapHandle, 0, Pool);
RtlFreeHeap(HeapHandle, 0, Target.Buffer);
return(FALSE);
}
Or2IndexPath(DestPath, Pool, Ind, &IndSize);
addsemi = 0;
while (TRUE) {
Or2SkipWWS(&SrcPath, FWD);
wch = *SrcPath;
if (wch == UNICODE_NULL) {
break;
}
if (wch == L';') {
SrcPath++;
continue;
}
p = wcschr(SrcPath, L';');
if (p == NULL) {
p = SrcPath + wcslen(SrcPath);
q = p - 1;
} else {
q = p - 1;
p++;
}
Or2SkipWWS(&q, BWD);
Tmp.Buffer = SrcPath;
Tmp.Length = (q - SrcPath + 1) * sizeof(WCHAR);
Tmp.MaximumLength = Tmp.Length;
for (i = 0; i < IndSize; i++) {
if (RtlEqualUnicodeString(&Tmp, &Ind[i].Src, TRUE)) {
Tmp = Ind[i].Dest;
break;
}
}
if (Target.Length + addsemi + Tmp.Length > Target.MaximumLength) {
#if DBG
IF_OD2_DEBUG( INIT ) {
KdPrint(("Or2ReplacePathByPath: Path buffer overflow\n"));
}
#endif
break; // out of string space, terminate processing.
}
if (addsemi) {
RtlAppendUnicodeToString(&Target, L";");
} else {
addsemi = 2;
}
RtlAppendUnicodeStringToString(&Target, &Tmp);
SrcPath = p;
}
RtlCopyUnicodeString(DestPath, &Target);
DestPath->Buffer[DestPath->Length/sizeof(WCHAR)] = UNICODE_NULL;
RtlFreeHeap(HeapHandle, 0, Ind);
RtlFreeHeap(HeapHandle, 0, Pool);
RtlFreeHeap(HeapHandle, 0, Target.Buffer);
return(TRUE);
}
//
// This routine appends a terminating semicolon to a path list, if that path list
// does not have a semicolon in it (in other words, if the path list contains only
// one path).
//
// Str - The string to do semicolon processing on.
//
VOID
Or2CheckSemicolon(
IN OUT PUNICODE_STRING Str
)
{
USHORT i;
BOOLEAN flag;
if (Str->Length == 0 || Str->Length >= Str->MaximumLength) {
return;
}
flag = FALSE;
for(i = 0; i < Str->Length/ (USHORT) sizeof(WCHAR); i++) {
if (Str->Buffer[i] == L';') {
flag = TRUE;
break;
}
}
if (!flag) {
Str->Buffer[i++] = L';';
Str->Buffer[i] = UNICODE_NULL;
Str->Length += sizeof(WCHAR);
}
}
//
// This routine fetches a path-list type environment variable from the registry.
//
// Data - an unallocated counted unicode string to hold the result.
// HeapHandle -- a handle to a heap where we allocate from.
// MaxSiz -- The MaximumLength Data should be given.
// EnvKey -- a handle to the registry key in which the environment to be searched is.
// ValueName -- a null-terminated unicode name of the registry value to fetch.
// ExpandIt -- If TRUE, the registry value is expanded for %..% type strings before returning it.
// return value -- TRUE on success, FALSE otherwise.
//
// Possible Errors Effect
// =============== ======
// allocation failure Data.Buffer = NULL, return value FALSE
// requested env variable
// doesn't exist, or
// can't query its value Data is allocated and given 0 length, return value TRUE
// can't expand the
// env variable Data is allocated and given 0 length, return value TRUE
//
BOOLEAN
Or2GetEnvPath(
OUT PUNICODE_STRING Data,
IN PVOID HeapHandle,
IN USHORT MaxSiz,
IN HANDLE EnvKey,
IN PWSTR ValueName,
IN BOOLEAN ExpandIt
)
{
PKEY_VALUE_PARTIAL_INFORMATION Buf;
UNICODE_STRING ExpBuf;
UNICODE_STRING ValueName_U;
UNICODE_STRING tmp;
ULONG ResultLength;
ULONG l1, l2;
NTSTATUS Status;
Data->Buffer = NULL;
l1 = ((ULONG) MaxSiz + 1) * sizeof(WCHAR) + sizeof(KEY_VALUE_PARTIAL_INFORMATION);
Buf = (PKEY_VALUE_PARTIAL_INFORMATION) RtlAllocateHeap(HeapHandle, 0, l1);
if (Buf == NULL) {
#if DBG
IF_OD2_DEBUG( INIT ) {
KdPrint(("Or2GetEnvPath: can't allocate Buf from heap\n"));
}
#endif
return(FALSE);
}
if (ExpandIt) {
l2 = ((ULONG) MaxSiz + 1) * sizeof(WCHAR);
ExpBuf.Buffer = (PWSTR) RtlAllocateHeap(HeapHandle, 0, l2);
if (ExpBuf.Buffer == NULL) {
#if DBG
IF_OD2_DEBUG( INIT ) {
KdPrint(("Or2GetEnvPath: can't allocate ExpBuf from heap\n"));
}
#endif
RtlFreeHeap(HeapHandle, 0, Buf);
return(FALSE);
}
ExpBuf.MaximumLength = MaxSiz * sizeof(WCHAR);
}
RtlInitUnicodeString(&ValueName_U, ValueName);
Status = NtQueryValueKey(EnvKey,
&ValueName_U,
KeyValuePartialInformation,
Buf,
l1,
&ResultLength
);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OD2_DEBUG( INIT ) {
KdPrint(("OS2SSRTL(Or2GetEnvPath): FAILED - NtQueryValueKey %lx\n", Status));
}
#endif
if (ExpandIt) {
RtlFreeHeap(HeapHandle, 0, Buf);
ExpBuf.Length = 0;
ExpBuf.Buffer[0] = UNICODE_NULL;
*Data = ExpBuf;
} else {
*((PWSTR) Buf) = UNICODE_NULL;
Data->Buffer = (PWSTR) Buf;
Data->Length = 0;
Data->MaximumLength = MaxSiz * sizeof(WCHAR);
}
return(TRUE);
}
if (ExpandIt) {
RtlInitUnicodeString(&tmp, (PWSTR) Buf->Data);
Status = RtlExpandEnvironmentStrings_U(NULL,
&tmp,
&ExpBuf,
NULL);
RtlFreeHeap(HeapHandle, 0, Buf);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OD2_DEBUG( INIT ) {
KdPrint(("OS2SSRTL(Or2GetEnvPath): FAILED - RtlExpandEnvironmentStrings_U %lx\n", Status));
}
#endif
ExpBuf.Buffer[0] = UNICODE_NULL;
ExpBuf.Length = 0;
*Data = ExpBuf;
return(TRUE);
}
ExpBuf.Buffer[ExpBuf.Length/sizeof(WCHAR)] = UNICODE_NULL;
*Data = ExpBuf;
} else {
l2 = Buf->DataLength;
RtlMoveMemory(Buf, Buf->Data, l2);
Data->Buffer = (PWSTR) Buf;
Data->Length = (USHORT) (l2 - sizeof(WCHAR));
Data->MaximumLength = MaxSiz * sizeof(WCHAR);
}
return(TRUE);
}
//
// This is a general routine to parse an environment. It loops over the lines in the environment, and
// identifies the variable in each line. For each variable, it searches a user dispatch table, and if
// that dispatch table contains an entry for this variable, it calls the user supplied routine with the
// name and value of the variable.
//
// Environment -- The environment to process. This is either in multi-string format, or a
// (null|^Z)-terminated string consisting of lines separated by crlfs.
// DispatchTable -- A dispatch table used to process the environment.
// NumberOfDispatchItems -- Contains the number of entries in DispatchTable.
// DelimOption -- specifies whether Environment is a multi-string (NULL_DELIM) or a string of crlf
// separated lines (CRLF_DELIM).
//
// Description of the DispatchTable:
// This is an array of structures of type ENVIRONMENT_DISPATCH_TABLE_ENTRY.
// Each entry specifies a particular variable which should be handled.
// an entry contains:
// -- the name of the variable to operate on (this is case-insensitive).
// -- a string of possible characters that serve as delimiters for the variable name (e.g. =, space, tab).
// -- a DispatchFunction. This function is dispatched with 6 parameters:
// -- the index in the dispatch table which triggered this call.
// -- a user defined pointer, see below.
// -- a pointer to the variable name in the environment.
// -- length of the variable name (in chars).
// -- a pointer to the variable value in the environment.
// -- length of the variable value (in chars).
// -- a user defined pointer that is passed to the dispatch function.
//
// The dispatch function may do anything it likes with the information it gets, and it
// is not expected to return any value.
//
// A special value of "*" in the variable name field of an entry indicates that any variable name
// is to be processed by the DispatchFunction. The DispatchFunction can figure out the name of
// the variable it was called for by examining its parameters. If this option is used together
// with a Delimeters value of NULL, no delimiter will be searched for, the line will be passed
// as is with Name and Value both pointing to the beginning of the line (and ValueLen containing
// its length)
//
VOID
Or2IterateEnvironment(
IN PWSTR Environment,
IN ENVIRONMENT_DISPATCH_TABLE DispatchTable,
IN ULONG NumberOfDispatchItems,
IN ULONG DelimOption
)
{
PWSTR Src;
PWSTR Tmp;
PWSTR Eol;
PWSTR NextLine;
ULONG i, m;
LONG l;
BOOLEAN Flag;
if (NumberOfDispatchItems == 0 || Environment == NULL || DispatchTable == NULL) {
return;
}
Src = Environment;
while (*Src != UNICODE_NULL) {
Or2SkipWWS(&Src, FWD);
if (DelimOption == CRLF_DELIM &&
*Src == L'\32') { // ^Z terminates file on CRLF delim texts
break;
}
for (i = 0; i < NumberOfDispatchItems; i++) {
if (DispatchTable[i].VarName[0] == L'*') {
if (DispatchTable[i].Delimiters == NULL) {
//
// No delimeter required. If the line is empty
// we don't process it.
//
if (*Src == UNICODE_NULL) {
continue;
}
if (DelimOption == CRLF_DELIM &&
(*Src == L'\r' || *Src == L'\n')) {
continue;
}
l = -1; // process the line from start
break;
}
Tmp = Src;
l = 0;
while (TRUE) {
if (*Tmp == UNICODE_NULL ||
(DelimOption == CRLF_DELIM && (*Tmp == L'\r' || *Tmp == L'\n' || *Tmp == L'\32'))) {
Flag = FALSE;
break;
}
if (wcschr(DispatchTable[i].Delimiters, RtlUpcaseUnicodeChar(*Tmp)) != NULL) {
Flag = TRUE;
break;
}
Tmp++;
l++;
}
if (Flag) {
break;
}
continue;
}
l = wcslen(DispatchTable[i].VarName);
if (Or2UnicodeEqualCI(Src, DispatchTable[i].VarName, (ULONG)l) &&
Src[l] != UNICODE_NULL &&
wcschr(DispatchTable[i].Delimiters, RtlUpcaseUnicodeChar(Src[l])) != NULL) {
break;
}
}
if (DelimOption == NULL_DELIM) {
Eol = Src + wcslen(Src);
NextLine = Eol + 1;
} else {
Eol = wcspbrk(Src, L"\r\n\32");
if (Eol == NULL) {
NextLine = Eol = Src + wcslen(Src);
} else {
NextLine = Eol;
if (*Eol != L'\32') {
while (*NextLine == L'\r' || *NextLine == L'\n') {
NextLine++;
}
}
}
}
if (i == NumberOfDispatchItems || DispatchTable[i].DispatchFunction == NULL) {
Src = NextLine;
continue;
}
Tmp = Src + l + 1;
Or2SkipWWS(&Tmp, FWD);
if (Eol != Tmp) {
Eol--;
Or2SkipWWS(&Eol, BWD);
m = (Eol - Tmp) + 1;
} else {
m = 0;
}
DispatchTable[i].DispatchFunction(i, DispatchTable[i].UserParameter, Src, l, Tmp, m);
Src = NextLine;
}
}
//
// Following is useful Dispatch Function for use with Or2IterateEnvironment.
// It copies the parameters passed to it into a structure of type
// ENVIRONMENT_SEARCH_RECORD. A pointer to the structure to fill must be
// passed through the UserParameter in the Dispatch Table.
//
VOID
Or2FillInSearchRecordDispatchFunction(
IN ULONG DispatchTableIndex,
IN PVOID UserParameter,
IN PWSTR Name,
IN ULONG NameLen,
IN PWSTR Value,
IN ULONG ValueLen
)
{
PENVIRONMENT_SEARCH_RECORD p = (PENVIRONMENT_SEARCH_RECORD) UserParameter;
p->DispatchTableIndex = DispatchTableIndex;
p->Name = Name;
p->NameLen = NameLen;
p->Value = Value;
p->ValueLen = ValueLen;
}