/*++ 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 . 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 . 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 . 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 , 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 . // 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 // and we encounter an error // we should set 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 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 ; 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; }