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.

1347 lines
29 KiB

  1. /*++
  2. Copyright (c) 1995-2001 Microsoft Corporation
  3. Module Name:
  4. string.c
  5. Abstract:
  6. Domain Name System (DNS) Library
  7. DNS string routines.
  8. Author:
  9. Jim Gilroy (jamesg) October 1995
  10. Revision History:
  11. jamesg Jan 1997 UTF-8, Unicode conversions
  12. --*/
  13. #include "local.h"
  14. PSTR
  15. Dns_CreateStringCopy(
  16. IN PCHAR pchString,
  17. IN DWORD cchString
  18. )
  19. /*++
  20. Routine Description:
  21. Create copy of string.
  22. Arguments:
  23. pchString -- ptr to string to copy
  24. cchString -- length of string, if unknown; if NOT given, then pchString
  25. MUST be NULL terminated
  26. Return Value:
  27. Ptr to string copy, if successful
  28. NULL on failure.
  29. --*/
  30. {
  31. LPSTR pstringNew;
  32. DNSDBG( TRACE, ( "Dns_CreateStringCopy()\n" ));
  33. if ( !pchString )
  34. {
  35. SetLastError( ERROR_INVALID_PARAMETER );
  36. return( NULL );
  37. }
  38. // determine string length, if not given
  39. if ( !cchString )
  40. {
  41. cchString = strlen( pchString );
  42. }
  43. // allocate memory
  44. pstringNew = (LPSTR) ALLOCATE_HEAP( cchString+1 );
  45. if ( !pstringNew )
  46. {
  47. SetLastError( DNS_ERROR_NO_MEMORY );
  48. return( NULL );
  49. }
  50. // copy and NULL terminate
  51. RtlCopyMemory(
  52. pstringNew,
  53. pchString,
  54. cchString );
  55. pstringNew[cchString] = 0;
  56. return( pstringNew );
  57. }
  58. DWORD
  59. Dns_GetBufferLengthForStringCopy(
  60. IN PCHAR pchString,
  61. IN DWORD cchString,
  62. IN DNS_CHARSET CharSetIn,
  63. IN DNS_CHARSET CharSetOut
  64. )
  65. /*++
  66. Routine Description:
  67. Determing length required for copy of string.
  68. Arguments:
  69. pchString -- ptr to string to get buffer length for
  70. cchString -- length of string, if known;
  71. - if CharSetIn is unicode, then this is length in wide characters
  72. - if NOT given, then pchString MUST be NULL terminated
  73. CharSetIn -- incoming character set
  74. CharSetOut -- result character set
  75. Return Value:
  76. Buffer length (bytes) required for string, includes space for terminating NULL.
  77. Zero on invalid\unconvertible string. GetLastError() set to ERROR_INVALID_DATA.
  78. --*/
  79. {
  80. INT length;
  81. DNSDBG( TRACE, ( "Dns_GetBufferLengthForStringCopy()\n" ));
  82. if ( !pchString )
  83. {
  84. SetLastError( ERROR_INVALID_PARAMETER );
  85. return( 0 );
  86. }
  87. //
  88. // incoming Unicode
  89. //
  90. if ( CharSetIn == DnsCharSetUnicode )
  91. {
  92. if ( !cchString )
  93. {
  94. cchString = (WORD) wcslen( (PWCHAR)pchString );
  95. }
  96. // unicode to unicode
  97. if ( CharSetOut == DnsCharSetUnicode )
  98. {
  99. return( (cchString+1) * 2 );
  100. }
  101. // unicode to UTF8
  102. //
  103. // use private unicode\utf8 conversion functions
  104. // - superior to public ones (faster, more robust)
  105. // - Win95 does not support CP_UTF8
  106. //
  107. // for unicode-UTF8 there's no invalid string possible
  108. else if ( CharSetOut == DnsCharSetUtf8 )
  109. {
  110. #if 0
  111. length = WideCharToMultiByte(
  112. CP_UTF8,
  113. 0, // no flags
  114. (PWCHAR) pchString,
  115. (INT) cchString,
  116. NULL,
  117. 0, // call determines required buffer length
  118. NULL,
  119. NULL );
  120. #endif
  121. length = Dns_UnicodeToUtf8(
  122. (PWCHAR) pchString,
  123. (INT) cchString,
  124. NULL,
  125. 0
  126. );
  127. ASSERT( length != 0 || cchString == 0 );
  128. return( length + 1 );
  129. }
  130. // unicode to ANSI
  131. // - some chars will NOT convert
  132. else if ( CharSetOut == DnsCharSetAnsi )
  133. {
  134. length = WideCharToMultiByte(
  135. CP_ACP,
  136. 0, // no flags
  137. (PWCHAR) pchString,
  138. (INT) cchString,
  139. NULL,
  140. 0, // call determines required buffer length
  141. NULL,
  142. NULL
  143. );
  144. if ( length == 0 && cchString != 0 )
  145. {
  146. goto Failed;
  147. }
  148. return( length + 1 );
  149. }
  150. // bad CharSetOut drops to Failed
  151. }
  152. //
  153. // incoming UTF8
  154. //
  155. else if ( CharSetIn == DnsCharSetUtf8 )
  156. {
  157. if ( !cchString )
  158. {
  159. cchString = strlen( pchString );
  160. }
  161. // UTF8 to UTF8
  162. if ( CharSetOut == DnsCharSetUtf8 )
  163. {
  164. return( cchString + 1 );
  165. }
  166. // UTF8 to unicode
  167. //
  168. // use private unicode\utf8 conversion functions
  169. // - superior to public ones (faster, more robust)
  170. // - Win95 does not support CP_UTF8
  171. //
  172. // for UTF8 string can be invalid, catch and return error
  173. else if ( CharSetOut == DnsCharSetUnicode )
  174. {
  175. #if 0
  176. length = MultiByteToWideChar(
  177. CP_UTF8,
  178. 0, // no flags
  179. pchString,
  180. (INT) cchString,
  181. NULL,
  182. 0 // call determines required buffer length
  183. );
  184. #endif
  185. length = Dns_Utf8ToUnicode(
  186. pchString,
  187. (INT) cchString,
  188. NULL,
  189. 0
  190. );
  191. if ( length == 0 && cchString != 0 )
  192. {
  193. ASSERT( GetLastError() == ERROR_INVALID_DATA );
  194. return( 0 );
  195. }
  196. return( (length+1)*2 );
  197. }
  198. // UTF8 to ANSI
  199. // - note, result length here is actually buffer length
  200. else if ( CharSetOut == DnsCharSetAnsi )
  201. {
  202. return Dns_Utf8ToAnsi(
  203. pchString,
  204. cchString,
  205. NULL,
  206. 0 );
  207. }
  208. // bad CharSetOut drops to Failed
  209. }
  210. //
  211. // incoming ANSI
  212. //
  213. else if ( CharSetIn == DnsCharSetAnsi )
  214. {
  215. if ( !cchString )
  216. {
  217. cchString = strlen( pchString );
  218. }
  219. // ANSI to ANSI
  220. if ( CharSetOut == DnsCharSetAnsi )
  221. {
  222. return( cchString + 1 );
  223. }
  224. // ANSI to unicode
  225. // - should always succeed
  226. else if ( CharSetOut == DnsCharSetUnicode )
  227. {
  228. length = MultiByteToWideChar(
  229. CP_ACP,
  230. 0, // no flags
  231. pchString,
  232. (INT) cchString,
  233. NULL,
  234. 0 // call determines required buffer length
  235. );
  236. if ( length == 0 && cchString )
  237. {
  238. ASSERT( FALSE );
  239. ASSERT( GetLastError() == ERROR_INVALID_DATA );
  240. goto Failed;
  241. }
  242. return( (length+1) * 2 );
  243. }
  244. // ANSI to UTF8
  245. // - note, result length here is actually buffer length
  246. else if ( CharSetOut == DnsCharSetUtf8 )
  247. {
  248. return Dns_AnsiToUtf8(
  249. pchString,
  250. cchString,
  251. NULL,
  252. 0 );
  253. }
  254. // bad CharSetOut drops to Failed
  255. }
  256. // all unhandled cases are failures
  257. Failed:
  258. DNSDBG( ANY, (
  259. "ERROR: Dns_GetBufferLengthForStringCopy() failed!\n"
  260. "\tpchString = %p (%*s)\n"
  261. "\tcchString = %d\n"
  262. "\tCharSetIn = %d\n"
  263. "\tCharSetOut = %d\n",
  264. pchString,
  265. cchString, pchString,
  266. cchString,
  267. CharSetIn,
  268. CharSetOut ));
  269. SetLastError( ERROR_INVALID_DATA );
  270. return( 0 );
  271. }
  272. DWORD
  273. Dns_StringCopy(
  274. OUT PBYTE pBuffer,
  275. IN OUT PDWORD pdwBufLength,
  276. IN PCHAR pchString,
  277. IN DWORD cchString,
  278. IN DNS_CHARSET CharSetIn,
  279. IN DNS_CHARSET CharSetOut
  280. )
  281. /*++
  282. Routine Description:
  283. Create copy of DNS string.
  284. Arguments:
  285. pBuffer -- buffer to copy to
  286. pdwBufLength -- ptr to length of buffer in bytes;
  287. if NULL, buffer MUST have adequate length
  288. if exists, then copy only completed if *pdwBufLength is adequate
  289. to hold converted result
  290. pchString -- ptr to string to copy
  291. cchString -- length of string, if known;
  292. - if CharSetIn is unicode, then this is length in wide characters
  293. - if NOT given, then pchString MUST be NULL terminated
  294. CharSetIn -- incoming character set
  295. CharSetOut -- result character set
  296. Return Value:
  297. Count of bytes written to buffer (includes terminating NULL).
  298. Zero on error. GetLastError() for status.
  299. --*/
  300. {
  301. INT length;
  302. DWORD bufLength;
  303. DNSDBG( TRACE, ( "Dns_StringCopy()\n" ));
  304. DNSDBG( STRING, (
  305. "Dns_StringCopy()\n"
  306. "\tpBuffer = %p\n"
  307. "\tpdwBufLen = %p\n"
  308. "\tbuf length = %d\n"
  309. "\tpchString = %p\n"
  310. "\tcchString = %d\n"
  311. "\tCharSetIn = %d\n"
  312. "\tCharSetOut = %d\n",
  313. pBuffer,
  314. pdwBufLength,
  315. pdwBufLength ? *pdwBufLength : 0,
  316. pchString,
  317. cchString,
  318. CharSetIn,
  319. CharSetOut ));
  320. if ( !pchString )
  321. {
  322. DNS_ASSERT( FALSE );
  323. SetLastError( ERROR_INVALID_PARAMETER );
  324. return( 0 );
  325. }
  326. //
  327. // find string length
  328. // do this here so don't do it twice if must calculate required buffer length
  329. //
  330. if ( cchString == 0 )
  331. {
  332. if ( CharSetIn == DnsCharSetUnicode )
  333. {
  334. cchString = (WORD) wcslen( (PWCHAR)pchString );
  335. }
  336. else
  337. {
  338. cchString = strlen( pchString );
  339. }
  340. }
  341. //
  342. // verify adequate buffer length
  343. //
  344. // DCR_PERF: ideally make direct copy to buffer and fail if
  345. // over length, rather than effectively having to convert
  346. // twice
  347. //
  348. if ( pdwBufLength )
  349. {
  350. bufLength = Dns_GetBufferLengthForStringCopy(
  351. pchString,
  352. cchString,
  353. CharSetIn,
  354. CharSetOut );
  355. if ( bufLength == 0 )
  356. {
  357. SetLastError( ERROR_INVALID_DATA );
  358. *pdwBufLength = 0;
  359. return( 0 );
  360. }
  361. if ( bufLength > *pdwBufLength )
  362. {
  363. SetLastError( ERROR_MORE_DATA );
  364. *pdwBufLength = bufLength;
  365. return( 0 );
  366. }
  367. *pdwBufLength = bufLength;
  368. }
  369. //
  370. // incoming unicode string
  371. //
  372. if ( CharSetIn == DnsCharSetUnicode )
  373. {
  374. // unicode to unicode straight copy
  375. // - correct for length in wide characters
  376. if ( CharSetOut == DnsCharSetUnicode )
  377. {
  378. ((PWORD)pBuffer)[ cchString ] = 0;
  379. cchString *= 2;
  380. RtlCopyMemory(
  381. pBuffer,
  382. pchString,
  383. cchString );
  384. return( cchString+2 );
  385. }
  386. // unicode => UTF8
  387. //
  388. // use private unicode\utf8 conversion functions
  389. // - superior to public ones (faster, more robust)
  390. // - Win95 does not support CP_UTF8
  391. //
  392. // for unicode-UTF8 there's no invalid string possible
  393. else if ( CharSetOut == DnsCharSetUtf8 )
  394. {
  395. #if 0
  396. length = WideCharToMultiByte(
  397. CP_UTF8,
  398. 0, // no flags
  399. (PWCHAR) pchString,
  400. (INT) cchString,
  401. pBuffer,
  402. MAXWORD, // assuming adequate length
  403. NULL,
  404. NULL );
  405. #endif
  406. length = Dns_UnicodeToUtf8(
  407. (LPWSTR) pchString,
  408. cchString,
  409. pBuffer,
  410. MAXWORD // assuming adequate length
  411. );
  412. ASSERT( length != 0 || cchString == 0 );
  413. pBuffer[ length ] = 0;
  414. return( length + 1 );
  415. }
  416. // unicode => ANSI
  417. // - this conversion can fail
  418. else if ( CharSetOut == DnsCharSetAnsi )
  419. {
  420. length = WideCharToMultiByte(
  421. CP_ACP,
  422. 0, // no flags
  423. (PWCHAR) pchString,
  424. (INT) cchString,
  425. pBuffer,
  426. MAXWORD, // assuming adequate length
  427. NULL,
  428. NULL );
  429. if ( length == 0 && cchString != 0 )
  430. {
  431. goto Failed;
  432. }
  433. pBuffer[ length ] = 0;
  434. return( length + 1 );
  435. }
  436. // bad CharSetOut drops to Failed
  437. }
  438. //
  439. // incoming UTF8
  440. //
  441. if ( CharSetIn == DnsCharSetUtf8 )
  442. {
  443. // UTF8 to UTF8 straight copy
  444. if ( CharSetOut == DnsCharSetUtf8 )
  445. {
  446. memcpy(
  447. pBuffer,
  448. pchString,
  449. cchString );
  450. pBuffer[cchString] = 0;
  451. return( cchString + 1 );
  452. }
  453. // UTF8 to unicode conversion
  454. //
  455. // use private unicode\utf8 conversion functions
  456. // - superior to public ones (faster, more robust)
  457. // - Win95 does not support CP_UTF8
  458. //
  459. // UTF8 strings can be invalid, and since sending in "infinite"
  460. // buffer, this is only possible error
  461. else if ( CharSetOut == DnsCharSetUnicode )
  462. {
  463. #if 0
  464. length = MultiByteToWideChar(
  465. CP_UTF8,
  466. 0, // no flags
  467. (PCHAR) pchString,
  468. (INT) cchString,
  469. (PWCHAR) pBuffer,
  470. MAXWORD // assuming adequate length
  471. );
  472. #endif
  473. length = Dns_Utf8ToUnicode(
  474. pchString,
  475. cchString,
  476. (LPWSTR) pBuffer,
  477. MAXWORD
  478. );
  479. if ( length == 0 && cchString != 0 )
  480. {
  481. ASSERT( GetLastError() == ERROR_INVALID_DATA );
  482. goto Failed;
  483. }
  484. ((PWORD)pBuffer)[length] = 0;
  485. return( (length+1) * 2 );
  486. }
  487. // UTF8 to ANSI
  488. // - note, result length here is actually buffer length
  489. else if ( CharSetOut == DnsCharSetAnsi )
  490. {
  491. length = Dns_Utf8ToAnsi(
  492. pchString,
  493. cchString,
  494. pBuffer,
  495. MAXWORD );
  496. if ( length == 0 )
  497. {
  498. goto Failed;
  499. }
  500. return( length );
  501. }
  502. // bad CharSetOut drops to Failed
  503. }
  504. //
  505. // incoming ANSI
  506. //
  507. if ( CharSetIn == DnsCharSetAnsi )
  508. {
  509. // ANSI to ANSI straight copy
  510. if ( CharSetOut == DnsCharSetAnsi )
  511. {
  512. memcpy(
  513. pBuffer,
  514. pchString,
  515. cchString );
  516. pBuffer[cchString] = 0;
  517. return( cchString + 1 );
  518. }
  519. // ANSI to unicode conversion
  520. // - ANSI to unicode should not fail
  521. else if ( CharSetOut == DnsCharSetUnicode )
  522. {
  523. length = MultiByteToWideChar(
  524. CP_ACP,
  525. 0, // no flags
  526. (PCHAR) pchString,
  527. (INT) cchString,
  528. (PWCHAR) pBuffer,
  529. MAXWORD // assuming adequate length
  530. );
  531. if ( length == 0 && cchString )
  532. {
  533. ASSERT( FALSE );
  534. ASSERT( GetLastError() == ERROR_INVALID_DATA );
  535. goto Failed;
  536. }
  537. ((PWORD)pBuffer)[length] = 0;
  538. return( (length+1) * 2 );
  539. }
  540. // ANSI to UTF8
  541. // - note, result length here is actually buffer length
  542. else if ( CharSetOut == DnsCharSetUtf8 )
  543. {
  544. length = Dns_AnsiToUtf8(
  545. pchString,
  546. cchString,
  547. pBuffer,
  548. MAXWORD );
  549. if ( length == 0 )
  550. {
  551. goto Failed;
  552. }
  553. return( length );
  554. }
  555. // bad CharSetOut drops to Failed
  556. }
  557. // all unhandled cases are failures
  558. Failed:
  559. DNSDBG( ANY, (
  560. "ERROR: Dns_StringCopy() failed!\n"
  561. "\tpBuffer = %p\n"
  562. "\tpdwBufLen = %p\n"
  563. "\tbuf length = %d\n"
  564. "\tpchString = %p (%*s)\n"
  565. "\tcchString = %d\n"
  566. "\tCharSetIn = %d\n"
  567. "\tCharSetOut = %d\n",
  568. pBuffer,
  569. pdwBufLength,
  570. pdwBufLength ? *pdwBufLength : 0,
  571. pchString,
  572. cchString, pchString,
  573. cchString,
  574. CharSetIn,
  575. CharSetOut ));
  576. SetLastError( ERROR_INVALID_DATA );
  577. return( 0 );
  578. }
  579. PVOID
  580. Dns_StringCopyAllocate(
  581. IN PCHAR pchString,
  582. IN DWORD cchString,
  583. IN DNS_CHARSET CharSetIn,
  584. IN DNS_CHARSET CharSetOut
  585. )
  586. /*++
  587. Routine Description:
  588. Create copy of DNS string
  589. Arguments:
  590. pchString -- ptr to string to copy
  591. cchString -- length of string, if known;
  592. - if CharSetIn, then this is length in wide characters
  593. - if NOT given, then pchString MUST be NULL terminated
  594. CharSetIn -- flag indicates incoming string is unicode
  595. CharSetOut -- flag indicates copy will be in unicode format
  596. Return Value:
  597. Ptr to string copy, if successful
  598. NULL on failure.
  599. --*/
  600. {
  601. PCHAR pnew;
  602. DWORD length;
  603. DNSDBG( TRACE, ( "Dns_StringCopyAllocate()\n" ));
  604. DNSDBG( STRING, (
  605. "Dns_StringCopyAllocate( %.*s )\n"
  606. "\tpchString = %p\n"
  607. "\tcchString = %d\n"
  608. "\tUnicodeIn = %d\n"
  609. "\tUnicodeOut = %d\n",
  610. cchString,
  611. pchString,
  612. pchString,
  613. cchString,
  614. CharSetIn,
  615. CharSetOut ));
  616. if ( !pchString )
  617. {
  618. DNS_ASSERT( FALSE );
  619. SetLastError( ERROR_INVALID_PARAMETER );
  620. return( NULL );
  621. }
  622. //
  623. // determine incoming string length
  624. // do this explicitly to avoid doing string length operations twice
  625. //
  626. if ( !cchString )
  627. {
  628. if ( CharSetIn == DnsCharSetUnicode )
  629. {
  630. cchString = (WORD) wcslen( (PWCHAR)pchString );
  631. }
  632. else
  633. {
  634. cchString = strlen( pchString );
  635. }
  636. }
  637. //
  638. // determine required buffer length and allocate
  639. //
  640. length = Dns_GetBufferLengthForStringCopy(
  641. pchString,
  642. cchString,
  643. CharSetIn,
  644. CharSetOut );
  645. if ( length == 0 )
  646. {
  647. ASSERT( CharSetIn && CharSetOut && GetLastError() == ERROR_INVALID_DATA );
  648. SetLastError( ERROR_INVALID_DATA );
  649. return( NULL );
  650. }
  651. pnew = (PVOID) ALLOCATE_HEAP( length );
  652. if ( !pnew )
  653. {
  654. SetLastError( DNS_ERROR_NO_MEMORY );
  655. return( NULL );
  656. }
  657. //
  658. // copy \ convert string
  659. // - can fail if conversion not valid
  660. // (ex. bogus UTF8 string, or attempting
  661. // conversion from ANSI to UTF8)
  662. //
  663. if ( ! Dns_StringCopy(
  664. pnew,
  665. NULL,
  666. pchString,
  667. cchString,
  668. CharSetIn,
  669. CharSetOut ) )
  670. {
  671. FREE_HEAP( pnew );
  672. return( NULL );
  673. }
  674. return( pnew );
  675. }
  676. //
  677. // Simple create string copy utilities.
  678. //
  679. PSTR
  680. Dns_CreateStringCopy_A(
  681. IN PCSTR pszString
  682. )
  683. /*++
  684. Routine Description:
  685. Create copy of string.
  686. Simple wrapper to handle
  687. - sizing
  688. - memory allocation
  689. - copy of string
  690. Arguments:
  691. pszString -- ptr to string to copy
  692. Return Value:
  693. Ptr to string copy, if successful
  694. NULL on failure.
  695. --*/
  696. {
  697. PSTR pnew;
  698. DWORD length;
  699. DNSDBG( TRACE, ( "Dns_CreateStringCopy_A( %s )\n", pszString ));
  700. if ( !pszString )
  701. {
  702. SetLastError( ERROR_INVALID_PARAMETER );
  703. return( NULL );
  704. }
  705. // determine string length, if not given
  706. length = strlen( pszString ) + 1;
  707. // allocate memory
  708. pnew = (LPSTR) ALLOCATE_HEAP( length );
  709. if ( !pnew )
  710. {
  711. SetLastError( DNS_ERROR_NO_MEMORY );
  712. return( NULL );
  713. }
  714. // copy and NULL terminate
  715. RtlCopyMemory(
  716. pnew,
  717. pszString,
  718. length );
  719. return( pnew );
  720. }
  721. PWSTR
  722. Dns_CreateStringCopy_W(
  723. IN PCWSTR pwsString
  724. )
  725. {
  726. PWSTR pnew;
  727. DWORD length;
  728. DNSDBG( TRACE, ( "Dns_CreateStringCopy_W( %S )\n", pwsString ));
  729. if ( !pwsString )
  730. {
  731. SetLastError( ERROR_INVALID_PARAMETER );
  732. return( NULL );
  733. }
  734. // allocate memory
  735. length = (wcslen( pwsString ) + 1) * sizeof(WCHAR);
  736. pnew = (PWSTR) ALLOCATE_HEAP( length );
  737. if ( !pnew )
  738. {
  739. SetLastError( DNS_ERROR_NO_MEMORY );
  740. return( NULL );
  741. }
  742. // copy and NULL terminate
  743. RtlCopyMemory(
  744. pnew,
  745. pwsString,
  746. length );
  747. return( pnew );
  748. }
  749. PWSTR
  750. Dns_CreateConcatenatedString_W(
  751. IN PCWSTR * pStringArray
  752. )
  753. /*++
  754. Routine Description:
  755. Create concatenated string.
  756. Arguments:
  757. pStringArray -- array of string pointers to concat
  758. NULL pointer terminates array
  759. Return Value:
  760. Ptr to concantenated string copy, if successful
  761. NULL on failure.
  762. --*/
  763. {
  764. PWSTR pnew;
  765. PCWSTR pwstr;
  766. DWORD length;
  767. DWORD iter;
  768. DNSDBG( TRACE, ( "Dns_CreateConcatenatedString_W()\n" ));
  769. if ( !pStringArray )
  770. {
  771. SetLastError( ERROR_INVALID_PARAMETER );
  772. return( NULL );
  773. }
  774. //
  775. // loop determining required length
  776. //
  777. length = 1;
  778. iter = 0;
  779. while ( pwstr = pStringArray[iter++] )
  780. {
  781. length += wcslen( pwstr );
  782. }
  783. //
  784. // allocate
  785. //
  786. pnew = (PWSTR) ALLOCATE_HEAP( length*sizeof(WCHAR) );
  787. if ( !pnew )
  788. {
  789. SetLastError( DNS_ERROR_NO_MEMORY );
  790. return NULL;
  791. }
  792. //
  793. // write concatented string
  794. //
  795. pnew[0] = 0;
  796. iter = 0;
  797. while ( pwstr = pStringArray[iter++] )
  798. {
  799. wcscat( pnew, pwstr );
  800. }
  801. DNSDBG( TRACE, ( "Concatented string = %S\n", pnew ));
  802. return pnew;
  803. }
  804. //
  805. // MULTI_SZ routines
  806. //
  807. DWORD
  808. MultiSz_Length_A(
  809. IN PCSTR pmszString
  810. )
  811. /*++
  812. Routine Description:
  813. Determine length (size) of MULTI_SZ string.
  814. Arguments:
  815. pmszString -- ptr to string to size
  816. Return Value:
  817. Size of MULTI_SZ string (in bytes).
  818. Includes terminating double NULL.
  819. --*/
  820. {
  821. PSTR pnext;
  822. DWORD lengthTotal = 0;
  823. DWORD length;
  824. DNSDBG( TRACE, ( "MultiSz_Length_A( %s )\n", pmszString ));
  825. //
  826. // loop until read at end of strings
  827. //
  828. // when we reach the end, we'll be pointing at the second
  829. // zero in the double null terminator; strlen() will return
  830. // zero, and we'll add that to our count as 1 and exit
  831. //
  832. pnext = (PSTR) pmszString;
  833. while ( pnext )
  834. {
  835. length = strlen( pnext ) + 1;
  836. lengthTotal += length;
  837. if ( length == 1 )
  838. {
  839. break;
  840. }
  841. pnext += length;
  842. }
  843. return lengthTotal;
  844. }
  845. PSTR
  846. MultiSz_NextString_A(
  847. IN PCSTR pmszString
  848. )
  849. /*++
  850. Routine Description:
  851. Find next string in MULTI_SZ string
  852. Arguments:
  853. pmszString -- ptr to multi string
  854. Return Value:
  855. Next string in MULTI_SZ string.
  856. NULL if no strings left.
  857. --*/
  858. {
  859. PSTR pnext;
  860. DWORD length;
  861. DNSDBG( TRACE, ( "MultiSz_NextString_A( %s )\n", pmszString ));
  862. //
  863. // find next string in multi-string
  864. // - find length of current string
  865. // - hop over it (inc. null)
  866. // - if pointing at terminating double-null return
  867. // NULL to signal end
  868. //
  869. pnext = (PSTR) pmszString;
  870. if ( !pnext )
  871. {
  872. return NULL;
  873. }
  874. length = strlen( pnext );
  875. if ( length == 0 )
  876. {
  877. DNSDBG( ANY, (
  878. "ERROR: MultiSz_Next(%p) called on terminator!\n",
  879. pmszString ));
  880. return NULL;
  881. }
  882. pnext += length + 1;
  883. if ( *pnext == 0 )
  884. {
  885. return NULL;
  886. }
  887. return pnext;
  888. }
  889. PSTR
  890. MultiSz_Copy_A(
  891. IN PCSTR pmszString
  892. )
  893. /*++
  894. Routine Description:
  895. Create copy of MULTI_SZ string.
  896. Simple wrapper to handle
  897. - sizing
  898. - memory allocation
  899. - copy of string
  900. Arguments:
  901. pmszString -- ptr to string to copy
  902. Return Value:
  903. Ptr to string copy, if successful
  904. NULL on failure.
  905. --*/
  906. {
  907. PSTR pnew;
  908. DWORD length;
  909. DNSDBG( TRACE, ( "MultiSz_Copy_A( %s )\n", pmszString ));
  910. if ( !pmszString )
  911. {
  912. SetLastError( ERROR_INVALID_PARAMETER );
  913. return( NULL );
  914. }
  915. // determine string length, if not given
  916. length = MultiSz_Length_A( pmszString );
  917. // allocate memory
  918. pnew = (LPSTR) ALLOCATE_HEAP( length );
  919. if ( !pnew )
  920. {
  921. SetLastError( DNS_ERROR_NO_MEMORY );
  922. return( NULL );
  923. }
  924. // copy and NULL terminate
  925. RtlCopyMemory(
  926. pnew,
  927. pmszString,
  928. length );
  929. return( pnew );
  930. }
  931. //
  932. // Random
  933. //
  934. INT
  935. wcsicmp_ThatWorks(
  936. IN PWSTR pString1,
  937. IN PWSTR pString2
  938. )
  939. /*++
  940. Routine Description:
  941. A version of wcsicmp that actually works.
  942. This is just a wrapped on CompareStringW, to hide all the detail
  943. and give an interface identical to wcsicmp().
  944. It uses US English to standardize the comparison.
  945. Arguments:
  946. pString1 -- first string; must be NULL terminated
  947. pString2 -- first second; must be NULL terminated
  948. Return Value:
  949. -1 -- if string 1 less than string 2
  950. 0 -- strings are equal
  951. 1 -- if string 1 greater than string 2
  952. --*/
  953. {
  954. INT result;
  955. //
  956. // compare
  957. // - case conversion done in default DNS locale -- US English
  958. // this locale correctly matches most non-locale sensitive
  959. // upper-lower characters
  960. //
  961. result = CompareStringW(
  962. DNS_DEFAULT_LOCALE,
  963. NORM_IGNORECASE,
  964. pString1,
  965. (-1), // NULL terminated
  966. pString2,
  967. (-1) // NULL terminated
  968. );
  969. if ( result == CSTR_EQUAL )
  970. {
  971. result = 0;
  972. }
  973. else if ( result == CSTR_LESS_THAN )
  974. {
  975. result = -1;
  976. }
  977. else // greater than or error
  978. {
  979. result = 1;
  980. }
  981. return( result );
  982. }
  983. LPWSTR
  984. Dns_GetResourceString(
  985. IN DWORD dwStringId,
  986. IN OUT LPWSTR pwszBuffer,
  987. IN DWORD cbBuffer
  988. )
  989. /*++
  990. Routine Description:
  991. Loads a string (defined in dnsmsg.mc) from current module
  992. Arguments:
  993. dwStringId -- The ID of the string to be fetched
  994. Return Value:
  995. DCR: kill off eyal function
  996. DEVNOTE: don't understand the value of this return
  997. -- it's essentially a BOOL, we already know what the ptr is
  998. it's the buffer passed in
  999. -- ptr to next byte is useful in continuous write situation
  1000. (ugly and useless in others)
  1001. -- better would just be the same return as LoadString, so we
  1002. both get the success\failure indication and also know
  1003. how many bytes forward we must push our buffer ptr if
  1004. we want to write more
  1005. Error: NULL
  1006. Success: a pointer to the loaded string
  1007. --*/
  1008. {
  1009. LPWSTR pStr = NULL;
  1010. DWORD status;
  1011. HANDLE hMod;
  1012. DNSDBG( TRACE, (
  1013. "Dns_GetStringResource()\n" ));
  1014. // Get module handle-- No need to close handle, it is just a ptr w/o increment on ref count.
  1015. hMod = GetModuleHandle( NULL );
  1016. if ( !hMod )
  1017. {
  1018. ASSERT( hMod );
  1019. return NULL;
  1020. }
  1021. status = LoadStringW(
  1022. hMod,
  1023. dwStringId,
  1024. pwszBuffer,
  1025. cbBuffer );
  1026. if ( status != 0 )
  1027. {
  1028. pStr = pwszBuffer;
  1029. }
  1030. ELSE
  1031. {
  1032. // LoadString returns # of bytes loaded, convert to error.
  1033. status = GetLastError();
  1034. DNSDBG( TRACE, (
  1035. "Error <%lu>: Failed to load string %d\n",
  1036. status, dwStringId ));
  1037. ASSERT ( FALSE );
  1038. }
  1039. DNSDBG( TRACE, (
  1040. "Exit <0x%p> Dns_GetStringResource\n",
  1041. pStr ));
  1042. return pStr;
  1043. }
  1044. //
  1045. // End string.c
  1046. //