Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

884 lines
25 KiB

/*++
Copyright (c) 1989-91 Microsoft Corporation
Module Name:
listfunc.c
Abstract:
This module contains functions which canonicalize and traverse lists.
The following functions are defined:
NetpwListCanonicalize
NetpwListTraverse
(FixupAPIListElement)
Author:
Danny Glasser (dannygl) 14 June 1989
Notes:
There are currently four types of lists supported by these
functions:
UI/Service input list - Leading and trailing delimiters
are allowed, multiple delimiters are allowed between
elements, and the full set of delimiter characters is
allowed (space, tab, comma, and semicolon). Note that
elements which contain a delimiter character must be
quoted. Unless explicitly specified otherwise, all UIs
and services must accept all input lists in this format.
API list - Leading and trailing delimiters are not allowed,
multiple delimiters are not allowed between elements,
and there is only one delimiter character (space). Elements
which contain a delimiter character must be quoted.
Unless explicitly specified otherwise, all lists provided
as input to API functions must be in this format, and all
lists generated as output by API functions will be in this
format.
Search-path list - The same format as an API list, except that
the delimiter is a semicolon. This list is designed as
input to the DosSearchPath API.
Null-null list - Each element is terminated by a null
byte and the list is terminated by a null string
(that is, a null byte immediately following the null
byte which terminates the last element). Clearly,
multiple, leading, and trailing delimiters are not
supported. Elements do not need to be quoted. An empty
null-null list is simply a null string. This list format
is designed for internal use.
NetpListCanonicalize() accepts all UI/Service, API, and null-null
lists on input and produces API, search-path, and null-null lists
on output. NetpListTraverse() supports null-null lists only.
Revision History:
--*/
#include "nticanon.h"
//
// prototypes
//
STATIC
DWORD
FixupAPIListElement(
IN LPTSTR Element,
IN LPTSTR* pElementTerm,
IN LPTSTR BufferEnd,
IN TCHAR cDelimiter
);
//
// routines
//
NET_API_STATUS
NetpwListCanonicalize(
IN LPTSTR List,
IN LPTSTR Delimiters OPTIONAL,
OUT LPTSTR Outbuf,
IN DWORD OutbufLen,
OUT LPDWORD OutCount,
OUT LPDWORD PathTypes,
IN DWORD PathTypesLen,
IN DWORD Flags
)
/*++
Routine Description:
NetpListCanonicalize produces the specified canonical version of
the list, validating and/or canonicalizing the individual list
elements as specified by <Flags>.
Arguments:
List - The list to canonicalize.
Delimiters - A string of valid delimiters for the input list.
A null pointer or null string indicates that the
input list is in null-null format.
Outbuf - The place to store the canonicalized version of the list.
OutbufLen - The size, in bytes, of <Outbuf>.
OutCount - The place to store the number of elements in the
canonicalized list.
PathTypes - The array in which to store the types of each of the paths
in the canonicalized list. This parameter is only used if
the NAMETYPE portion of the flags parameter is set to
NAMETYPE_PATH.
PathTypesLen- The number of elements in <pflPathTypes>.
Flags - Flags to determine operation. Currently defined values are:
rrrrrrrrrrrrrrrrrrrrmcootttttttt
where:
r = Reserved. MBZ.
m = If set, multiple, leading, and trailing delimiters are
allowed in the input list.
c = If set, each of the individual list elements are
validated and canonicalized. If not set, each of the
individual list elements are validated only. This
bit is ignored if the NAMETYPE portion of the flags is
set to NAMETYPE_COPYONLY.
o = Type of output list. Currently defined types are
API, search-path, and null-null.
t = The type of the objects in the list, for use in
canonicalization or validation. If this value is
NAMETYPE_COPYONLY, type is irrelevant; a canonical list
is generated but no interpretation of the list elements
is done. If this value is NAMETYPE_PATH, the list
elements are assumed to be pathnames; NetpPathType is
run on each element, the results are stored in
<pflPathTypes>, and NetpPathCanonicalize is run on
each element (if appropriate). Any other values for
this is considered to be the type of the list elements
and is passed to NetpName{Validate,Canonicalize} as
appropriate.
Manifest values for these flags are defined in NET\H\ICANON.H.
Return Value:
0 if successful.
The error number (> 0) if unsuccessful.
Possible error returns include:
ERROR_INVALID_PARAMETER
NERR_TooManyEntries
NERR_BufTooSmall
--*/
{
NET_API_STATUS rc = 0;
BOOL NullInputDelimiter;
LPTSTR next_string;
DWORD len;
DWORD list_len = 0; // cumulative input buffer length
LPTSTR Input;
LPTSTR OutPtr;
LPTSTR OutbufEnd;
DWORD OutElementCount;
DWORD DelimiterLen;
LPTSTR NextQuote;
LPTSTR ElementBegin;
LPTSTR ElementEnd;
TCHAR cElementEndBackup;
LPTSTR OutElementEnd;
BOOL DelimiterInElement;
DWORD OutListType;
TCHAR OutListDelimiter;
#ifdef CANONDBG
DbgPrint("NetpwListCanonicalize\n");
#endif
if (Flags & INLC_FLAGS_MASK_RESERVED) {
return ERROR_INVALID_PARAMETER;
}
//
// Determine if our input list is in null-null format. We do
// this first because we need to use it to do proper GP-fault probing
// on <List>.
//
NullInputDelimiter = !ARGUMENT_PRESENT(Delimiters) || (*Delimiters == TCHAR_EOS);
//
// Validate address parameters (i.e. GP-fault tests) and accumulate string
// lengths (accumulate: now there's a word from a past I'd rather forget)
//
list_len = STRLEN(List) + 1;
if (NullInputDelimiter) {
//
// This is a null-null list; stop when we find a null string.
//
next_string = List + list_len;
do {
//
// Q: Is the compiler smart enough to do the right thing with
// these +1s?
//
len = STRLEN(next_string);
list_len += len + 1;
next_string += len + 1;
} while (len);
}
if (ARGUMENT_PRESENT(Delimiters)) {
STRLEN(Delimiters);
}
if ((Flags & INLC_FLAGS_MASK_NAMETYPE) == NAMETYPE_PATH && PathTypesLen > 0) {
PathTypes[0] = PathTypes[PathTypesLen - 1] = 0;
}
*OutCount = 0;
//
// Initialize variables
//
Input = List;
OutPtr = Outbuf;
OutbufEnd = Outbuf + OutbufLen;
OutElementCount = 0;
NullInputDelimiter = !ARGUMENT_PRESENT(Delimiters) || (*Delimiters == TCHAR_EOS);
OutListType = Flags & INLC_FLAGS_MASK_OUTLIST_TYPE;
//
// Skip leading delimiters
//
// NOTE: We don't have to both to do this for a null-null list,
// because if it has a leading delimiter then it's an
// empty list.
//
if (!NullInputDelimiter) {
DelimiterLen = STRSPN(Input, Delimiters);
if (DelimiterLen > 0 && !(Flags & INLC_FLAGS_MULTIPLE_DELIMITERS)) {
return ERROR_INVALID_PARAMETER;
}
Input += DelimiterLen;
}
//
// We validate the output list type here are store the delimiter
// character.
//
// NOTE: Later on, we rely on the fact that the delimiter character
// is not zero if the output list is either API or search-path.
//
if (OutListType == OUTLIST_TYPE_API) {
OutListDelimiter = LIST_DELIMITER_CHAR_API;
} else if (OutListType == OUTLIST_TYPE_SEARCHPATH) {
OutListDelimiter = LIST_DELIMITER_CHAR_SEARCHPATH;
} else if (OutListType == OUTLIST_TYPE_NULL_NULL) {
OutListDelimiter = TCHAR_EOS;
} else {
return ERROR_INVALID_PARAMETER;
}
//
// Loop until we've reached the end of the input list
// OR until we encounter an error
//
while (*Input != TCHAR_EOS) {
//
// Find the beginning and ending characters of the list element
//
//
// Handle quoted strings separately
//
if (!NullInputDelimiter && *Input == LIST_QUOTE_CHAR) {
//
// Find the next quote; return an error if there is none
// or if it's the next character.
//
NextQuote = STRCHR(Input + 1, LIST_QUOTE_CHAR);
if (NextQuote == NULL || NextQuote == Input + 1) {
return ERROR_INVALID_PARAMETER;
}
ElementBegin = Input + 1;
ElementEnd = NextQuote;
} else {
ElementBegin = Input;
ElementEnd = Input
+ (NullInputDelimiter
? STRLEN(Input)
: STRCSPN(Input, Delimiters)
);
}
//
// Set the end character to null so that we can treat the list
// element as a string, saving its real value for later.
//
// WARNING: Once we have done this, we should not return from
// this function until we've restored this character,
// since we don't want to trash the string the caller
// passed us. If we are above the label
// <INLC_RestoreEndChar> and we encounter an error
// we should set <rc> to the error code and jump
// to that label (which will restore the character and
// return if the error is non-zero).
//
cElementEndBackup = *ElementEnd;
*ElementEnd = TCHAR_EOS;
//
// Copy the list element to the output buffer, validating its
// name or canonicalizing it as specified by the user.
//
switch(Flags & INLC_FLAGS_MASK_NAMETYPE) {
case NAMETYPE_PATH:
//
// Make sure that that the <PathTypes> array is big enough
//
if (OutElementCount >= PathTypesLen) {
rc = NERR_TooManyEntries;
goto INLC_RestoreEndChar;
}
//
// Determine if we only want to validate or if we also
// want to canonicalize.
//
if (Flags & INLC_FLAGS_CANONICALIZE) {
//
// We need to set the type to 0 before calling
// NetpwPathCanonicalize.
//
PathTypes[OutElementCount] = 0;
//
// Call NetICanonicalize and abort if it fails
//
rc = NetpwPathCanonicalize(
ElementBegin,
OutPtr,
(DWORD)(OutbufEnd - OutPtr),
NULL,
&PathTypes[OutElementCount],
0L
);
} else {
//
// Just validate the name and determine its type
//
rc = NetpwPathType(
ElementBegin,
&PathTypes[OutElementCount],
0L
);
//
// If this succeeded, attempt to copy it into the buffer
//
if (rc == 0) {
if (OutbufEnd - OutPtr < ElementEnd - ElementBegin + 1) {
rc = NERR_BufTooSmall;
} else {
STRCPY(OutPtr, ElementBegin);
}
}
}
if (rc) {
goto INLC_RestoreEndChar;
}
//
// Determine the end of the element (for use below)
//
OutElementEnd = STRCHR(OutPtr, TCHAR_EOS);
//
// Do fix-ups for API list format (enclose element in
// quotes, if necessary, and replace terminating null
// with the list delimiter).
//
if (OutListDelimiter != TCHAR_EOS) {
rc = FixupAPIListElement(
OutPtr,
&OutElementEnd,
OutbufEnd,
OutListDelimiter
);
if (rc) {
goto INLC_RestoreEndChar;
}
}
break;
case NAMETYPE_COPYONLY:
//
// Determine if this element needs to be quoted
//
DelimiterInElement = (OutListDelimiter != TCHAR_EOS)
&& (STRCHR(ElementBegin, OutListDelimiter) != NULL);
//
// See if there's enough room in the output buffer for
// this element; abort if there isn't.
//
// (ElementEnd - ElementBegin) is the number of bytes
// in the element. We add 1 for the element separator and
// an additional 2 if we need to enclose the element in quotes.
//
if (OutbufEnd - OutPtr < ElementEnd - ElementBegin + 1 + DelimiterInElement * 2) {
rc = NERR_BufTooSmall;
goto INLC_RestoreEndChar;
}
//
// Start the copying; set pointer to output string
//
OutElementEnd = OutPtr;
//
// Put in leading quote, if appropriate
//
if (DelimiterInElement) {
*OutElementEnd++ = LIST_QUOTE_CHAR;
}
//
// Copy input to output and advance end pointer
//
STRCPY(OutElementEnd, ElementBegin);
OutElementEnd += ElementEnd - ElementBegin;
//
// Put in trailing quote, if appropriate
//
if (DelimiterInElement) {
*OutElementEnd++ = LIST_QUOTE_CHAR;
}
//
// Store delimiter
//
*OutElementEnd = OutListDelimiter;
break;
default:
//
// If this isn't one of the special types, we assume that it's
// a type with meaning to NetpwNameValidate and
// NetpwNameCanonicalize. We call the appropriate one of these
// functions and let it validate the name type and the name,
// passing back any error it returns.
//
//
// Determine if we only want to validate or if we also
// want to canonicalize.
//
if (Flags & INLC_FLAGS_CANONICALIZE) {
rc = NetpwNameCanonicalize(
ElementBegin,
OutPtr,
(DWORD)(OutbufEnd - OutPtr),
Flags & INLC_FLAGS_MASK_NAMETYPE,
0L
);
} else {
rc = NetpwNameValidate(
ElementBegin,
Flags & INLC_FLAGS_MASK_NAMETYPE,
0L
);
//
// If this succeeded, attempt to copy it into the buffer
//
if (rc == 0) {
if (OutbufEnd - OutPtr < ElementEnd - ElementBegin + 1) {
rc = NERR_BufTooSmall;
} else {
STRCPY(OutPtr, ElementBegin);
}
}
}
if (rc) {
goto INLC_RestoreEndChar;
}
//
// Determine the end of the element (for use below)
//
OutElementEnd = STRCHR(OutPtr, TCHAR_EOS);
//
// Do fix-ups for API list format (enclose element in
// quotes, if necessary, and replace terminating null
// with the list delimiter).
//
if (OutListDelimiter != TCHAR_EOS) {
rc = FixupAPIListElement(
OutPtr,
&OutElementEnd,
OutbufEnd,
OutListDelimiter
);
if (rc) {
goto INLC_RestoreEndChar;
}
}
break;
}
//
// End of switch statement
//
INLC_RestoreEndChar:
//
// Restore the character at <ElementEnd>; return if one of the
// above tasks failed.
//
*ElementEnd = cElementEndBackup;
if (rc) {
return rc;
}
//
// Skip past the last input character if it's a quote character
//
if (*ElementEnd == LIST_QUOTE_CHAR) {
ElementEnd++;
}
//
// Skip delimiter(s)
//
if (!NullInputDelimiter) {
//
// Determine the number of delimiters and set the input
// pointer to point past them.
//
DelimiterLen = STRSPN(ElementEnd, Delimiters);
Input = ElementEnd + DelimiterLen;
//
// Return an error if:
//
// - there are multiple delimiters and the multiple delimiters
// flag isn't set
// - we aren't at the end of the list and there are no
// delimiters
// - we are at the end of the list, there is a delimiter
// and the multiple delimiters flag isn't set
//
if (DelimiterLen > 1 && !(Flags & INLC_FLAGS_MULTIPLE_DELIMITERS)) {
return ERROR_INVALID_PARAMETER;
}
if (*Input != TCHAR_EOS && DelimiterLen == 0) {
return ERROR_INVALID_PARAMETER;
}
if (*Input == TCHAR_EOS && DelimiterLen > 0 && !(Flags & INLC_FLAGS_MULTIPLE_DELIMITERS)) {
return ERROR_INVALID_PARAMETER;
}
} else {
//
// Since this is a null-null list, we know we've already
// found at least one delimiter. We don't have to worry about
// multiple delimiters, because a second delimiter indicates
// the end of the list.
//
Input = ElementEnd + 1;
}
//
// Update output list pointer and output list count
//
OutPtr = OutElementEnd + 1;
OutElementCount++;
}
//
// End of while loop
//
//
// If the input list was empty, set the output buffer to be a null
// string. Otherwise, stick the list terminator at the end of the
// output buffer.
//
if (OutElementCount == 0) {
if (OutbufLen < 1) {
return NERR_BufTooSmall;
}
*Outbuf = TCHAR_EOS;
} else {
if (OutListType == OUTLIST_TYPE_NULL_NULL) {
//
// Make sure there's room for one more byte
//
if (OutPtr >= OutbufEnd) {
return NERR_BufTooSmall;
}
*OutPtr = TCHAR_EOS;
} else {
//
// NOTE: It's OK to move backwards in the string here because
// we know then OutPtr points one byte past the
// delimiter which follows the last element in the list.
// This does not violate DBCS as long as the delimiter
// is a single-byte character.
//
*(OutPtr - 1) = TCHAR_EOS;
}
}
//
// Set the count of elements in the list
//
*OutCount = OutElementCount;
//
// We're done; return with success
//
return 0;
}
LPTSTR
NetpwListTraverse(
IN LPTSTR Reserved OPTIONAL,
IN LPTSTR* pList,
IN DWORD Flags
)
/*++
Routine Description:
Traverse a list which has been converted to null-null form by
NetpwListCanonicalize. NetpwListTraverse returns a pointer to the first
element in the list, and modifies the list pointer parameter to point to the
next element of the list.
Arguments:
Reserved- A reserved far pointer. Must be NULL.
pList - A pointer to the pointer to the beginning of the list. On return
this will point to the next element in the list or NULL if we
have reached the end
Flags - Flags to determine operation. Currently MBZ.
Return Value:
A pointer to the first element in the list, or NULL if the list
is empty.
--*/
{
LPTSTR FirstElement;
UNREFERENCED_PARAMETER(Reserved);
UNREFERENCED_PARAMETER(Flags);
//
// Produce an assertion error if the reserved parameter is not NULL
// or the flags parameter is no zero.
//
//
// KEEP - This code is ifdef'd out because NETAPI.DLL won't build
// with the standard C version of assert(). This code
// should either be replaced or the #if 0 should be removed
// when we get a standard Net assert function.
//
#ifdef CANONDBG
NetpAssert((Reserved == NULL) && (Flags == 0));
#endif
//
// Return immediately if the pointer to the list pointer is NULL,
// if the list pointer itself is NULL, or if the list is a null
// string (which marks the end of the null-null list).
//
if (pList == NULL || *pList == NULL || **pList == TCHAR_EOS) {
return NULL;
}
//
// Save a pointer to the first element
//
FirstElement = *pList;
//
// Update the list pointer to point to the next element
//
// *pList += STRLEN(FirstElement) + 1;
*pList = STRCHR(FirstElement, TCHAR_EOS) + 1;
//
// Return the pointer to the first element
//
return FirstElement;
}
STATIC
DWORD
FixupAPIListElement(
IN LPTSTR Element,
IN LPTSTR* pElementTerm,
IN LPTSTR BufferEnd,
IN TCHAR DelimiterChar
)
/*++
Routine Description:
FixupAPIListElement Fixes-up a list element which has been copied into the
output buffer so that it conforms to API list format.
FixupAPIListElement takes an unquoted, null-terminated list element
(normally, which has been copied into the output buffer by strcpy() or by
Netpw{Name,Path}Canonicalize) and translates it into the format expected by
API and search-path lists. Specifically, it surrounds the element with quote
characters if it contains a list delimiter character, and it replaces the
null terminator with the API list delimiter.
Arguments:
Element - A pointer to the beginning of the null-terminated element.
pElementTerm- A pointer to the pointer to the element's (null) terminator.
BufferEnd - A pointer to the end of the output buffer (actually, one byte
past the end of the buffer).
DelimiterChar- The list delimiter character.
Return Value:
0 if successful.
NERR_BufTooSmall if the buffer doesn't have room for the additional
quote characters.
--*/
{
//
// See if the element contains a delimiter; if it does, it needs to
// be quoted.
//
if (STRCHR(Element, DelimiterChar) != NULL) {
//
// Make sure that the output buffer has room for two more
// characters (the quotes).
//
if (BufferEnd - *pElementTerm <= 2 * sizeof(*BufferEnd)) {
return NERR_BufTooSmall;
}
//
// Shift the string one byte to the right, stick quotes on either
// side, and update the end pointer. The element itself with be
// stored in the range [Element + 1, *pElementTerm].
//
MEMMOVE(Element + sizeof(*Element), Element, (int)(*pElementTerm - Element));
*Element = LIST_QUOTE_CHAR;
*(*pElementTerm + 1) = LIST_QUOTE_CHAR;
*pElementTerm += 2;
}
//
// Put a delimiter at the end of the element
//
**pElementTerm = DelimiterChar;
//
// Return with success
//
return 0;
}