Leaked source code of windows server 2003
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.

676 lines
20 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1997.
  5. //
  6. // File: N C M S Z . C P P
  7. //
  8. // Contents: Common multi-sz routines.
  9. //
  10. // Notes: Split out from ncstring.cpp
  11. //
  12. // Author: shaunco 7 Jun 1998
  13. //
  14. //----------------------------------------------------------------------------
  15. #include <pch.h>
  16. #pragma hdrstop
  17. #include "ncdebug.h"
  18. #include "ncstring.h"
  19. //+---------------------------------------------------------------------------
  20. //
  21. // Function: CchOfMultiSzSafe
  22. //
  23. // Purpose: Count the number of characters of a double NULL terminated
  24. // multi-sz, including all NULLs except for the final terminating
  25. // NULL.
  26. //
  27. // Arguments:
  28. // pmsz [in] The multi-sz to count characters for.
  29. //
  30. // Returns: The count of characters.
  31. //
  32. // Author: tongl 17 June 1997
  33. //
  34. // Notes:
  35. //
  36. ULONG
  37. CchOfMultiSzSafe (
  38. IN PCWSTR pmsz)
  39. {
  40. // NULL strings have zero length by definition.
  41. if (!pmsz)
  42. return 0;
  43. ULONG cchTotal = 0;
  44. ULONG cch;
  45. while (*pmsz)
  46. {
  47. cch = wcslen (pmsz) + 1;
  48. cchTotal += cch;
  49. pmsz += cch;
  50. }
  51. // Return the count of characters.
  52. return cchTotal;
  53. }
  54. //+---------------------------------------------------------------------------
  55. //
  56. // Function: CchOfMultiSzAndTermSafe
  57. //
  58. // Purpose: Count the number of characters of a double NULL terminated
  59. // multi-sz, including all NULLs.
  60. //
  61. // Arguments:
  62. // pmsz [in] The multi-sz to count characters for.
  63. //
  64. // Returns: The count of characters.
  65. //
  66. // Author: tongl 17 June 1997
  67. //
  68. // Notes:
  69. //
  70. ULONG
  71. CchOfMultiSzAndTermSafe (
  72. IN PCWSTR pmsz)
  73. {
  74. // NULL strings have zero length by definition.
  75. if (!pmsz)
  76. return 0;
  77. // Return the count of characters plus room for the
  78. // extra null terminator.
  79. return CchOfMultiSzSafe (pmsz) + 1;
  80. }
  81. //+---------------------------------------------------------------------------
  82. //
  83. // Function: FIsSzInMultiSzSafe
  84. //
  85. // Purpose: Determine if a given string is present in a Multi-Sz string
  86. // by doing a case insensitive compare.
  87. //
  88. // Arguments:
  89. // psz [in] String to search for in pmsz
  90. // pmsz [in] The multi-sz to search
  91. //
  92. // Returns: TRUE if the specified string 'psz' was found in 'pmsz'.
  93. //
  94. // Author: scottbri 25 Feb 1997
  95. //
  96. // Notes: Note that the code can handle Null input values.
  97. //
  98. BOOL
  99. FIsSzInMultiSzSafe (
  100. IN PCWSTR psz,
  101. IN PCWSTR pmsz)
  102. {
  103. if (!pmsz || !psz)
  104. {
  105. return FALSE;
  106. }
  107. while (*pmsz)
  108. {
  109. if (0 == _wcsicmp (pmsz, psz))
  110. {
  111. return TRUE;
  112. }
  113. pmsz += wcslen (pmsz) + 1;
  114. }
  115. return FALSE;
  116. }
  117. //+---------------------------------------------------------------------------
  118. //
  119. // Function: FGetSzPositionInMultiSzSafe
  120. //
  121. // Purpose: Determine if a given string is present in a Multi-Sz string
  122. // by doing a case insensitive compare.
  123. //
  124. // Arguments:
  125. // psz [in] String to search for in pmsz
  126. // pmsz [in] The multi-sz to search
  127. // pdwIndex [out] The index of the first matching psz in pmsz
  128. // pfDuplicatePresent [out] Optional. TRUE if the string is present in
  129. // the multi-sz more than once. FALSE otherwise.
  130. // pcStrings [out] Optional. The number of strings in pmsz
  131. //
  132. // Returns: TRUE if the specified string 'psz' was found in 'pmsz'.
  133. //
  134. // Author: BillBe 9 Oct 1998
  135. //
  136. // Notes: Note that the code can handle Null input values.
  137. //
  138. BOOL
  139. FGetSzPositionInMultiSzSafe (
  140. IN PCWSTR psz,
  141. IN PCWSTR pmsz,
  142. OUT DWORD* pdwIndex,
  143. OUT BOOL *pfDuplicatePresent,
  144. OUT DWORD* pcStrings)
  145. {
  146. // initialize out params.
  147. //
  148. *pdwIndex = 0;
  149. if (pfDuplicatePresent)
  150. {
  151. *pfDuplicatePresent = FALSE;
  152. }
  153. if (pcStrings)
  154. {
  155. *pcStrings = 0;
  156. }
  157. if (!pmsz || !psz)
  158. {
  159. return FALSE;
  160. }
  161. // Need to keep track if duplicates are found
  162. BOOL fFoundAlready = FALSE;
  163. DWORD dwIndex = 0;
  164. while (*pmsz)
  165. {
  166. if (0 == _wcsicmp (pmsz, psz))
  167. {
  168. if (!fFoundAlready)
  169. {
  170. *pdwIndex = dwIndex;
  171. fFoundAlready = TRUE;
  172. }
  173. else if (pfDuplicatePresent)
  174. {
  175. *pfDuplicatePresent = TRUE;
  176. }
  177. }
  178. pmsz += wcslen (pmsz) + 1;
  179. ++dwIndex;
  180. }
  181. if (pcStrings)
  182. {
  183. *pcStrings = dwIndex;
  184. }
  185. return fFoundAlready;
  186. }
  187. //+---------------------------------------------------------------------------
  188. //
  189. // Function: HrAddSzToMultiSz
  190. //
  191. // Purpose: Add a string into a REG_MULTI_SZ registry value
  192. //
  193. // Arguments:
  194. // pszAddString [in] The string to add to the multi-sz
  195. // pmszIn [in] (OPTIONAL) The original Multi-Sz to add to.
  196. // dwFlags [in] Can contain one or more of the following
  197. // values:
  198. //
  199. // STRING_FLAG_ALLOW_DUPLICATES
  200. // Don't remove duplicate values when adding
  201. // the string to the list. Default is to
  202. // remove all other instance of this string.
  203. // STRING_FLAG_ENSURE_AT_FRONT
  204. // Ensure the string is the first element of
  205. // the list. If the string is present and
  206. // duplicates aren't allowed, move the
  207. // string to the end.
  208. // STRING_FLAG_ENSURE_AT_END
  209. // Ensure the string is the last
  210. // element of the list. This can not be used
  211. // with STRING_FLAG_ENSURE_AT_FRONT. If the
  212. // string is present and duplicates aren't
  213. // allowed, move the string to the end.
  214. // STRING_FLAG_ENSURE_AT_INDEX
  215. // Ensure that the string is at dwStringIndex
  216. // in the multi-sz. If the index specified
  217. // is greater than the number of strings
  218. // in the multi-sz, the string will be
  219. // placed at the end.
  220. // STRING_FLAG_DONT_MODIFY_IF_PRESENT
  221. // If the string already exists in the
  222. // multi-sz then no modication will take
  223. // place. Note: This takes precedent
  224. // over the presence/non-presence of the
  225. // STRING_FLAG_ALLOW_DUPLICATES flag.
  226. // i.e nothing will be added or removed
  227. // if this flag is set and the string was
  228. // present in the multi-sz
  229. // dwStringIndex [in] If STRING_FLAG_ENSURE_AT_INDEX is specified,
  230. // this is the index for the string position.
  231. // Otherwise, this value is ignored.
  232. //
  233. // pmszOut [out] The new multi-sz.
  234. // pfChanged [out] TRUE if the multi-sz changed in any way,
  235. // FALSE otherwise.
  236. //
  237. // Returns: S_OK or an HRESULT_FROM_WIN32 error code.
  238. //
  239. // Author: jeffspr 27 Mar 1997
  240. //
  241. // Modified: BillBe 6 Oct 1998
  242. // (Extracted from HrRegAddStringTo MultiSz and modified)
  243. //
  244. HRESULT
  245. HrAddSzToMultiSz(
  246. IN PCWSTR pszAddString,
  247. IN PCWSTR pmszIn,
  248. IN DWORD dwFlags,
  249. IN DWORD dwStringIndex,
  250. OUT PWSTR* ppmszOut,
  251. OUT BOOL* pfChanged)
  252. {
  253. Assert(pszAddString && *pszAddString);
  254. Assert(ppmszOut);
  255. Assert(pfChanged);
  256. HRESULT hr = S_OK;
  257. BOOL fEnsureAtFront = dwFlags & STRING_FLAG_ENSURE_AT_FRONT;
  258. BOOL fEnsureAtEnd = dwFlags & STRING_FLAG_ENSURE_AT_END;
  259. BOOL fEnsureAtIndex = dwFlags & STRING_FLAG_ENSURE_AT_INDEX;
  260. // Can't specify more than one of these flags
  261. if ((fEnsureAtFront && fEnsureAtEnd) ||
  262. (fEnsureAtFront && fEnsureAtIndex) ||
  263. (fEnsureAtEnd && fEnsureAtIndex))
  264. {
  265. AssertSz(FALSE, "Invalid flags in HrAddSzToMultiSz");
  266. return E_INVALIDARG;
  267. }
  268. // Must specify at least one
  269. if (!fEnsureAtFront && !fEnsureAtEnd && !fEnsureAtIndex)
  270. {
  271. AssertSz(FALSE, "No operation flag specified in HrAddSzToMultiSz");
  272. return E_INVALIDARG;
  273. }
  274. // Initialize the output parameters.
  275. //
  276. *ppmszOut = NULL;
  277. *pfChanged = TRUE;
  278. DWORD dwIndex;
  279. BOOL fDupePresent;
  280. DWORD cItems;
  281. // If the string to add is not empty...
  282. //
  283. if (*pszAddString)
  284. {
  285. // Check if the string is already present in the MultiSz
  286. BOOL fPresent = FGetSzPositionInMultiSzSafe (pszAddString, pmszIn,
  287. &dwIndex, &fDupePresent, &cItems);
  288. if (fPresent)
  289. {
  290. // If the flag don't modify is present then we aren't changing
  291. // anything
  292. //
  293. if (dwFlags & STRING_FLAG_DONT_MODIFY_IF_PRESENT)
  294. {
  295. *pfChanged = FALSE;
  296. }
  297. // if there are no duplicates present and we are not allowing
  298. // duplicates, then we need to determine if the string is already in
  299. // the correct position
  300. //
  301. if (!fDupePresent && !(dwFlags & STRING_FLAG_ALLOW_DUPLICATES))
  302. {
  303. // If we are to insert the string at front but it is already
  304. // there, then we aren't changing anything
  305. //
  306. if (fEnsureAtFront && (0 == dwIndex))
  307. {
  308. *pfChanged = FALSE;
  309. }
  310. // If we are to insert the string at the end but it is already
  311. // there, then we aren't changing anything
  312. //
  313. if (fEnsureAtEnd && (dwIndex == (cItems - 1)))
  314. {
  315. *pfChanged = FALSE;
  316. }
  317. if (fEnsureAtIndex && (dwIndex == dwStringIndex))
  318. {
  319. *pfChanged = FALSE;
  320. }
  321. }
  322. }
  323. }
  324. else
  325. {
  326. // If string to add was empty so we aren't changing anything
  327. *pfChanged = FALSE;
  328. }
  329. // If we are still going to change things...
  330. //
  331. if (*pfChanged)
  332. {
  333. DWORD cchDataSize = CchOfMultiSzSafe (pmszIn);
  334. // Enough space for the old data plus the new string and NULL, and for the
  335. // second trailing NULL (multi-szs are double-terminated)
  336. DWORD cchAllocSize = cchDataSize + wcslen (pszAddString) + 1 + 1;
  337. PWSTR pmszOrderNew = (PWSTR) MemAlloc(cchAllocSize * sizeof(WCHAR));
  338. if (pmszOrderNew)
  339. {
  340. // If we've gotten the "insert at front" flag, do the insert. Otherwise,
  341. // the default is "insert at end"
  342. //
  343. DWORD cchOffsetNew = 0;
  344. DWORD dwCurrentIndex = 0;
  345. if (fEnsureAtFront || (fEnsureAtIndex && (0 == dwStringIndex)))
  346. {
  347. // Insert our passed-in value at the beginning of the new buffer.
  348. //
  349. wcscpy (pmszOrderNew + cchOffsetNew, pszAddString);
  350. cchOffsetNew += wcslen ((PWSTR)pmszOrderNew) + 1;
  351. ++dwCurrentIndex;
  352. }
  353. // Loop through the old buffer, and copy all of the strings that are not
  354. // identical to our insertion string.
  355. //
  356. DWORD cchOffsetOld = 0;
  357. PWSTR pszCurrent;
  358. while ((cchOffsetOld + 1) < cchDataSize)
  359. {
  360. if (fEnsureAtIndex && (dwCurrentIndex == dwStringIndex))
  361. {
  362. // Insert our passed-in value at the current index of the
  363. // new buffer.
  364. //
  365. wcscpy (pmszOrderNew + cchOffsetNew, pszAddString);
  366. cchOffsetNew += wcslen (pmszOrderNew + cchOffsetNew) + 1;
  367. ++dwCurrentIndex;
  368. }
  369. else
  370. {
  371. BOOL fCopyThisElement = FALSE;
  372. // Get the next string in the list.
  373. //
  374. pszCurrent = (PWSTR) (pmszIn + cchOffsetOld);
  375. // If we allow duplicates, then copy this element, else
  376. // check for a match, and if there's no match, then
  377. // copy this element.
  378. if (dwFlags & STRING_FLAG_ALLOW_DUPLICATES)
  379. {
  380. fCopyThisElement = TRUE;
  381. }
  382. else
  383. {
  384. if (_wcsicmp (pszCurrent, pszAddString) != 0)
  385. {
  386. fCopyThisElement = TRUE;
  387. }
  388. }
  389. // If we're allowing the copy, then copy!
  390. if (fCopyThisElement)
  391. {
  392. wcscpy (pmszOrderNew + cchOffsetNew, pszCurrent);
  393. cchOffsetNew +=
  394. wcslen (pmszOrderNew + cchOffsetNew) + 1;
  395. ++dwCurrentIndex;
  396. }
  397. // Update the offset
  398. //
  399. cchOffsetOld += wcslen (pmszIn + cchOffsetOld) + 1;
  400. }
  401. }
  402. // If we have the ENSURE_AT_END flag set or if the ENSURE_AT_INDEX
  403. // flag was set and the index was greater than the possible
  404. // index, this means we want to insert at the end
  405. //
  406. if (fEnsureAtEnd ||
  407. (fEnsureAtIndex && (dwCurrentIndex <= dwStringIndex)))
  408. {
  409. wcscpy (pmszOrderNew + cchOffsetNew, pszAddString);
  410. cchOffsetNew += wcslen (pmszOrderNew + cchOffsetNew) + 1;
  411. }
  412. // Put the last of the double-NULL chars on the end.
  413. //
  414. pszCurrent = pmszOrderNew + cchOffsetNew;
  415. pszCurrent[0] = (WCHAR) 0;
  416. *ppmszOut = pmszOrderNew;
  417. }
  418. else
  419. {
  420. hr = E_OUTOFMEMORY;
  421. }
  422. }
  423. TraceError("HrAddSzToMultiSz", hr);
  424. return hr;
  425. }
  426. //+---------------------------------------------------------------------------
  427. //
  428. // Function: HrCreateArrayOfStringPointersIntoMultiSz
  429. //
  430. // Purpose: Allocates and initializes an array of string pointers.
  431. // The array of pointers is initialized to point to the
  432. // individual strings in a multi-sz.
  433. //
  434. // Arguments:
  435. // pmszSrc [in] The multi-sz to index.
  436. // pcStrings [out] Returned count of string pointers in the array.
  437. // papsz [out] Returned array of string pointers.
  438. //
  439. // Returns: S_OK or E_OUTOFMEMORY
  440. //
  441. // Author: shaunco 20 Jun 1998
  442. //
  443. // Notes: It is the callers responsibility to ensure there is at
  444. // least one string. The restriction is explicitly chosen to
  445. // reduce confusion about what would be returned if the
  446. // multi-sz were empty.
  447. //
  448. // Free the returned array with free.
  449. //
  450. HRESULT
  451. HrCreateArrayOfStringPointersIntoMultiSz (
  452. IN PCWSTR pmszSrc,
  453. OUT UINT* pcStrings,
  454. OUT PCWSTR** papsz)
  455. {
  456. Assert (pmszSrc && *pmszSrc);
  457. Assert (papsz);
  458. // First, count the number of strings in the multi-sz.
  459. //
  460. UINT cStrings = 0;
  461. PCWSTR pmsz;
  462. for (pmsz = pmszSrc; *pmsz; pmsz += wcslen(pmsz) + 1)
  463. {
  464. cStrings++;
  465. }
  466. Assert (cStrings); // See Notes above.
  467. *pcStrings = cStrings;
  468. // Allocate enough memory for the array.
  469. //
  470. HRESULT hr = HrMalloc (cStrings * sizeof(PWSTR*),
  471. reinterpret_cast<VOID**>(papsz));
  472. if (S_OK == hr)
  473. {
  474. // Initialize the returned array. ppsz is a pointer to each
  475. // element of the array. It is incremented after each element
  476. // is initialized.
  477. //
  478. PCWSTR* ppsz = *papsz;
  479. for (pmsz = pmszSrc; *pmsz; pmsz += wcslen(pmsz) + 1)
  480. {
  481. *ppsz = pmsz;
  482. ppsz++;
  483. }
  484. }
  485. TraceError ("HrCreateArrayOfStringPointersIntoMultiSz", hr);
  486. return hr;
  487. }
  488. //+---------------------------------------------------------------------------
  489. //
  490. // Function: RemoveSzFromMultiSz
  491. //
  492. // Purpose: Remove all occurrences of a string from a multi-sz. The
  493. // removals are performed in place.
  494. //
  495. // Arguments:
  496. // psz [in] The string to remove.
  497. // pmsz [in out] The multi-sz to remove psz from.
  498. // dwFlags [in] Can contain one or more of the following
  499. // values:
  500. //
  501. // STRING_FLAG_REMOVE_SINGLE
  502. // Don't remove more than one value, if
  503. // multiple are present.
  504. // [default] STRING_FLAG_REMOVE_ALL
  505. // If multiple matching values are present,
  506. // remove them all.
  507. // pfRemoved [out] Set to TRUE on return if one or more strings
  508. // were removed.
  509. //
  510. // Returns: nothing
  511. //
  512. // Author: shaunco 8 Jun 1998
  513. //
  514. // Notes:
  515. //
  516. VOID
  517. RemoveSzFromMultiSz (
  518. IN PCWSTR psz,
  519. IN OUT PWSTR pmsz,
  520. IN DWORD dwFlags,
  521. OUT BOOL* pfRemoved)
  522. {
  523. Assert (pfRemoved);
  524. // Initialize the output parameters.
  525. //
  526. *pfRemoved = FALSE;
  527. if (!pmsz || !psz || !*psz)
  528. {
  529. return;
  530. }
  531. // Look for all occurrences of psz in pmsz. When one is found, move
  532. // the remaining part of the multi-sz over it.
  533. //
  534. while (*pmsz)
  535. {
  536. if (0 == _wcsicmp (pmsz, psz))
  537. {
  538. PWSTR pmszRemain = pmsz + (wcslen (pmsz) + 1);
  539. INT cchRemain = CchOfMultiSzAndTermSafe (pmszRemain);
  540. MoveMemory (pmsz, pmszRemain, cchRemain * sizeof(WCHAR));
  541. *pfRemoved = TRUE;
  542. if (dwFlags & STRING_FLAG_REMOVE_SINGLE)
  543. {
  544. break;
  545. }
  546. }
  547. else
  548. {
  549. pmsz += wcslen (pmsz) + 1;
  550. }
  551. }
  552. }
  553. //+---------------------------------------------------------------------------
  554. //
  555. // Function: SzListToMultiSz
  556. //
  557. // Purpose: Converts a comma-separated list into a multi-sz style list.
  558. //
  559. // Arguments:
  560. // psz [in] String to be converted. It is not modified.
  561. // pcb [out] Number of *bytes* in the resulting string. If NULL,
  562. // size is not returned.
  563. // ppszOut [out] Resulting string.
  564. //
  565. // Returns: Nothing.
  566. //
  567. // Author: danielwe 3 Apr 1997
  568. //
  569. // Notes: Resulting string must be freed with MemFree.
  570. //
  571. VOID
  572. SzListToMultiSz (
  573. IN PCWSTR psz,
  574. OUT DWORD* pcb,
  575. OUT PWSTR* ppszOut)
  576. {
  577. Assert(psz);
  578. Assert(ppszOut);
  579. PCWSTR pch;
  580. INT cch;
  581. PWSTR pszOut;
  582. const WCHAR c_chSep = L',';
  583. // Add 2 to the length. One for final NULL, and one for second NULL.
  584. cch = wcslen (psz) + 2;
  585. pszOut = (PWSTR)MemAlloc(CchToCb(cch));
  586. if (pszOut)
  587. {
  588. ZeroMemory(pszOut, CchToCb(cch));
  589. if (pcb)
  590. {
  591. *pcb = CchToCb(cch);
  592. }
  593. *ppszOut = pszOut;
  594. // count the number of separator chars and put NULLs there
  595. //
  596. for (pch = psz; *pch; pch++)
  597. {
  598. if (*pch == c_chSep)
  599. {
  600. *pszOut++ = 0;
  601. }
  602. else
  603. {
  604. *pszOut++ = *pch;
  605. }
  606. }
  607. }
  608. }