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.

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