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.

2417 lines
69 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation. All rights reserved.
  3. Module Name:
  4. strtab.c
  5. Abstract:
  6. String table functions for Windows NT Setup API dll
  7. A string table is a block of memory that contains a bunch of strings.
  8. Hashing is used, and each hash table entry points to a linked list
  9. of strings within the string table. Strings within each linked list
  10. are sorted in ascending order. A node in the linked list consists of
  11. a pointer to the next node, followed by the string itself. Nodes
  12. are manually aligned to start on DWORD boundaries so we don't have to
  13. resort to using unaligned pointers.
  14. Author:
  15. Ted Miller (tedm) Jan-11-1995
  16. Revision History:
  17. Jamie Hunter (JamieHun) Jan-15-1997 fixed minor bug regarding use of STRTAB_NEW_EXTRADATA
  18. Jamie Hunter (JamieHun) Feb-8-2000 improved string table growth algorithm
  19. Jamie Hunter (JamieHun) Jun-27-2000 moved to sputils static library
  20. --*/
  21. #include "precomp.h"
  22. #pragma hdrstop
  23. //
  24. // Values used for the initial and growth size
  25. // of the string table data area
  26. //
  27. // We start out with 6K, but remember that this includes the hash buckets.
  28. // After you subtract their part of the buffer, you're left with ~4K bytes.
  29. // STRING_TABLE_NEW_SIZE_ADJUST - determines approximate increase
  30. // STRING_TABLE_NEW_SIZE - will increase oldsize by at least STRING_TABLE_GROWTH_SIZE
  31. // and a multiple of STRING_TABLE_GROWTH_SIZE
  32. // if string table gets very big, we limit growth to STRING_TABLE_GROWTH_CAP bytes.
  33. //
  34. #define STRING_TABLE_INITIAL_SIZE 6144
  35. #define STRING_TABLE_GROWTH_SIZE 2048
  36. #define STRING_TABLE_GROWTH_CAP 0x100000
  37. #define STRING_TABLE_NEW_SIZE_ADJUST(oldsize) ((oldsize)/3*2)
  38. #define STRING_TABLE_NEW_SIZE(oldsize) \
  39. (oldsize+min((((DWORD)(STRING_TABLE_NEW_SIZE_ADJUST(oldsize)/STRING_TABLE_GROWTH_SIZE)+1)*STRING_TABLE_GROWTH_SIZE),STRING_TABLE_GROWTH_CAP))
  40. //
  41. // WARNING:
  42. //
  43. // Don't change this structure, various file formats depend upon it
  44. //
  45. #include "pshpack1.h"
  46. #ifdef SPUTILSW
  47. //
  48. // name mangling so the names don't conflict with any in sputilsa.lib
  49. //
  50. #define _STRING_NODE _STRING_NODE_W
  51. #define STRING_NODE STRING_NODE_W
  52. #define PSTRING_NODE PSTRING_NODE_W
  53. #define _STRING_TABLE _STRING_TABLE_W
  54. #define STRING_TABLE STRING_TABLE_W
  55. #define PSTRING_TABLE PSTRING_TABLE_W
  56. #endif // SPUTILSW
  57. typedef struct _STRING_NODE {
  58. //
  59. // This is stored as an offset instead of a pointer
  60. // because the table can move as it's built
  61. // The offset is from the beginning of the table
  62. //
  63. LONG NextOffset;
  64. //
  65. // This field must be last
  66. //
  67. TCHAR String[ANYSIZE_ARRAY];
  68. } STRING_NODE, *PSTRING_NODE;
  69. #include "poppack.h"
  70. //
  71. // in-memory details about the string table
  72. //
  73. typedef struct _STRING_TABLE {
  74. PUCHAR Data; // First HASH_BUCKET_COUNT DWORDS are StringNodeOffset array.
  75. DWORD DataSize;
  76. DWORD BufferSize;
  77. MYLOCK Lock;
  78. UINT ExtraDataSize;
  79. LCID Locale;
  80. } STRING_TABLE, *PSTRING_TABLE;
  81. #define LockTable(table) BeginSynchronizedAccess(&((table)->Lock))
  82. #define UnlockTable(table) EndSynchronizedAccess(&((table)->Lock))
  83. #ifdef UNICODE
  84. #define FixedCompareString CompareString
  85. #else
  86. #include <locale.h>
  87. #include <mbctype.h>
  88. static
  89. INT
  90. FixedCompareString (
  91. IN LCID Locale,
  92. IN DWORD Flags,
  93. IN PCSTR FirstString,
  94. IN INT Count1,
  95. IN PCSTR SecondString,
  96. IN INT Count2
  97. )
  98. {
  99. LCID OldLocale;
  100. INT Result = 0;
  101. //
  102. // This routine uses the C runtime to compare the strings, because
  103. // the Win32 APIs are broken on some versions of Win95
  104. //
  105. OldLocale = GetThreadLocale();
  106. if (OldLocale != Locale) {
  107. SetThreadLocale (Locale);
  108. setlocale(LC_ALL,"");
  109. }
  110. __try {
  111. if (Count1 == -1) {
  112. Count1 = strlen (FirstString);
  113. }
  114. if (Count2 == -1) {
  115. Count2 = strlen (SecondString);
  116. }
  117. //
  118. // The C runtime compares strings differently than the CompareString
  119. // API. Most importantly, the C runtime considers uppercase to be
  120. // less than lowercase; the CompareString API is the opposite.
  121. //
  122. if (Flags & NORM_IGNORECASE) {
  123. Result = _mbsnbicmp (FirstString, SecondString, min (Count1, Count2));
  124. } else {
  125. Result = _mbsnbcmp (FirstString, SecondString, min (Count1, Count2));
  126. }
  127. //
  128. // We now convert the C runtime result into the CompareString result.
  129. // This means making the comparison a Z to A ordering, with lowercase
  130. // coming before uppercase. The length comparison does not get reversed.
  131. //
  132. if(Result == _NLSCMPERROR) {
  133. Result = 0; // zero returned if _mbsnbicmp could not compare
  134. } else if (Result < 0) {
  135. Result = CSTR_GREATER_THAN;
  136. } else if (Result == 0) {
  137. if (Count1 < Count2) {
  138. Result = CSTR_LESS_THAN; // first string shorter than second
  139. } else if (Count1 > Count2) {
  140. Result = CSTR_GREATER_THAN; // first string longer than second
  141. } else {
  142. Result = CSTR_EQUAL;
  143. }
  144. } else {
  145. Result = CSTR_LESS_THAN;
  146. }
  147. }
  148. __except (TRUE) {
  149. Result = 0;
  150. }
  151. if (OldLocale != Locale) {
  152. SetThreadLocale (OldLocale);
  153. setlocale(LC_ALL,"");
  154. }
  155. return Result;
  156. }
  157. #endif
  158. static
  159. DWORD
  160. _StringTableCheckFlags(
  161. IN DWORD FlagsIn
  162. )
  163. /*++
  164. Routine Description:
  165. Pre-process flags, called by exported routines we want to handle the
  166. combination of CASE_INSENSITIVE, CASE_SENSITIVE and BUFFER_WRITEABLE
  167. and keep all other flags as is.
  168. Arguments:
  169. FlagsIn - flags as supplied
  170. Return Value:
  171. Flags out
  172. --*/
  173. {
  174. DWORD FlagsOut;
  175. DWORD FlagsSpecial;
  176. //
  177. // we're just interested in these flags for the switch
  178. //
  179. FlagsSpecial = FlagsIn & (STRTAB_CASE_SENSITIVE | STRTAB_BUFFER_WRITEABLE);
  180. //
  181. // strip these off FlagsIn to create initial FlagsOut
  182. //
  183. FlagsOut = FlagsIn ^ FlagsSpecial;
  184. switch (FlagsSpecial) {
  185. case STRTAB_CASE_INSENSITIVE :
  186. case STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE :
  187. //
  188. // these cases ok
  189. //
  190. FlagsOut |= FlagsSpecial;
  191. break;
  192. default :
  193. //
  194. // any other combination is treated as STRTAB_CASE_SENSITIVE (and so
  195. // WRITEABLE doesn't matter)
  196. //
  197. FlagsOut |= STRTAB_CASE_SENSITIVE;
  198. }
  199. return FlagsOut;
  200. }
  201. static
  202. VOID
  203. _ComputeHashValue(
  204. IN PTSTR String,
  205. OUT PDWORD StringLength,
  206. IN DWORD Flags,
  207. OUT PDWORD HashValue
  208. )
  209. /*++
  210. Routine Description:
  211. Compute a hash value for a given string.
  212. The algorithm simply adds up the unicode values for each
  213. character in the string and then takes the result mod the
  214. number of hash buckets.
  215. Arguments:
  216. String - supplies the string for which a hash value is desired.
  217. StringLength - receives the number of characters in the string,
  218. not including the terminating nul.
  219. Flags - supplies flags controlling how the hashing is to be done. May be
  220. a combination of the following values (all other bits ignored):
  221. STRTAB_BUFFER_WRITEABLE - The caller-supplied buffer may be written to during
  222. the string look-up. Specifying this flag improves the
  223. performance of this API for case-insensitive string
  224. additions. This flag is ignored for case-sensitive
  225. string additions.
  226. STRTAB_ALREADY_LOWERCASE - The supplied string has already been converted to
  227. all lower-case (e.g., by calling CharLower), and
  228. therefore doesn't need to be lower-cased in the
  229. hashing routine. If this flag is supplied, then
  230. STRTAB_BUFFER_WRITEABLE is ignored, since modifying
  231. the caller's buffer is not required.
  232. HashValue - receives the hash value.
  233. Return Value:
  234. None.
  235. --*/
  236. {
  237. DWORD Length;
  238. DWORD Value = 0;
  239. PCTSTR p, q;
  240. DWORD Char;
  241. try {
  242. if((Flags & (STRTAB_BUFFER_WRITEABLE | STRTAB_ALREADY_LOWERCASE)) == STRTAB_BUFFER_WRITEABLE) {
  243. //
  244. // Then the buffer is writeable, but isn't yet lower-case. Take care of that right now.
  245. //
  246. #ifndef UNICODE
  247. _mbslwr (String);
  248. #else
  249. CharLower(String);
  250. #endif
  251. Flags |= STRTAB_ALREADY_LOWERCASE;
  252. }
  253. //
  254. // Define a macro to ensure we don't get sign-extension when adding up character values.
  255. //
  256. #ifdef UNICODE
  257. #define DWORD_FROM_TCHAR(x) ((DWORD)((WCHAR)(x)))
  258. #else
  259. #define DWORD_FROM_TCHAR(x) ((DWORD)((UCHAR)(x)))
  260. #endif
  261. p = String;
  262. if(Flags & STRTAB_ALREADY_LOWERCASE) {
  263. while (*p) {
  264. Value += DWORD_FROM_TCHAR (*p);
  265. p++;
  266. }
  267. } else {
  268. //
  269. // Make sure we don't get sign-extension on extended chars
  270. // in String -- otherwise we get values like 0xffffffe4 passed
  271. // to CharLower(), which thinks it's a pointer and faults.
  272. //
  273. #ifdef UNICODE
  274. //
  275. // The WCHAR case is trivial
  276. //
  277. while (*p) {
  278. Value += DWORD_FROM_TCHAR(CharLower((PWSTR)(WORD) (*p)));
  279. p++;
  280. }
  281. #else
  282. //
  283. // The DBCS case is a mess because of the possibility of CharLower
  284. // altering a two-byte character
  285. // Standardize to use _mbslwr as that is used elsewhere
  286. // ie, if we did _mbslwr, & called this function with
  287. // flag set to say "already lower", vs we called function
  288. // with buffer writable, vs calling with neither
  289. // we should ensure we get same hash in each case
  290. // it may fail, but at least it will fail *universally* and
  291. // generate the same hash
  292. //
  293. PTSTR copy = pSetupDuplicateString(String);
  294. if(copy) {
  295. //
  296. // do conversion on copied string
  297. //
  298. _mbslwr(copy);
  299. p = copy;
  300. while (*p) {
  301. Value += DWORD_FROM_TCHAR (*p);
  302. p++;
  303. }
  304. pSetupFree(copy);
  305. p = String+lstrlen(String);
  306. } else {
  307. //
  308. // we had a memory failure
  309. //
  310. *HashValue = 0;
  311. *StringLength = 0;
  312. leave;
  313. }
  314. #endif
  315. }
  316. *HashValue = Value % HASH_BUCKET_COUNT;
  317. *StringLength = (DWORD)(p - String);
  318. } except(EXCEPTION_EXECUTE_HANDLER) {
  319. //
  320. // Inbound string was bogus
  321. //
  322. *HashValue = 0;
  323. *StringLength = 0;
  324. MYASSERT(FALSE);
  325. }
  326. }
  327. BOOL
  328. _pSpUtilsStringTableLock(
  329. IN PVOID StringTable
  330. )
  331. /*++
  332. Routine Description:
  333. This routine acquires the lock for the specified string table (it's
  334. implemented as a function call for uses in setupapi where locking needs to
  335. be explicitly controlled).
  336. Arguments:
  337. StringTable - supplies handle to string table to be locked.
  338. Return Value:
  339. If the lock was successfully acquired, the return value is TRUE.
  340. Otherwise, the return value is FALSE.
  341. --*/
  342. {
  343. return LockTable((PSTRING_TABLE)StringTable);
  344. }
  345. VOID
  346. _pSpUtilsStringTableUnlock(
  347. IN PVOID StringTable
  348. )
  349. /*++
  350. Routine Description:
  351. This routine releases the lock (previously acquired via
  352. _pSpUtilsStringTableLock) for the specified string table.
  353. Arguments:
  354. StringTable - supplies handle to string table to be unlocked.
  355. Return Value:
  356. None.
  357. --*/
  358. {
  359. UnlockTable((PSTRING_TABLE)StringTable);
  360. }
  361. LONG
  362. _pSpUtilsStringTableLookUpString(
  363. IN PVOID StringTable,
  364. IN OUT PTSTR String,
  365. OUT PDWORD StringLength,
  366. OUT PDWORD HashValue, OPTIONAL
  367. OUT PVOID *FindContext, OPTIONAL
  368. IN DWORD Flags,
  369. OUT PVOID ExtraData, OPTIONAL
  370. IN UINT ExtraDataBufferSize OPTIONAL
  371. )
  372. /*++
  373. Routine Description:
  374. Locates a string in the string table, if present.
  375. If the string is not present, this routine may optionally tell its
  376. caller where the search stopped. This is useful for maintaining a
  377. sorted order for the strings.
  378. Arguments:
  379. StringTable - supplies handle to string table to be searched
  380. for the string
  381. String - supplies the string to be looked up
  382. StringLength - receives number of characters in the string, not
  383. including the terminating nul.
  384. HashValue - Optionally, receives hash value for the string.
  385. FindContext - Optionally, receives the context at which the search was
  386. terminated.
  387. (NOTE: This is actually a PSTRING_NODE pointer, that is used
  388. during new string addition. Since this routine has wider exposure
  389. than just internal string table usage, this parameter is made into
  390. a PVOID, so no one else has to have access to string table-internal
  391. structures.
  392. On return, this variable receives a pointer to the string node of
  393. the node where the search stopped. If the string was found, then
  394. this is a pointer to the string's node. If the string was not found,
  395. then this is a pointer to the last string node whose string is
  396. 'less' (based on lstrcmpi) than the string we're looking for.
  397. Note that this value may be NULL.)
  398. Flags - supplies flags controlling how the string is to be located. May be
  399. a combination of the following values:
  400. STRTAB_CASE_INSENSITIVE - Search for the string case-insensitively.
  401. STRTAB_CASE_SENSITIVE - Search for the string case-sensitively. This flag
  402. overrides the STRTAB_CASE_INSENSITIVE flag.
  403. STRTAB_BUFFER_WRITEABLE - The caller-supplied buffer may be written to during
  404. the string look-up. Specifying this flag improves the
  405. performance of this API for case-insensitive string
  406. additions. This flag is ignored for case-sensitive
  407. string additions.
  408. In addition to the above public flags, the following private flag is also
  409. allowed:
  410. STRTAB_ALREADY_LOWERCASE - The supplied string has already been converted to
  411. all lower-case (e.g., by calling CharLower), and
  412. therefore doesn't need to be lower-cased in the
  413. hashing routine. If this flag is supplied, then
  414. STRTAB_BUFFER_WRITEABLE is ignored, since modifying
  415. the caller's buffer is not required.
  416. ExtraData - if specified, receives extra data associated with the string
  417. if the string is found.
  418. ExtraDataBufferSize - if ExtraData is specified, then this parameter
  419. specifies the size of the buffer, in bytes. As much extra data as will fit
  420. is stored here.
  421. Return Value:
  422. The return value is a value that uniquely identifies the string
  423. within the string table, namely the offset of the string node
  424. within the string table.
  425. If the string could not be found the value is -1.
  426. --*/
  427. {
  428. PSTRING_NODE node,prev;
  429. int i;
  430. PSTRING_TABLE stringTable = StringTable;
  431. DWORD hashValue;
  432. PSTRING_NODE FinalNode;
  433. LONG rc = -1;
  434. LCID Locale;
  435. DWORD CompareFlags;
  436. BOOL CollateEnded = FALSE;
  437. //
  438. // If this is a case-sensitive lookup, then we want to reset the STRTAB_BUFFER_WRITEABLE
  439. // flag, if present, since otherwise the string will get replaced with its all-lowercase
  440. // counterpart.
  441. //
  442. if(Flags & STRTAB_CASE_SENSITIVE) {
  443. Flags &= ~STRTAB_BUFFER_WRITEABLE;
  444. }
  445. //
  446. // Compute hash value
  447. //
  448. _ComputeHashValue(String,StringLength,Flags,&hashValue);
  449. if(((PLONG)(stringTable->Data))[hashValue] == -1) {
  450. //
  451. // The string table contains no strings at the computed hash value.
  452. //
  453. FinalNode = NULL;
  454. goto clean0;
  455. }
  456. //
  457. // We know there's at least one string in the table with the computed
  458. // hash value, so go find it. There's no previous node yet.
  459. //
  460. node = (PSTRING_NODE)(stringTable->Data + ((PLONG)(stringTable->Data))[hashValue]);
  461. prev = NULL;
  462. //
  463. // Go looking through the string nodes for that hash value,
  464. // looking through the string.
  465. //
  466. Locale = stringTable->Locale;
  467. CompareFlags = (Flags & STRTAB_CASE_SENSITIVE) ? 0 : NORM_IGNORECASE;
  468. while(1) {
  469. if(i = FixedCompareString(Locale,CompareFlags,String,-1,node->String,-1)) {
  470. i -= 2;
  471. } else {
  472. //
  473. // Failure, try system default locale
  474. //
  475. if(i = FixedCompareString(LOCALE_SYSTEM_DEFAULT,CompareFlags,String,-1,node->String,-1)) {
  476. i -= 2;
  477. } else {
  478. //
  479. // Failure, just use CRTs
  480. //
  481. // This could give wrong collation?? If it does, we're stuck with it now.
  482. //
  483. i = (Flags & STRTAB_CASE_SENSITIVE)
  484. ? _tcscmp(String,node->String)
  485. : _tcsicmp(String,node->String);
  486. }
  487. }
  488. if(i == 0) {
  489. FinalNode = node;
  490. rc = (LONG)((PUCHAR)node - stringTable->Data);
  491. break;
  492. }
  493. //
  494. // If the string we are looking for is 'less' than the current
  495. // string, mark it's position so we can insert a new string before here
  496. // (ANSI) but keep searching (UNICODE) we can abort - old behaviour
  497. //
  498. if((i < 0) && !CollateEnded) {
  499. CollateEnded = TRUE;
  500. FinalNode = prev;
  501. #if UNICODE
  502. break;
  503. #endif
  504. }
  505. //
  506. // The string we are looking for is 'greater' than the current string.
  507. // Keep looking, unless we've reached the end of the table.
  508. //
  509. if(node->NextOffset == -1) {
  510. if(!CollateEnded)
  511. {
  512. //
  513. // unless we found a more ideal position
  514. // return the end of the list
  515. //
  516. FinalNode = node;
  517. }
  518. break;
  519. } else {
  520. prev = node;
  521. node = (PSTRING_NODE)(stringTable->Data + node->NextOffset);
  522. }
  523. }
  524. clean0:
  525. if((rc != -1) && ExtraData) {
  526. //
  527. // Extra data is stored immediately following the string.
  528. //
  529. CopyMemory(
  530. ExtraData,
  531. FinalNode->String + *StringLength + 1,
  532. min(ExtraDataBufferSize,stringTable->ExtraDataSize)
  533. );
  534. }
  535. if(HashValue) {
  536. *HashValue = hashValue;
  537. }
  538. if(FindContext) {
  539. *FindContext = FinalNode;
  540. }
  541. return rc;
  542. }
  543. LONG
  544. pSetupStringTableLookUpString(
  545. IN PVOID StringTable,
  546. IN OUT PTSTR String,
  547. IN DWORD Flags
  548. )
  549. /*++
  550. Routine Description:
  551. Locates a string in the string table, if present.
  552. Arguments:
  553. StringTable - supplies handle to string table to be searched
  554. for the string
  555. String - supplies the string to be looked up. If STRTAB_BUFFER_WRITEABLE is
  556. specified and a case-insensitive lookup is requested, then this buffer
  557. will be all lower-case upon return.
  558. Flags - supplies flags controlling how the string is to be located. May be
  559. a combination of the following values:
  560. STRTAB_CASE_INSENSITIVE - Search for the string case-insensitively.
  561. STRTAB_CASE_SENSITIVE - Search for the string case-sensitively. This flag
  562. overrides the STRTAB_CASE_INSENSITIVE flag.
  563. STRTAB_BUFFER_WRITEABLE - The caller-supplied buffer may be written to during
  564. the string look-up. Specifying this flag improves the
  565. performance of this API for case-insensitive string
  566. additions. This flag is ignored for case-sensitive
  567. string additions.
  568. In addition to the above public flags, the following private flag is also
  569. allowed:
  570. STRTAB_ALREADY_LOWERCASE - The supplied string has already been converted to
  571. all lower-case (e.g., by calling CharLower), and
  572. therefore doesn't need to be lower-cased in the
  573. hashing routine. If this flag is supplied, then
  574. STRTAB_BUFFER_WRITEABLE is ignored, since modifying
  575. the caller's buffer is not required.
  576. Return Value:
  577. The return value is a value that uniquely identifies the string
  578. within the string table.
  579. If the string could not be found the value is -1.
  580. --*/
  581. {
  582. DWORD StringLength, PrivateFlags, AlreadyLcFlag;
  583. LONG rc = -1;
  584. BOOL locked = FALSE;
  585. try {
  586. if (!LockTable((PSTRING_TABLE)StringTable)) {
  587. leave;
  588. }
  589. locked = TRUE;
  590. PrivateFlags = _StringTableCheckFlags(Flags);
  591. rc = _pSpUtilsStringTableLookUpString(
  592. StringTable,
  593. String,
  594. &StringLength,
  595. NULL,
  596. NULL,
  597. PrivateFlags,
  598. NULL,
  599. 0
  600. );
  601. } except (EXCEPTION_EXECUTE_HANDLER) {
  602. rc = -1;
  603. }
  604. if (locked) {
  605. UnlockTable((PSTRING_TABLE)StringTable);
  606. }
  607. return (rc);
  608. }
  609. LONG
  610. pSetupStringTableLookUpStringEx(
  611. IN PVOID StringTable,
  612. IN OUT PTSTR String,
  613. IN DWORD Flags,
  614. OUT PVOID ExtraData, OPTIONAL
  615. IN UINT ExtraDataBufferSize OPTIONAL
  616. )
  617. /*++
  618. Routine Description:
  619. Locates a string in the string table, if present.
  620. Arguments:
  621. StringTable - supplies handle to string table to be searched
  622. for the string
  623. String - supplies the string to be looked up. If STRTAB_BUFFER_WRITEABLE is
  624. specified and a case-insensitive lookup is requested, then this buffer
  625. will be all lower-case upon return.
  626. Flags - supplies flags controlling how the string is to be located. May be
  627. a combination of the following values:
  628. STRTAB_CASE_INSENSITIVE - Search for the string case-insensitively.
  629. STRTAB_CASE_SENSITIVE - Search for the string case-sensitively. This flag
  630. overrides the STRTAB_CASE_INSENSITIVE flag.
  631. STRTAB_BUFFER_WRITEABLE - The caller-supplied buffer may be written to during
  632. the string look-up. Specifying this flag improves the
  633. performance of this API for case-insensitive string
  634. additions. This flag is ignored for case-sensitive
  635. string additions.
  636. In addition to the above public flags, the following private flag is also
  637. allowed:
  638. STRTAB_ALREADY_LOWERCASE - The supplied string has already been converted to
  639. all lower-case (e.g., by calling CharLower), and
  640. therefore doesn't need to be lower-cased in the
  641. hashing routine. If this flag is supplied, then
  642. STRTAB_BUFFER_WRITEABLE is ignored, since modifying
  643. the caller's buffer is not required.
  644. ExtraData - if specified, receives extra data associated with the string
  645. if the string is found.
  646. ExtraDataBufferSize - if ExtraData is specified, then this parameter
  647. specifies the size of the buffer, in bytes. As much extra data as will fit
  648. is stored here.
  649. Return Value:
  650. The return value is a value that uniquely identifies the string
  651. within the string table.
  652. If the string could not be found the value is -1.
  653. --*/
  654. {
  655. DWORD StringLength, PrivateFlags, AlreadyLcFlag;
  656. LONG rc = -1;
  657. BOOL locked = FALSE;
  658. try {
  659. if(!LockTable((PSTRING_TABLE)StringTable)) {
  660. leave;
  661. }
  662. locked = TRUE;
  663. PrivateFlags = _StringTableCheckFlags(Flags);
  664. rc = _pSpUtilsStringTableLookUpString(
  665. StringTable,
  666. String,
  667. &StringLength,
  668. NULL,
  669. NULL,
  670. PrivateFlags,
  671. ExtraData,
  672. ExtraDataBufferSize
  673. );
  674. } except (EXCEPTION_EXECUTE_HANDLER) {
  675. rc = -1;
  676. }
  677. if (locked) {
  678. UnlockTable((PSTRING_TABLE)StringTable);
  679. }
  680. return (rc);
  681. }
  682. BOOL
  683. pSetupStringTableGetExtraData(
  684. IN PVOID StringTable,
  685. IN LONG StringId,
  686. OUT PVOID ExtraData,
  687. IN UINT ExtraDataBufferSize
  688. )
  689. /*++
  690. Routine Description:
  691. Get arbitrary data associated with a string table entry.
  692. Arguments:
  693. StringTable - supplies handle to string table containing the string
  694. whose associated data is to be returned.
  695. String - supplies the id of the string whose associated data is to be returned.
  696. ExtraData - receives the data associated with the string. Data is truncated
  697. to fit, if necessary.
  698. ExtraDataBufferSize - supplies the size in bytes of the buffer specified
  699. by ExtraData. If this value is smaller than the extra data size for
  700. the string table, data is truncated to fit.
  701. Return Value:
  702. Boolean value indicating outcome.
  703. --*/
  704. {
  705. BOOL b = FALSE;
  706. BOOL locked = FALSE;
  707. try {
  708. if(!LockTable((PSTRING_TABLE)StringTable)) {
  709. leave;
  710. }
  711. locked = TRUE;
  712. b = _pSpUtilsStringTableGetExtraData(StringTable,StringId,ExtraData,ExtraDataBufferSize);
  713. } except (EXCEPTION_EXECUTE_HANDLER) {
  714. b = FALSE;
  715. }
  716. if (locked) {
  717. UnlockTable((PSTRING_TABLE)StringTable);
  718. }
  719. return(b);
  720. }
  721. BOOL
  722. _pSpUtilsStringTableGetExtraData(
  723. IN PVOID StringTable,
  724. IN LONG StringId,
  725. OUT PVOID ExtraData,
  726. IN UINT ExtraDataBufferSize
  727. )
  728. /*++
  729. Routine Description:
  730. Get arbitrary data associated with a string table entry.
  731. THIS ROUTINE DOES NOT DO LOCKING and IT DOES NOT HANDLE EXCEPTIONS!
  732. Arguments:
  733. StringTable - supplies handle to string table containing the string
  734. whose associated data is to be returned.
  735. String - supplies the id of the string whose associated data is to be returned.
  736. ExtraData - receives the data associated with the string. Data is truncated
  737. to fit, if necessary.
  738. ExtraDataBufferSize - supplies the size in bytes of the buffer specified
  739. by ExtraData. If this value is smaller than the extra data size for
  740. the string table, data is truncated to fit.
  741. Return Value:
  742. Boolean value indicating outcome.
  743. --*/
  744. {
  745. PSTRING_TABLE stringTable = StringTable;
  746. PSTRING_NODE stringNode;
  747. PVOID p;
  748. stringNode = (PSTRING_NODE)(stringTable->Data + StringId);
  749. p = stringNode->String + lstrlen(stringNode->String) + 1;
  750. CopyMemory(ExtraData,p,min(ExtraDataBufferSize,stringTable->ExtraDataSize));
  751. return(TRUE);
  752. }
  753. BOOL
  754. pSetupStringTableSetExtraData(
  755. IN PVOID StringTable,
  756. IN LONG StringId,
  757. IN PVOID ExtraData,
  758. IN UINT ExtraDataSize
  759. )
  760. /*++
  761. Routine Description:
  762. Associate arbitrary data with a string table entry.
  763. Arguments:
  764. StringTable - supplies handle to string table containing the string
  765. with which the data is to be associated.
  766. String - supplies the id of the string with which the data is to be associated.
  767. ExtraData - supplies the data to be associated with the string.
  768. ExtraDataSize - specifies the size in bytes of the data. If the data is
  769. larger than the extra data size for this string table, then the routine fails.
  770. Return Value:
  771. Boolean value indicating outcome.
  772. --*/
  773. {
  774. BOOL b = FALSE;
  775. BOOL locked = FALSE;
  776. try {
  777. if(!LockTable((PSTRING_TABLE)StringTable)) {
  778. leave;
  779. }
  780. locked = TRUE;
  781. b = _pSpUtilsStringTableSetExtraData(StringTable,StringId,ExtraData,ExtraDataSize);
  782. } except (EXCEPTION_EXECUTE_HANDLER) {
  783. b = FALSE;
  784. }
  785. if (locked) {
  786. UnlockTable((PSTRING_TABLE)StringTable);
  787. }
  788. return(b);
  789. }
  790. BOOL
  791. _pSpUtilsStringTableSetExtraData(
  792. IN PVOID StringTable,
  793. IN LONG StringId,
  794. IN PVOID ExtraData,
  795. IN UINT ExtraDataSize
  796. )
  797. /*++
  798. Routine Description:
  799. Associate arbitrary data with a string table entry.
  800. Arguments:
  801. StringTable - supplies handle to string table containing the string
  802. with which the data is to be associated.
  803. String - supplies the id of the string with which the data is to be associated.
  804. ExtraData - supplies the data to be associated with the string.
  805. ExtraDataSize - specifies the size in bytes of the data. If the data is
  806. larger than the extra data size for this string table, then the routine fails.
  807. Return Value:
  808. Boolean value indicating outcome.
  809. --*/
  810. {
  811. PSTRING_TABLE stringTable = StringTable;
  812. PSTRING_NODE stringNode;
  813. BOOL b;
  814. PVOID p;
  815. if(ExtraDataSize <= stringTable->ExtraDataSize) {
  816. stringNode = (PSTRING_NODE)(stringTable->Data + StringId);
  817. p = stringNode->String + lstrlen(stringNode->String) + 1;
  818. ZeroMemory(p,stringTable->ExtraDataSize);
  819. CopyMemory(p,ExtraData,ExtraDataSize);
  820. b = TRUE;
  821. } else {
  822. b = FALSE;
  823. }
  824. return(b);
  825. }
  826. LONG
  827. _pSpUtilsStringTableAddString(
  828. IN PVOID StringTable,
  829. IN OUT PTSTR String,
  830. IN DWORD Flags,
  831. IN PVOID ExtraData, OPTIONAL
  832. IN UINT ExtraDataSize OPTIONAL
  833. )
  834. /*++
  835. Routine Description:
  836. Adds a string to the string table if the string is not already
  837. in the string table. (Does not do locking!)
  838. If the string is to be added case-insensitively, then it is
  839. lower-cased, and added case-sensitively. Since lower-case characters
  840. are 'less than' lower case ones (according to lstrcmp), this ensures that
  841. a case-insensitive string will always appear in front of any of its
  842. case-sensitive counterparts. This ensures that we always find the correct
  843. string ID for things like section names.
  844. Arguments:
  845. StringTable - supplies handle to string table to be searched
  846. for the string
  847. String - supplies the string to be added
  848. Flags - supplies flags controlling how the string is to be added, and
  849. whether the caller-supplied buffer may be modified. May be a combination
  850. of the following values:
  851. STRTAB_CASE_INSENSITIVE - Add the string case-insensitively. The
  852. specified string will be added to the string
  853. table as all lower-case. This flag is overridden
  854. if STRTAB_CASE_SENSITIVE is specified.
  855. STRTAB_CASE_SENSITIVE - Add the string case-sensitively. This flag
  856. overrides the STRTAB_CASE_INSENSITIVE flag.
  857. STRTAB_BUFFER_WRITEABLE - The caller-supplied buffer may be written to during
  858. the string-addition process. Specifying this flag
  859. improves the performance of this API for case-
  860. insensitive string additions. This flag is ignored
  861. for case-sensitive string additions.
  862. STRTAB_NEW_EXTRADATA - if the string already exists in the table
  863. and ExtraData is specified (see below) then
  864. the new ExtraData overwrites any existing extra data.
  865. Otherwise any existing extra data is left alone.
  866. In addition to the above public flags, the following private flag is also
  867. allowed:
  868. STRTAB_ALREADY_LOWERCASE - The supplied string has already been converted to
  869. all lower-case (e.g., by calling CharLower), and
  870. therefore doesn't need to be lower-cased in the
  871. hashing routine. If this flag is supplied, then
  872. STRTAB_BUFFER_WRITEABLE is ignored, since modifying
  873. the caller's buffer is not required.
  874. ExtraData - if supplied, specifies extra data to be associated with the string
  875. in the string table. If the string already exists in the table, the Flags
  876. field controls whether the new data overwrites existing data already
  877. associated with the string.
  878. ExtraDataSize - if ExtraData is supplied, then this value supplies the size
  879. in bytes of the buffer pointed to by ExtraData. If the data is larger than
  880. the extra data size for the string table, the routine fails.
  881. Return Value:
  882. The return value uniquely identifes the string within the string table.
  883. It is -1 if the string was not in the string table but could not be added
  884. (out of memory).
  885. --*/
  886. {
  887. LONG rc;
  888. PSTRING_TABLE stringTable = StringTable;
  889. DWORD StringLength;
  890. DWORD HashValue;
  891. PSTRING_NODE PreviousNode,NewNode;
  892. DWORD SpaceRequired;
  893. PTSTR TempString = String;
  894. BOOL FreeTempString = FALSE;
  895. PVOID p;
  896. DWORD sz;
  897. if (!(Flags & STRTAB_CASE_SENSITIVE)) {
  898. //
  899. // not case sensitive ( = insensitive)
  900. //
  901. if (!(Flags & STRTAB_ALREADY_LOWERCASE)) {
  902. //
  903. // not already lowercase
  904. //
  905. if (!(Flags & STRTAB_BUFFER_WRITEABLE)) {
  906. //
  907. // not writable
  908. //
  909. //
  910. // Then the string is to be added case-insensitively, but the caller
  911. // doesn't want us to write to their buffer. Allocate one of our own.
  912. //
  913. if (TempString = pSetupDuplicateString(String)) {
  914. FreeTempString = TRUE;
  915. } else {
  916. //
  917. // We couldn't allocate space for our duplicated string. Since we'll
  918. // only consider exact matches (where the strings are all lower-case),
  919. // we're stuck, since we can't lower-case the buffer in place.
  920. //
  921. return -1;
  922. }
  923. }
  924. //
  925. // Lower-case the buffer.
  926. //
  927. #ifndef UNICODE
  928. _mbslwr (TempString);
  929. #else
  930. CharLower(TempString);
  931. #endif
  932. }
  933. //
  934. // we know that the string is now lower-case
  935. // we no longer need "Writable" flag
  936. // searches will be case sensitive
  937. //
  938. Flags &= ~ (STRTAB_BUFFER_WRITEABLE | STRTAB_CASE_INSENSITIVE);
  939. Flags |= STRTAB_CASE_SENSITIVE | STRTAB_ALREADY_LOWERCASE;
  940. }
  941. try {
  942. if (ExtraData && (ExtraDataSize > stringTable->ExtraDataSize)) {
  943. //
  944. // Force us into the exception handler -- sort of a non-local goto.
  945. //
  946. RaiseException(0,0,0,NULL);
  947. }
  948. //
  949. // The string might already be in there.
  950. //
  951. rc = _pSpUtilsStringTableLookUpString(
  952. StringTable,
  953. TempString,
  954. &StringLength,
  955. &HashValue,
  956. &PreviousNode,
  957. Flags,
  958. NULL,
  959. 0
  960. );
  961. if (rc != -1) {
  962. if (ExtraData && (Flags & STRTAB_NEW_EXTRADATA)) {
  963. //
  964. // Overwrite extra data. We know the data is small enough to fit
  965. // because we checked for this above.
  966. //
  967. p = PreviousNode->String + StringLength + 1;
  968. ZeroMemory(p,stringTable->ExtraDataSize);
  969. CopyMemory(p,ExtraData,ExtraDataSize);
  970. }
  971. if (FreeTempString) {
  972. pSetupFree(TempString);
  973. }
  974. return (rc);
  975. }
  976. //
  977. // Figure out how much space is required to hold this entry.
  978. // This is the size of a STRING_NODE plus the length of the string
  979. // plus space for extra per-element data.
  980. //
  981. SpaceRequired = offsetof(STRING_NODE,String)
  982. + ((StringLength+1)*sizeof(TCHAR))
  983. + stringTable->ExtraDataSize;
  984. //
  985. // Make sure things stay aligned within the table
  986. //
  987. if (SpaceRequired % sizeof(DWORD)) {
  988. SpaceRequired += sizeof(DWORD) - (SpaceRequired % sizeof(DWORD));
  989. }
  990. while(stringTable->DataSize + SpaceRequired > stringTable->BufferSize) {
  991. //
  992. // Grow the string table.
  993. // do this exponentially so that tables with a lot of items
  994. // added won't cause lots of reallocs
  995. //
  996. sz = STRING_TABLE_NEW_SIZE(stringTable->BufferSize);
  997. if (sz < stringTable->BufferSize) {
  998. sz = stringTable->DataSize + SpaceRequired;
  999. }
  1000. p = pSetupReallocWithTag(stringTable->Data,sz,MEMTAG_STRINGDATA);
  1001. if (!p) {
  1002. //
  1003. // we've run out of room, this could be because we asked
  1004. // for too big of a re-alloc
  1005. // if we're in this state, we're probably going to
  1006. // have problems later anyway, but for now, let's
  1007. // try and proceed with exactly what we need
  1008. //
  1009. sz = stringTable->DataSize + SpaceRequired;
  1010. p = pSetupReallocWithTag(stringTable->Data,sz,MEMTAG_STRINGDATA);
  1011. if (!p) {
  1012. //
  1013. // nope, this didn't help
  1014. //
  1015. if (FreeTempString) {
  1016. pSetupFree(TempString);
  1017. }
  1018. return (-1);
  1019. }
  1020. }
  1021. //
  1022. // Adjust previous node pointer.
  1023. //
  1024. if (PreviousNode) {
  1025. PreviousNode = (PSTRING_NODE)((PUCHAR)p + ((PUCHAR)PreviousNode-(PUCHAR)stringTable->Data));
  1026. }
  1027. stringTable->Data = p;
  1028. stringTable->BufferSize = sz;
  1029. }
  1030. //
  1031. // Stick the string and extra data, if any, in the string table buffer.
  1032. //
  1033. NewNode = (PSTRING_NODE)(stringTable->Data + stringTable->DataSize);
  1034. if (PreviousNode) {
  1035. NewNode->NextOffset = PreviousNode->NextOffset;
  1036. PreviousNode->NextOffset = (LONG)((LONG_PTR)NewNode - (LONG_PTR)stringTable->Data);
  1037. } else {
  1038. NewNode->NextOffset = ((PLONG)(stringTable->Data))[HashValue];
  1039. ((PLONG)(stringTable->Data))[HashValue] = (LONG)((LONG_PTR)NewNode - (LONG_PTR)stringTable->Data);
  1040. }
  1041. lstrcpy(NewNode->String,TempString);
  1042. p = NewNode->String + StringLength + 1;
  1043. ZeroMemory(p,stringTable->ExtraDataSize);
  1044. if (ExtraData) {
  1045. CopyMemory(p,ExtraData,ExtraDataSize);
  1046. }
  1047. stringTable->DataSize += SpaceRequired;
  1048. rc = (LONG)((LONG_PTR)NewNode - (LONG_PTR)stringTable->Data);
  1049. }except(EXCEPTION_EXECUTE_HANDLER) {
  1050. rc = -1;
  1051. }
  1052. if (FreeTempString) {
  1053. pSetupFree(TempString);
  1054. }
  1055. return rc;
  1056. }
  1057. LONG
  1058. pSetupStringTableAddString(
  1059. IN PVOID StringTable,
  1060. IN PTSTR String,
  1061. IN DWORD Flags
  1062. )
  1063. /*++
  1064. Routine Description:
  1065. Adds a string to the string table if the string is not already
  1066. in the string table.
  1067. Arguments:
  1068. StringTable - supplies handle to string table to be searched
  1069. for the string
  1070. String - supplies the string to be added
  1071. Flags - supplies flags controlling how the string is to be added, and
  1072. whether the caller-supplied buffer may be modified. May be a combination
  1073. of the following values:
  1074. STRTAB_CASE_INSENSITIVE - Add the string case-insensitively. The
  1075. specified string will be added to the string
  1076. table as all lower-case. This flag is overridden
  1077. if STRTAB_CASE_SENSITIVE is specified.
  1078. STRTAB_CASE_SENSITIVE - Add the string case-sensitively. This flag
  1079. overrides the STRTAB_CASE_INSENSITIVE flag.
  1080. STRTAB_BUFFER_WRITEABLE - The caller-supplied buffer may be written to during
  1081. the string-addition process. Specifying this flag
  1082. improves the performance of this API for case-
  1083. insensitive string additions. This flag is ignored
  1084. for case-sensitive string additions.
  1085. In addition to the above public flags, the following private flag is also
  1086. allowed:
  1087. STRTAB_ALREADY_LOWERCASE - The supplied string has already been converted to
  1088. all lower-case (e.g., by calling CharLower), and
  1089. therefore doesn't need to be lower-cased in the
  1090. hashing routine. If this flag is supplied, then
  1091. STRTAB_BUFFER_WRITEABLE is ignored, since modifying
  1092. the caller's buffer is not required.
  1093. Return Value:
  1094. The return value uniquely identifes the string within the string table.
  1095. It is -1 if the string was not in the string table but could not be added
  1096. (out of memory).
  1097. --*/
  1098. {
  1099. LONG rc = -1;
  1100. BOOL locked = FALSE;
  1101. DWORD PrivateFlags;
  1102. try {
  1103. if(!LockTable((PSTRING_TABLE)StringTable)) {
  1104. leave;
  1105. }
  1106. locked = TRUE;
  1107. PrivateFlags = _StringTableCheckFlags(Flags);
  1108. rc = _pSpUtilsStringTableAddString(StringTable, String, PrivateFlags, NULL, 0);
  1109. } except (EXCEPTION_EXECUTE_HANDLER) {
  1110. rc = -1;
  1111. }
  1112. if (locked) {
  1113. UnlockTable((PSTRING_TABLE)StringTable);
  1114. }
  1115. return(rc);
  1116. }
  1117. LONG
  1118. pSetupStringTableAddStringEx(
  1119. IN PVOID StringTable,
  1120. IN PTSTR String,
  1121. IN DWORD Flags,
  1122. IN PVOID ExtraData, OPTIONAL
  1123. IN UINT ExtraDataSize OPTIONAL
  1124. )
  1125. /*++
  1126. Routine Description:
  1127. Adds a string to the string table if the string is not already
  1128. in the string table.
  1129. Arguments:
  1130. StringTable - supplies handle to string table to be searched
  1131. for the string
  1132. String - supplies the string to be added
  1133. Flags - supplies flags controlling how the string is to be added, and
  1134. whether the caller-supplied buffer may be modified. May be a combination
  1135. of the following values:
  1136. STRTAB_CASE_INSENSITIVE - Add the string case-insensitively. The
  1137. specified string will be added to the string
  1138. table as all lower-case. This flag is overridden
  1139. if STRTAB_CASE_SENSITIVE is specified.
  1140. STRTAB_CASE_SENSITIVE - Add the string case-sensitively. This flag
  1141. overrides the STRTAB_CASE_INSENSITIVE flag.
  1142. STRTAB_BUFFER_WRITEABLE - The caller-supplied buffer may be written to during
  1143. the string-addition process. Specifying this flag
  1144. improves the performance of this API for case-
  1145. insensitive string additions. This flag is ignored
  1146. for case-sensitive string additions.
  1147. STRTAB_NEW_EXTRADATA - If the string already exists in the table
  1148. and ExtraData is specified (see below) then
  1149. the new ExtraData overwrites any existing extra data.
  1150. Otherwise any existing extra data is left alone.
  1151. In addition to the above public flags, the following private flag is also
  1152. allowed:
  1153. STRTAB_ALREADY_LOWERCASE - The supplied string has already been converted to
  1154. all lower-case (e.g., by calling CharLower), and
  1155. therefore doesn't need to be lower-cased in the
  1156. hashing routine. If this flag is supplied, then
  1157. STRTAB_BUFFER_WRITEABLE is ignored, since modifying
  1158. the caller's buffer is not required.
  1159. ExtraData - if supplied, specifies extra data to be associated with the string
  1160. in the string table. If the string already exists in the table, the Flags
  1161. field controls whether the new data overwrites existing data already
  1162. associated with the string.
  1163. ExtraDataSize - if ExtraData is supplied, then this value supplies the size
  1164. in bytes of the buffer pointed to by ExtraData. If the data is larger than
  1165. the extra data size for the string table, the routine fails.
  1166. Return Value:
  1167. The return value uniquely identifes the string within the string table.
  1168. It is -1 if the string was not in the string table but could not be added
  1169. (out of memory).
  1170. --*/
  1171. {
  1172. LONG rc = -1;
  1173. BOOL locked = FALSE;
  1174. DWORD PrivateFlags;
  1175. try {
  1176. if(!LockTable((PSTRING_TABLE)StringTable)) {
  1177. leave;
  1178. }
  1179. locked = TRUE;
  1180. PrivateFlags = _StringTableCheckFlags(Flags);
  1181. rc = _pSpUtilsStringTableAddString(StringTable, String, PrivateFlags, ExtraData, ExtraDataSize);
  1182. } except (EXCEPTION_EXECUTE_HANDLER) {
  1183. rc = -1;
  1184. }
  1185. if (locked) {
  1186. UnlockTable((PSTRING_TABLE)StringTable);
  1187. }
  1188. return (rc);
  1189. }
  1190. BOOL
  1191. pSetupStringTableEnum(
  1192. IN PVOID StringTable,
  1193. OUT PVOID ExtraDataBuffer, OPTIONAL
  1194. IN UINT ExtraDataBufferSize, OPTIONAL
  1195. IN PSTRTAB_ENUM_ROUTINE Callback,
  1196. IN LPARAM lParam OPTIONAL
  1197. )
  1198. /*++
  1199. Routine Description:
  1200. For every string in a string table, inform a callback routine of
  1201. the stirng's id, it's value, and any associated data.
  1202. Arguments:
  1203. StringTable - supplies a pointer to the string table to be enumerated.
  1204. ExtraDataBuffer - supplies the address of a buffer to be passed to
  1205. the callback routine for each string, which will be filled in
  1206. with the associated data of each string.
  1207. ExtraDataBufferSize - if ExtraDataBuffer is specified then this
  1208. supplies the size of that buffer in bytes. If this value is
  1209. smaller than the size of the extra data for the string table,
  1210. the enumeration fails.
  1211. Callback - supplies the routine to be notified of each string.
  1212. lParam - supplies an optional parameter meaningful to the caller
  1213. which is passed on to the callback unchanged.
  1214. Return Value:
  1215. Boolean value indicating outcome. TRUE unless ExtraDataBufferSize
  1216. is too small.
  1217. --*/
  1218. {
  1219. BOOL b = FALSE;
  1220. BOOL locked = FALSE;
  1221. try {
  1222. if(!LockTable((PSTRING_TABLE)StringTable)) {
  1223. leave;
  1224. }
  1225. locked = TRUE;
  1226. b = _pSpUtilsStringTableEnum(StringTable,ExtraDataBuffer,ExtraDataBufferSize,Callback,lParam);
  1227. } except (EXCEPTION_EXECUTE_HANDLER) {
  1228. b = FALSE;
  1229. }
  1230. if (locked) {
  1231. UnlockTable((PSTRING_TABLE)StringTable);
  1232. }
  1233. return(b);
  1234. }
  1235. BOOL
  1236. _pSpUtilsStringTableEnum(
  1237. IN PVOID StringTable,
  1238. OUT PVOID ExtraDataBuffer, OPTIONAL
  1239. IN UINT ExtraDataBufferSize, OPTIONAL
  1240. IN PSTRTAB_ENUM_ROUTINE Callback,
  1241. IN LPARAM lParam OPTIONAL
  1242. )
  1243. /*++
  1244. Routine Description:
  1245. For every string in a string table, inform a callback routine of
  1246. the stirng's id, its value, and any associated data.
  1247. THIS ROUTINE DOES NOT DO LOCKING.
  1248. Arguments:
  1249. StringTable - supplies a pointer to the string table to be enumerated.
  1250. ExtraDataBuffer - supplies the address of a buffer to be passed to
  1251. the callback routine for each string, which will be filled in
  1252. with the associated data of each string.
  1253. ExtraDataBufferSize - if ExtraDataBuffer is specified then this
  1254. supplies the size of that buffer in bytes. If this value is
  1255. smaller than the size of the extra data for the string table,
  1256. the enumeration fails.
  1257. Callback - supplies the routine to be notified of each string.
  1258. lParam - supplies an optional parameter meaningful to the caller
  1259. which is passed on to the callback unchanged.
  1260. Return Value:
  1261. Boolean value indicating outcome. TRUE unless ExtraDataBufferSize
  1262. is too small.
  1263. --*/
  1264. {
  1265. UINT u;
  1266. PSTRING_TABLE stringTable = StringTable;
  1267. PSTRING_NODE stringNode;
  1268. LONG FirstOffset;
  1269. BOOL b;
  1270. //
  1271. // Validate buffer size.
  1272. //
  1273. if(ExtraDataBuffer && (ExtraDataBufferSize < stringTable->ExtraDataSize)) {
  1274. return(FALSE);
  1275. }
  1276. for(b=TRUE,u=0; b && (u<HASH_BUCKET_COUNT); u++) {
  1277. FirstOffset = ((PLONG)stringTable->Data)[u];
  1278. if(FirstOffset == -1) {
  1279. continue;
  1280. }
  1281. stringNode = (PSTRING_NODE)(stringTable->Data + FirstOffset);
  1282. do {
  1283. if(ExtraDataBuffer) {
  1284. CopyMemory(
  1285. ExtraDataBuffer,
  1286. stringNode->String + lstrlen(stringNode->String) + 1,
  1287. stringTable->ExtraDataSize
  1288. );
  1289. }
  1290. b = Callback(
  1291. StringTable,
  1292. (LONG)((PUCHAR)stringNode - stringTable->Data),
  1293. stringNode->String,
  1294. ExtraDataBuffer,
  1295. ExtraDataBuffer ? stringTable->ExtraDataSize : 0,
  1296. lParam
  1297. );
  1298. stringNode = (stringNode->NextOffset == -1)
  1299. ? NULL
  1300. : (PSTRING_NODE)(stringTable->Data + stringNode->NextOffset);
  1301. } while(b && stringNode);
  1302. }
  1303. return(TRUE);
  1304. }
  1305. PTSTR
  1306. _pSpUtilsStringTableStringFromId(
  1307. IN PVOID StringTable,
  1308. IN LONG StringId
  1309. )
  1310. /*++
  1311. Routine Description:
  1312. Given a string ID returned when a string was added or looked up,
  1313. return a pointer to the actual string. (This is exactly the same
  1314. as pSetupStringTableStringFromId, except that it doesn't do locking.)
  1315. Arguments:
  1316. StringTable - supplies a pointer to the string table containing the
  1317. string to be retrieved.
  1318. StringId - supplies a string id returned from pSetupStringTableAddString
  1319. or pSetupStringTableLookUpString.
  1320. Return Value:
  1321. Pointer to string data. The caller must not write into or otherwise
  1322. alter the string.
  1323. --*/
  1324. {
  1325. return ((PSTRING_NODE)(((PSTRING_TABLE)StringTable)->Data + StringId))->String;
  1326. }
  1327. PTSTR
  1328. pSetupStringTableStringFromId(
  1329. IN PVOID StringTable,
  1330. IN LONG StringId
  1331. )
  1332. /*++
  1333. Routine Description:
  1334. Given a string ID returned when a string was added or looked up,
  1335. return a pointer to the actual string.
  1336. Arguments:
  1337. StringTable - supplies a pointer to the string table containing the
  1338. string to be retrieved.
  1339. StringId - supplies a string id returned from pSetupStringTableAddString
  1340. or pSetupStringTableLookUpString.
  1341. Return Value:
  1342. Pointer to string data. The caller must not write into or otherwise
  1343. alter the string.
  1344. This function is fundamentally not thread-safe
  1345. since the ptr we return could be modified by another thread
  1346. if string table is accessed by more than one thread
  1347. Hence new API below pSetupStringTableStringFromIdEx
  1348. --*/
  1349. {
  1350. PTSTR p = NULL;
  1351. BOOL locked = FALSE;
  1352. try {
  1353. if(!LockTable((PSTRING_TABLE)StringTable)) {
  1354. leave;
  1355. }
  1356. locked = TRUE;
  1357. p = ((PSTRING_NODE)(((PSTRING_TABLE)StringTable)->Data + StringId))->String;
  1358. } except (EXCEPTION_EXECUTE_HANDLER) {
  1359. p = NULL;
  1360. //
  1361. // Reference the following variable so the compiler will respect
  1362. // statement ordering w.r.t. its assignment.
  1363. //
  1364. locked = locked;
  1365. }
  1366. if (locked) {
  1367. UnlockTable((PSTRING_TABLE)StringTable);
  1368. }
  1369. return(p);
  1370. }
  1371. BOOL
  1372. pSetupStringTableStringFromIdEx(
  1373. IN PVOID StringTable,
  1374. IN LONG StringId,
  1375. IN OUT PTSTR pBuffer,
  1376. IN OUT PULONG pBufSize
  1377. )
  1378. /*++
  1379. Routine Description:
  1380. Given a string ID returned when a string was added or looked up,
  1381. return a pointer to the actual string.
  1382. Arguments:
  1383. StringTable - supplies a pointer to the string table containing the
  1384. string to be retrieved.
  1385. StringId - supplies a string id returned from pSetupStringTableAddString
  1386. or pSetupStringTableLookUpString.
  1387. pBuffer - points to a buffer that will be filled out with the string
  1388. to be retrieved
  1389. pBufSize - supplies a pointer to an input/output parameter that contains
  1390. the size of the buffer on entry, and the number of chars. written on
  1391. exit.
  1392. Return Value:
  1393. TRUE if the string was written. pBufSize contains the length of the string
  1394. FALSE if the buffer was invalid, or the string ID was invalid, or the buffer
  1395. wasn't big enough. If pBufSize non-zero, then it is the required bufsize.
  1396. currently caller can tell the difference between invalid param and buffer
  1397. size by checking pBufSize
  1398. --*/
  1399. {
  1400. PTSTR p;
  1401. ULONG len;
  1402. PSTRING_TABLE stringTable = (PSTRING_TABLE)StringTable;
  1403. DWORD status = ERROR_INVALID_DATA;
  1404. BOOL locked = FALSE;
  1405. try {
  1406. if (!pBufSize) {
  1407. status = ERROR_INVALID_PARAMETER;
  1408. leave;
  1409. }
  1410. if(!LockTable(stringTable)) {
  1411. if (pBuffer != NULL && *pBufSize > 0) {
  1412. pBuffer[0]=0;
  1413. }
  1414. *pBufSize = 0;
  1415. status = ERROR_INVALID_HANDLE;
  1416. leave;
  1417. }
  1418. locked = TRUE;
  1419. //
  1420. // CFGMGR calls this with an ID passed by it's caller
  1421. // we have to check bounds here (while table is locked)
  1422. // the check has to do:
  1423. //
  1424. // (1) StringId must be > 0 (0 is hash-bucket 0)
  1425. // (2) StringId must be < size of string table
  1426. // This should catch common errors but isn't perfect.
  1427. //
  1428. // the check is here since Id validity requires access to Opaque pointer
  1429. //
  1430. if(StringId <= 0 || StringId >= (LONG)(stringTable->DataSize)) {
  1431. if (pBuffer != NULL && *pBufSize > 0) {
  1432. pBuffer[0]=0;
  1433. }
  1434. *pBufSize = 0;
  1435. status = ERROR_INVALID_PARAMETER;
  1436. leave;
  1437. }
  1438. len = lstrlen( ((PSTRING_NODE)(stringTable->Data + StringId))->String);
  1439. len ++; // account for terminating NULL
  1440. if (len > *pBufSize || pBuffer == NULL) {
  1441. if (pBuffer != NULL && *pBufSize > 0) {
  1442. pBuffer[0]=0;
  1443. }
  1444. *pBufSize = len;
  1445. status = ERROR_INSUFFICIENT_BUFFER;
  1446. leave;
  1447. }
  1448. lstrcpy (pBuffer,((PSTRING_NODE)(stringTable->Data + StringId))->String);
  1449. *pBufSize = len;
  1450. status = NO_ERROR;
  1451. } except (EXCEPTION_EXECUTE_HANDLER) {
  1452. status = ERROR_INVALID_DATA;
  1453. }
  1454. if (locked) {
  1455. UnlockTable((PSTRING_TABLE)StringTable);
  1456. }
  1457. if(status == NO_ERROR) {
  1458. //
  1459. // if success, return TRUE without modifying error code
  1460. //
  1461. return TRUE;
  1462. }
  1463. //
  1464. // if error, we may be interested in cause
  1465. //
  1466. // SetLastError(status); // left disabled till I know this is safe to do
  1467. return FALSE;
  1468. }
  1469. VOID
  1470. _pSpUtilsStringTableTrim(
  1471. IN PVOID StringTable
  1472. )
  1473. /*++
  1474. Routine Description:
  1475. Free any memory currently allocated for the string table
  1476. but not currently used.
  1477. This is useful after all strings have been added to a string table
  1478. because the string table grows by a fixed block size as it's being built.
  1479. THIS ROUTINE DOES NOT DO LOCKING!
  1480. Arguments:
  1481. StringTable - supplies a string table handle returned from
  1482. a call to pSetupStringTableInitialize().
  1483. Return Value:
  1484. None.
  1485. --*/
  1486. {
  1487. PSTRING_TABLE stringTable = StringTable;
  1488. PVOID p;
  1489. //
  1490. // If the realloc failed the original block is not freed,
  1491. // so we don't really care.
  1492. //
  1493. if(p = pSetupReallocWithTag(stringTable->Data, stringTable->DataSize, MEMTAG_STRINGDATA)) {
  1494. stringTable->Data = p;
  1495. stringTable->BufferSize = stringTable->DataSize;
  1496. }
  1497. }
  1498. PVOID
  1499. pSetupStringTableInitialize(
  1500. VOID
  1501. )
  1502. /*++
  1503. Routine Description:
  1504. Create and initialize a string table.
  1505. Arguments:
  1506. None.
  1507. Return Value:
  1508. NULL if the string table could not be created (out of memory).
  1509. Otherwise returns an opaque value that references the string
  1510. table in other StringTable calls.
  1511. Remarks:
  1512. This routine returns a string table with synchronization locks
  1513. required by all public StringTable APIs. If the string table
  1514. is to be enclosed in a structure that has its own locking
  1515. (e.g., HINF, HDEVINFO), then the private version of this API
  1516. may be called, which will not create locks for the string table.
  1517. --*/
  1518. {
  1519. PSTRING_TABLE StringTable;
  1520. if(StringTable = (PSTRING_TABLE)_pSpUtilsStringTableInitialize(0)) {
  1521. if(InitializeSynchronizedAccess(&StringTable->Lock)) {
  1522. return StringTable;
  1523. }
  1524. _pSpUtilsStringTableDestroy(StringTable);
  1525. }
  1526. return NULL;
  1527. }
  1528. PVOID
  1529. pSetupStringTableInitializeEx(
  1530. IN UINT ExtraDataSize, OPTIONAL
  1531. IN UINT Reserved
  1532. )
  1533. /*++
  1534. Routine Description:
  1535. Create and initialize a string table, where each string can have
  1536. some arbitrary data associated with it.
  1537. Arguments:
  1538. ExtraDataSize - supplies maximum size of arbitrary data that can be
  1539. associated with strings in the string table that will be created.
  1540. Reserved - unused, must be 0.
  1541. Return Value:
  1542. NULL if the string table could not be created (out of memory).
  1543. Otherwise returns an opaque value that references the string
  1544. table in other StringTable calls.
  1545. Remarks:
  1546. This routine returns a string table with synchronization locks
  1547. required by all public StringTable APIs. If the string table
  1548. is to be enclosed in a structure that has its own locking
  1549. (e.g., HINF, HDEVINFO), then the private version of this API
  1550. may be called, which will not create locks for the string table.
  1551. --*/
  1552. {
  1553. PSTRING_TABLE StringTable;
  1554. if(Reserved) {
  1555. return(NULL);
  1556. }
  1557. if(StringTable = (PSTRING_TABLE)_pSpUtilsStringTableInitialize(ExtraDataSize)) {
  1558. if(InitializeSynchronizedAccess(&StringTable->Lock)) {
  1559. return StringTable;
  1560. }
  1561. _pSpUtilsStringTableDestroy(StringTable);
  1562. }
  1563. return NULL;
  1564. }
  1565. PVOID
  1566. _pSpUtilsStringTableInitialize(
  1567. IN UINT ExtraDataSize OPTIONAL
  1568. )
  1569. /*++
  1570. Routine Description:
  1571. Create and initialize a string table. Each string can optionally have
  1572. some arbitrary data associated with it.
  1573. THIS ROUTINE DOES NOT INITIALIZE STRING TABLE SYNCHRONIZATION LOCKS!
  1574. Arguments:
  1575. ExtraDataSize - supplies maximum size of arbitrary data that can be
  1576. associated with strings in the string table that will be created.
  1577. Return Value:
  1578. NULL if the string table could not be created (out of memory).
  1579. Otherwise returns an opaque value that references the string
  1580. table in other StringTable calls.
  1581. Remarks:
  1582. The string table returned from this API may not be used as-is with the
  1583. public StringTable APIs--it must have its synchronization locks initialized
  1584. by the public form of this API.
  1585. --*/
  1586. {
  1587. UINT u;
  1588. PSTRING_TABLE stringTable;
  1589. LCID locale;
  1590. //
  1591. // Allocate a string table
  1592. //
  1593. if(stringTable = pSetupMallocWithTag(sizeof(STRING_TABLE),MEMTAG_STRINGTABLE)) {
  1594. ZeroMemory(stringTable,sizeof(STRING_TABLE));
  1595. stringTable->ExtraDataSize = ExtraDataSize;
  1596. locale = GetThreadLocale();
  1597. //
  1598. // changes here may need to be reflected in _pSpUtilsStringTableInitializeFromMemoryMappedFile
  1599. //
  1600. if(PRIMARYLANGID(LANGIDFROMLCID(locale)) == LANG_TURKISH) {
  1601. //
  1602. // Turkish has a problem with i and dotted i's.
  1603. // Do comparison in English (default sort)
  1604. //
  1605. stringTable->Locale = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT);
  1606. } else {
  1607. //
  1608. // string tables always use default sorting algorithm
  1609. //
  1610. stringTable->Locale = MAKELCID(LANGIDFROMLCID(locale),SORT_DEFAULT);
  1611. }
  1612. //
  1613. // Allocate space for the string table data.
  1614. //
  1615. if(stringTable->Data = pSetupMallocWithTag(STRING_TABLE_INITIAL_SIZE,MEMTAG_STRINGDATA)) {
  1616. stringTable->BufferSize = STRING_TABLE_INITIAL_SIZE;
  1617. //
  1618. // Initialize the hash table
  1619. //
  1620. for(u=0; u<HASH_BUCKET_COUNT; u++) {
  1621. ((PLONG)(stringTable->Data))[u] = -1;
  1622. }
  1623. //
  1624. // Set the DataSize to the size of the StringNodeOffset list, so
  1625. // we'll start adding new strings after it.
  1626. //
  1627. stringTable->DataSize = HASH_BUCKET_COUNT * sizeof(LONG);
  1628. return(stringTable);
  1629. }
  1630. pSetupFreeWithTag(stringTable,MEMTAG_STRINGTABLE);
  1631. }
  1632. return(NULL);
  1633. }
  1634. VOID
  1635. pSetupStringTableDestroy(
  1636. IN PVOID StringTable
  1637. )
  1638. /*++
  1639. Routine Description:
  1640. Destroy a string table, freeing all resources it uses.
  1641. Arguments:
  1642. StringTable - supplies a string table handle returned from
  1643. a call to pSetupStringTableInitialize().
  1644. Return Value:
  1645. None.
  1646. --*/
  1647. {
  1648. try {
  1649. if(!LockTable((PSTRING_TABLE)StringTable)) {
  1650. leave;
  1651. }
  1652. DestroySynchronizedAccess(&(((PSTRING_TABLE)StringTable)->Lock));
  1653. _pSpUtilsStringTableDestroy(StringTable);
  1654. } except (EXCEPTION_EXECUTE_HANDLER) {
  1655. //
  1656. }
  1657. }
  1658. VOID
  1659. _pSpUtilsStringTableDestroy(
  1660. IN PVOID StringTable
  1661. )
  1662. /*++
  1663. Routine Description:
  1664. Destroy a string table, freeing all resources it uses.
  1665. THIS ROUTINE DOES NOT DO LOCKING!
  1666. Arguments:
  1667. StringTable - supplies a string table handle returned from
  1668. a call to pSetupStringTableInitialize() or _pSpUtilsStringTableInitializeFromMemoryMappedFile
  1669. Return Value:
  1670. None.
  1671. --*/
  1672. {
  1673. if (((PSTRING_TABLE)StringTable)->BufferSize) {
  1674. pSetupFreeWithTag(((PSTRING_TABLE)StringTable)->Data,MEMTAG_STRINGDATA);
  1675. pSetupFreeWithTag(StringTable,MEMTAG_STRINGTABLE);
  1676. } else {
  1677. pSetupFreeWithTag(StringTable,MEMTAG_STATICSTRINGTABLE);
  1678. }
  1679. }
  1680. PVOID
  1681. pSetupStringTableDuplicate(
  1682. IN PVOID StringTable
  1683. )
  1684. /*++
  1685. Routine Description:
  1686. Create an independent duplicate of a string table.
  1687. Arguments:
  1688. StringTable - supplies a string table handle of string table to duplicate.
  1689. Return Value:
  1690. Handle for new string table, NULL if out of memory.
  1691. --*/
  1692. {
  1693. PSTRING_TABLE New = NULL;
  1694. BOOL locked = FALSE;
  1695. try {
  1696. if(!LockTable((PSTRING_TABLE)StringTable)) {
  1697. leave;
  1698. }
  1699. locked = TRUE;
  1700. if(New = (PSTRING_TABLE)_pSpUtilsStringTableDuplicate(StringTable)) {
  1701. if(!InitializeSynchronizedAccess(&New->Lock)) {
  1702. _pSpUtilsStringTableDestroy(New);
  1703. New = NULL;
  1704. }
  1705. }
  1706. } except (EXCEPTION_EXECUTE_HANDLER) {
  1707. New = NULL;
  1708. }
  1709. if (locked) {
  1710. UnlockTable((PSTRING_TABLE)StringTable);
  1711. }
  1712. return New;
  1713. }
  1714. PVOID
  1715. _pSpUtilsStringTableDuplicate(
  1716. IN PVOID StringTable
  1717. )
  1718. /*++
  1719. Routine Description:
  1720. Create an independent duplicate of a string table.
  1721. THIS ROUTINE DOES NOT DO LOCKING!
  1722. Arguments:
  1723. StringTable - supplies a string table handle of string table to duplicate.
  1724. Return Value:
  1725. Handle for new string table, NULL if out of memory or buffer copy failure.
  1726. Remarks:
  1727. This routine does not initialize synchronization locks for the duplicate--these
  1728. fields are initialized to NULL.
  1729. --*/
  1730. {
  1731. PSTRING_TABLE New;
  1732. PSTRING_TABLE stringTable = StringTable;
  1733. BOOL Success;
  1734. if(New = pSetupMallocWithTag(sizeof(STRING_TABLE),MEMTAG_STRINGTABLE)) {
  1735. CopyMemory(New,StringTable,sizeof(STRING_TABLE));
  1736. //
  1737. // Allocate space for the string table data.
  1738. //
  1739. if(New->Data = pSetupMallocWithTag(stringTable->DataSize,MEMTAG_STRINGDATA)) {
  1740. //
  1741. // Surround memory copy in try/except, since we may be dealing with
  1742. // a string table contained in a PNF, in which case the buffer is
  1743. // in a memory-mapped file.
  1744. //
  1745. Success = TRUE; // assume success unless we get an inpage error...
  1746. try {
  1747. CopyMemory(New->Data, stringTable->Data, stringTable->DataSize);
  1748. } except(EXCEPTION_EXECUTE_HANDLER) {
  1749. Success = FALSE;
  1750. }
  1751. if(Success) {
  1752. New->BufferSize = New->DataSize;
  1753. ZeroMemory(&New->Lock, sizeof(MYLOCK));
  1754. return New;
  1755. }
  1756. pSetupFreeWithTag(New->Data,MEMTAG_STRINGDATA);
  1757. }
  1758. pSetupFreeWithTag(New,MEMTAG_STRINGTABLE);
  1759. }
  1760. return NULL;
  1761. }
  1762. PVOID
  1763. _pSpUtilsStringTableInitializeFromMemoryMappedFile(
  1764. IN PVOID DataBlock,
  1765. IN DWORD DataBlockSize,
  1766. IN LCID Locale,
  1767. IN UINT ExtraDataSize
  1768. )
  1769. {
  1770. PSTRING_TABLE StringTable;
  1771. BOOL WasLoaded = TRUE;
  1772. //
  1773. // Allocate a string table
  1774. //
  1775. if(!(StringTable = pSetupMallocWithTag(sizeof(STRING_TABLE),MEMTAG_STATICSTRINGTABLE))) {
  1776. return NULL;
  1777. }
  1778. try {
  1779. StringTable->Data = (PUCHAR)DataBlock;
  1780. StringTable->DataSize = DataBlockSize;
  1781. StringTable->BufferSize = 0; // no allocated buffer
  1782. //
  1783. // Clear the Lock structure, because mem-mapped string tables can only be accessed
  1784. // internally
  1785. //
  1786. StringTable->Lock.Handles[0] = StringTable->Lock.Handles[1] = NULL;
  1787. StringTable->ExtraDataSize = ExtraDataSize;
  1788. if(PRIMARYLANGID(LANGIDFROMLCID(Locale)) == LANG_TURKISH) {
  1789. //
  1790. // Turkish has a problem with i and dotted i's.
  1791. // Do comparison in English.
  1792. //
  1793. StringTable->Locale = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT);
  1794. } else {
  1795. StringTable->Locale = MAKELCID(LANGIDFROMLCID(Locale),SORT_DEFAULT);
  1796. }
  1797. } except(EXCEPTION_EXECUTE_HANDLER) {
  1798. WasLoaded = FALSE;
  1799. }
  1800. if(WasLoaded) {
  1801. return StringTable;
  1802. } else {
  1803. pSetupFreeWithTag(StringTable,MEMTAG_STATICSTRINGTABLE);
  1804. return NULL;
  1805. }
  1806. }
  1807. DWORD
  1808. _pSpUtilsStringTableGetDataBlock(
  1809. IN PVOID StringTable,
  1810. OUT PVOID *StringTableBlock
  1811. )
  1812. {
  1813. *StringTableBlock = (PVOID)(((PSTRING_TABLE)StringTable)->Data);
  1814. return ((PSTRING_TABLE)StringTable)->DataSize;
  1815. }