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.

4538 lines
126 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. report.c
  5. Abstract:
  6. This module contains routines that prepare the text that goes
  7. in the incompatibility report. The text is displayed as details.
  8. Author:
  9. Jim Schmidt (jimschm) 28-Oct-1997
  10. Revision History:
  11. ovidiut 27-Sep-2000 Added Level member to GENERIC_LIST struct
  12. ovidiut 20-May-1999 Added Flags member to GENERIC_LIST struct
  13. jimschm 23-Sep-1998 TWAIN group
  14. jimschm 02-Mar-1998 Added Auto Uninstall group
  15. jimschm 12-Jan-1998 Reorganized to force messages into a defined message group
  16. --*/
  17. #include "pch.h"
  18. #include "uip.h"
  19. #define DBG_REPORT "ReportMsg"
  20. #define MAX_SPACES 32
  21. #define MAX_MSGGROUP 2048
  22. //
  23. // ROOT_MSGGROUPS lists all well-defined message groups. These are the only groups
  24. // that can appear as a root in the report. Root groups are formatted so their name
  25. // appears with a horizontal line directly under it. Subgroups are then listed for
  26. // the group.
  27. //
  28. // For example, Installation Notes is a root-level group. There are several subgroups
  29. // of Installation Notes, and each subgroup might be formatted differently. Some may
  30. // list several items, while others might have indented detailed descriptions.
  31. //
  32. // Each group can be split into zero or more defined subgroups, and zero or more
  33. // undefined subgroups. A defined subgroup is one that either has a handler declared
  34. // below in SUBGROUP_HANDLERS, or it is a list, defined in SUBGROUP_LISTS below.
  35. // If a group is split this way, then there can be introduction text placed above
  36. // all defined subgroups, and there can also be introduction text placed above
  37. // all undefined subgroups.
  38. //
  39. // For example, the Incompatible Hardware category might be split as follows:
  40. //
  41. // Incompatible Hardware (A)
  42. // ---------------------
  43. // The following hardware is bad: (B)
  44. // a (S)
  45. // b (S)
  46. // c (S)
  47. //
  48. // Contact the manufacturer. (B)
  49. //
  50. // The following hardware has limited functionality: (A)
  51. //
  52. // foo video (S)
  53. // This device does not have 1280x1024 mode on NT. (M)
  54. //
  55. // In the example above, the text "Incompatible Hardware" and the underline would
  56. // come from the group's intro text. The text "The following hardware is bad"
  57. // comes from the hardware list subgroup intro. Then a, b and c come from the
  58. // subgroup's subgroup. "Contact the manuafacturer" comes from the subgroup's conclusion
  59. // text. Then "The following hardware" text comes from the group's other intro. And
  60. // finally the remaining text comes from undefined subgroups (foo video in this case).
  61. //
  62. // NOTES:
  63. // (A) indicates the message was specified in the ROOT_MSGGROUPS macro
  64. // (B) indicates the message was specified in the SUBGROUPS_LIST macro
  65. // (S) indicates the text was specified as the last subgroup in a MsgMgr call
  66. // (M) indicates the message was specified in a MsgMgr call
  67. //
  68. // DO NOT change ROOT_MSGGROUPS unless you know what you are doing.
  69. //
  70. // Syntax: (one of the following)
  71. //
  72. // NO_TEXT(<msgid>) - Group has no intro text
  73. // HAS_INTRO(<msgid>) - Group has intro text
  74. // HAS_OTHER(<msgid>) - Group has intro text for undefined subgroups
  75. // HAS_BOTH(<msgid) - Group has both types of intro text
  76. //
  77. // The following MSG_* strings are required to be defined in msg.mc:
  78. //
  79. // NO_TEXT - <msgid>_ROOT
  80. // HAS_INTRO - <msgid>_ROOT, <msgid>_INTRO, <msgid>_INTRO_HTML
  81. // HAS_OTHER - <msgid>_ROOT, <msgid>_OTHER, <msgid>_OTHER_HTML
  82. // HAS_BOTH - <msgid>_ROOT, <msgid>_INTRO, <msgid>_INTRO_HTML, <msgid>_OTHER, <msgid>_OTHER_HTML
  83. //
  84. //
  85. // REMEMBER: _ROOT, _INTRO, _INTRO_HTML, _OTHER and _OTHER_HTML are appended to
  86. // the constants in ROOT_MSGGROUPS, as described above.
  87. //
  88. #if 0
  89. HAS_INTRO(MSG_MUST_UNINSTALL, REPORTLEVEL_BLOCKING) \
  90. NO_TEXT(MSG_REINSTALL_BLOCK, REPORTLEVEL_BLOCKING) \
  91. #endif
  92. #define ROOT_MSGGROUPS \
  93. HAS_OTHER(MSG_BLOCKING_ITEMS, REPORTLEVEL_BLOCKING) \
  94. HAS_BOTH(MSG_INCOMPATIBLE_HARDWARE, REPORTLEVEL_ERROR) \
  95. HAS_INTRO(MSG_INCOMPATIBLE, REPORTLEVEL_WARNING) \
  96. HAS_INTRO(MSG_REINSTALL, REPORTLEVEL_WARNING) \
  97. HAS_INTRO(MSG_LOSTSETTINGS, REPORTLEVEL_WARNING) \
  98. HAS_INTRO(MSG_MISC_WARNINGS, REPORTLEVEL_WARNING) \
  99. HAS_INTRO(MSG_MINOR_PROBLEM, REPORTLEVEL_VERBOSE) \
  100. HAS_INTRO(MSG_DOS_DESIGNED, REPORTLEVEL_VERBOSE) \
  101. HAS_INTRO(MSG_INSTALL_NOTES, REPORTLEVEL_VERBOSE) \
  102. HAS_INTRO(MSG_MIGDLL, REPORTLEVEL_VERBOSE) \
  103. NO_TEXT(MSG_UNKNOWN, REPORTLEVEL_VERBOSE) \
  104. //
  105. // SUBGROUP_HANDLERS declares special formatting handlers, which format text of subgroup
  106. // messages. There are two common default handlers that are used by most subgroups --
  107. // the generic list handler and the default message handler. If your subgroup requires
  108. // unique message formatting, define your handler in SUBGROUP_HANDLERS.
  109. //
  110. // Syntax:
  111. //
  112. // DECLARE(<groupid>, fn, <DWORD arg>)
  113. //
  114. // groupid specifies the exact text used for display and can be either a group or
  115. // a subgroup of one of the groups defined above in ROOT_MSGGROUPS. The report
  116. // generation code will search each segment of the group name for the specified string.
  117. //
  118. // For example, MSG_NAMECHANGE_WARNING_GROUP is "Names That Will Change" and is in the
  119. // Installation Notes group. By specifying this string ID, and a handler function,
  120. // the handler is called all name change messages, and the report generation code
  121. // will process the name change messages during the formatting of Installation Notes.
  122. //
  123. #define SUBGROUP_HANDLERS \
  124. DECLARE(MSG_UNSUPPORTED_HARDWARE_PNP_SUBGROUP, pAddPnpHardwareToReport, 0) \
  125. DECLARE(MSG_INCOMPATIBLE_HARDWARE_PNP_SUBGROUP, pAddPnpHardwareToReport, 1) \
  126. DECLARE(MSG_REINSTALL_HARDWARE_PNP_SUBGROUP, pAddPnpHardwareToReport, 2) \
  127. DECLARE(0, pDefaultHandler, 0)
  128. //
  129. // *This is where changes are commonly made*
  130. //
  131. // SUBGROUP_LISTS define the string IDs for the intro text and summary text for
  132. // the message subgroup (text not needed for root groups). This step puts your subgroup
  133. // in the correct root-level group.
  134. //
  135. // If you have a simple list, you only need to add an entry to this macro expansion
  136. // list, then you're done. The generic list handler will be processed for your
  137. // subgroup.
  138. //
  139. // NOTE: You must put all info in the group when calling message manager APIs. That
  140. // is, the group should be formatted as <group>\<subgroup>\<list-item>.
  141. //
  142. // Syntax:
  143. //
  144. // DECLARE(<rootid>, <toptext>, <toptext-html>, <bottomtext>, <bottomtext-html>, <formatargs>, <flags>, <level>)
  145. //
  146. // Specify zero for no message. If you specify zero for plain text, you must
  147. // also specify zero for the html text.
  148. //
  149. // Use flags to specify additional features, like RF_BOLDITEMS to display items in
  150. // bold case.
  151. //
  152. #define SUBGROUP_LISTS \
  153. DECLARE( \
  154. MSG_BLOCKING_HARDWARE_SUBGROUP, \
  155. MSG_BLOCKING_HARDWARE_INTRO, \
  156. MSG_BLOCKING_HARDWARE_INTRO_HTML, \
  157. MSG_UNINDENT, \
  158. MSG_UNINDENT_HTML, \
  159. NULL, \
  160. RF_BOLDITEMS | RF_ENABLEMSG | RF_USEROOT, \
  161. REPORTLEVEL_BLOCKING \
  162. ) \
  163. DECLARE( \
  164. MSG_MUST_UNINSTALL_ROOT, \
  165. MSG_MUST_UNINSTALL_INTRO, \
  166. MSG_MUST_UNINSTALL_INTRO_HTML, \
  167. MSG_UNINDENT, \
  168. MSG_UNINDENT_HTML, \
  169. NULL, \
  170. RF_BOLDITEMS | RF_ENABLEMSG | RF_USEROOT, \
  171. REPORTLEVEL_BLOCKING \
  172. ) \
  173. DECLARE( \
  174. MSG_REINSTALL_BLOCK_ROOT, \
  175. MSG_REPORT_REINSTALL_BLOCK_INSTRUCTIONS, \
  176. MSG_REPORT_REINSTALL_BLOCK_INSTRUCTIONS_HTML, \
  177. MSG_UNINDENT, \
  178. MSG_UNINDENT_HTML, \
  179. NULL, \
  180. RF_BOLDITEMS | RF_ENABLEMSG | RF_USEROOT, \
  181. REPORTLEVEL_BLOCKING \
  182. ) \
  183. DECLARE( \
  184. MSG_INCOMPATIBLE_DETAIL_SUBGROUP, \
  185. MSG_REPORT_BEGIN_INDENT, \
  186. MSG_REPORT_BEGIN_INDENT_HTML, \
  187. MSG_UNINDENT, \
  188. MSG_UNINDENT_HTML, \
  189. NULL, \
  190. RF_BOLDITEMS | RF_ENABLEMSG | RF_USEROOT, \
  191. REPORTLEVEL_WARNING \
  192. ) \
  193. DECLARE( \
  194. MSG_REINSTALL_DETAIL_SUBGROUP, \
  195. MSG_REPORT_BEGIN_INDENT, \
  196. MSG_REPORT_BEGIN_INDENT_HTML, \
  197. MSG_UNINDENT, \
  198. MSG_UNINDENT_HTML, \
  199. NULL, \
  200. RF_BOLDITEMS | RF_ENABLEMSG | RF_USEROOT, \
  201. REPORTLEVEL_WARNING \
  202. ) \
  203. DECLARE( \
  204. MSG_REINSTALL_LIST_SUBGROUP, \
  205. MSG_REPORT_BEGIN_INDENT, \
  206. MSG_REPORT_BEGIN_INDENT_HTML, \
  207. MSG_UNINDENT, \
  208. MSG_UNINDENT_HTML, \
  209. NULL, \
  210. RF_BOLDITEMS | RF_USEROOT, \
  211. REPORTLEVEL_WARNING \
  212. ) \
  213. DECLARE( \
  214. MSG_UNKNOWN_ROOT, \
  215. MSG_REPORT_UNKNOWN_INSTRUCTIONS, \
  216. MSG_REPORT_UNKNOWN_INSTRUCTIONS_HTML, \
  217. 0, \
  218. 0, \
  219. NULL, \
  220. RF_BOLDITEMS, \
  221. REPORTLEVEL_VERBOSE \
  222. ) \
  223. DECLARE( \
  224. MSG_HWPROFILES_SUBGROUP, \
  225. MSG_HWPROFILES_INTRO, \
  226. MSG_HWPROFILES_INTRO_HTML, \
  227. MSG_UNINDENT2, \
  228. MSG_UNINDENT2_HTML, \
  229. NULL, \
  230. RF_BOLDITEMS, \
  231. REPORTLEVEL_VERBOSE \
  232. ) \
  233. DECLARE( \
  234. MSG_REG_SETTINGS_SUBGROUP, \
  235. MSG_REG_SETTINGS_INCOMPLETE, \
  236. MSG_REG_SETTINGS_INCOMPLETE_HTML, \
  237. MSG_UNINDENT2, \
  238. MSG_UNINDENT2_HTML, \
  239. NULL, \
  240. RF_BOLDITEMS, \
  241. REPORTLEVEL_INFORMATION \
  242. ) \
  243. DECLARE( \
  244. MSG_SHELL_SETTINGS_SUBGROUP, \
  245. MSG_SHELL_SETTINGS_INCOMPLETE, \
  246. MSG_SHELL_SETTINGS_INCOMPLETE_HTML, \
  247. MSG_UNINDENT2, \
  248. MSG_UNINDENT2_HTML, \
  249. NULL, \
  250. RF_BOLDITEMS, \
  251. REPORTLEVEL_INFORMATION \
  252. ) \
  253. DECLARE( \
  254. MSG_DRIVE_EXCLUDED_SUBGROUP, \
  255. MSG_DRIVE_EXCLUDED_INTRO, \
  256. MSG_DRIVE_EXCLUDED_INTRO_HTML, \
  257. MSG_UNINDENT2, \
  258. MSG_UNINDENT2_HTML, \
  259. NULL, \
  260. RF_BOLDITEMS, \
  261. REPORTLEVEL_VERBOSE \
  262. ) \
  263. DECLARE( \
  264. MSG_DRIVE_NETWORK_SUBGROUP, \
  265. MSG_DRIVE_NETWORK_INTRO, \
  266. MSG_DRIVE_NETWORK_INTRO_HTML, \
  267. MSG_UNINDENT2, \
  268. MSG_UNINDENT2_HTML, \
  269. NULL, \
  270. RF_BOLDITEMS, \
  271. REPORTLEVEL_VERBOSE \
  272. ) \
  273. DECLARE( \
  274. MSG_DRIVE_RAM_SUBGROUP, \
  275. MSG_DRIVE_RAM_INTRO, \
  276. MSG_DRIVE_RAM_INTRO_HTML, \
  277. MSG_UNINDENT2, \
  278. MSG_UNINDENT2_HTML, \
  279. NULL, \
  280. RF_BOLDITEMS, \
  281. REPORTLEVEL_VERBOSE \
  282. ) \
  283. DECLARE( \
  284. MSG_DRIVE_SUBST_SUBGROUP, \
  285. MSG_DRIVE_SUBST_INTRO, \
  286. MSG_DRIVE_SUBST_INTRO_HTML, \
  287. MSG_UNINDENT2, \
  288. MSG_UNINDENT2_HTML, \
  289. NULL, \
  290. RF_BOLDITEMS, \
  291. REPORTLEVEL_VERBOSE \
  292. ) \
  293. DECLARE( \
  294. MSG_DRIVE_INACCESSIBLE_SUBGROUP, \
  295. MSG_DRIVE_INACCESSIBLE_INTRO, \
  296. MSG_DRIVE_INACCESSIBLE_INTRO_HTML, \
  297. MSG_UNINDENT2, \
  298. MSG_UNINDENT2_HTML, \
  299. NULL, \
  300. RF_BOLDITEMS, \
  301. REPORTLEVEL_VERBOSE \
  302. ) \
  303. DECLARE( \
  304. MSG_HELPFILES_SUBGROUP, \
  305. MSG_HELPFILES_INTRO, \
  306. MSG_HELPFILES_INTRO_HTML, \
  307. MSG_UNINDENT2, \
  308. MSG_UNINDENT2_HTML, \
  309. NULL, \
  310. RF_BOLDITEMS, \
  311. REPORTLEVEL_VERBOSE \
  312. ) \
  313. DECLARE( \
  314. MSG_TWAIN_SUBGROUP, \
  315. MSG_TWAIN_DEVICES_UNKNOWN, \
  316. MSG_TWAIN_DEVICES_UNKNOWN_HTML, \
  317. MSG_UNINDENT2, \
  318. MSG_UNINDENT2_HTML, \
  319. NULL, \
  320. RF_BOLDITEMS, \
  321. REPORTLEVEL_ERROR \
  322. ) \
  323. DECLARE( \
  324. MSG_JOYSTICK_SUBGROUP, \
  325. MSG_JOYSTICK_DEVICE_UNKNOWN, \
  326. MSG_JOYSTICK_DEVICE_UNKNOWN_HTML, \
  327. MSG_UNINDENT2, \
  328. MSG_UNINDENT2_HTML, \
  329. NULL, \
  330. RF_BOLDITEMS, \
  331. REPORTLEVEL_ERROR \
  332. ) \
  333. DECLARE( \
  334. MSG_CONNECTION_BADPROTOCOL_SUBGROUP, \
  335. MSG_CONNECTION_BADPROTOCOL_INTRO, \
  336. MSG_CONNECTION_BADPROTOCOL_INTRO_HTML, \
  337. MSG_UNINDENT2, \
  338. MSG_UNINDENT2_HTML, \
  339. NULL, \
  340. RF_BOLDITEMS, \
  341. REPORTLEVEL_WARNING \
  342. ) \
  343. DECLARE( \
  344. MSG_CONNECTION_PASSWORD_SUBGROUP, \
  345. MSG_CONNECTION_PASSWORD_INTRO, \
  346. MSG_CONNECTION_PASSWORD_INTRO_HTML, \
  347. MSG_UNINDENT2, \
  348. MSG_UNINDENT2_HTML, \
  349. NULL, \
  350. RF_BOLDITEMS, \
  351. REPORTLEVEL_WARNING \
  352. ) \
  353. DECLARE( \
  354. MSG_RUNNING_MIGRATION_DLLS_SUBGROUP, \
  355. MSG_RUNNING_MIGRATION_DLLS_INTRO, \
  356. MSG_RUNNING_MIGRATION_DLLS_INTRO_HTML, \
  357. MSG_UNINDENT2, \
  358. MSG_UNINDENT2_HTML, \
  359. NULL, \
  360. RF_BOLDITEMS, \
  361. REPORTLEVEL_VERBOSE \
  362. ) \
  363. DECLARE( \
  364. MSG_TOTALLY_INCOMPATIBLE_SUBGROUP, \
  365. MSG_TOTALLY_INCOMPATIBLE_INTRO, \
  366. MSG_TOTALLY_INCOMPATIBLE_INTRO_HTML, \
  367. MSG_TOTALLY_INCOMPATIBLE_TRAIL, \
  368. MSG_TOTALLY_INCOMPATIBLE_TRAIL_HTML, \
  369. NULL, \
  370. RF_BOLDITEMS | RF_ENABLEMSG, \
  371. REPORTLEVEL_WARNING \
  372. ) \
  373. DECLARE( \
  374. MSG_INCOMPATIBLE_PREINSTALLED_UTIL_SUBGROUP, \
  375. MSG_PREINSTALLED_UTIL_INSTRUCTIONS, \
  376. MSG_PREINSTALLED_UTIL_INSTRUCTIONS_HTML, \
  377. MSG_UNINDENT2, \
  378. MSG_UNINDENT2_HTML, \
  379. NULL, \
  380. RF_BOLDITEMS, \
  381. REPORTLEVEL_VERBOSE \
  382. ) \
  383. DECLARE( \
  384. MSG_INCOMPATIBLE_UTIL_SIMILAR_FEATURE_SUBGROUP, \
  385. MSG_PREINSTALLED_SIMILAR_FEATURE, \
  386. MSG_PREINSTALLED_SIMILAR_FEATURE_HTML, \
  387. MSG_UNINDENT2, \
  388. MSG_UNINDENT2_HTML, \
  389. NULL, \
  390. RF_BOLDITEMS, \
  391. REPORTLEVEL_VERBOSE \
  392. ) \
  393. DECLARE( \
  394. MSG_INCOMPATIBLE_HW_UTIL_SUBGROUP, \
  395. MSG_HW_UTIL_INTRO, \
  396. MSG_HW_UTIL_INTRO_HTML, \
  397. MSG_UNINDENT2, \
  398. MSG_UNINDENT2_HTML, \
  399. NULL, \
  400. RF_BOLDITEMS, \
  401. REPORTLEVEL_VERBOSE \
  402. ) \
  403. DECLARE( \
  404. MSG_REMOVED_FOR_SAFETY_SUBGROUP, \
  405. MSG_REMOVED_FOR_SAFETY_INTRO, \
  406. MSG_REMOVED_FOR_SAFETY_INTRO_HTML, \
  407. MSG_UNINDENT, \
  408. MSG_UNINDENT_HTML, \
  409. NULL, \
  410. RF_BOLDITEMS, \
  411. REPORTLEVEL_INFORMATION \
  412. ) \
  413. DECLARE( \
  414. MSG_DIRECTORY_COLLISION_SUBGROUP, \
  415. MSG_DIRECTORY_COLLISION_INTRO, \
  416. MSG_DIRECTORY_COLLISION_INTRO_HTML, \
  417. MSG_UNINDENT, \
  418. MSG_UNINDENT_HTML, \
  419. NULL, \
  420. RF_USESUBGROUP, \
  421. REPORTLEVEL_VERBOSE \
  422. ) \
  423. DECLARE( \
  424. MSG_BACKUP_DETECTED_LIST_SUBGROUP, \
  425. MSG_BACKUP_DETECTED_INTRO, \
  426. MSG_BACKUP_DETECTED_INTRO_HTML, \
  427. MSG_BACKUP_DETECTED_TRAIL, \
  428. MSG_BACKUP_DETECTED_TRAIL_HTML, \
  429. g_BackupDetectedFormatArgs, \
  430. RF_BOLDITEMS, \
  431. REPORTLEVEL_VERBOSE \
  432. ) \
  433. DECLARE( \
  434. MSG_NAMECHANGE_WARNING_GROUP, \
  435. MSG_REPORT_NAMECHANGE, \
  436. MSG_REPORT_NAMECHANGE_HTML, \
  437. MSG_UNINDENT2, \
  438. MSG_UNINDENT2_HTML, \
  439. NULL, \
  440. RF_BOLDITEMS, \
  441. REPORTLEVEL_INFORMATION \
  442. ) \
  443. //
  444. // Declare an array of message groups
  445. //
  446. typedef struct {
  447. DWORD GroupLevel;
  448. UINT MsgGroup;
  449. UINT IntroId;
  450. UINT IntroIdHtml;
  451. UINT OtherId;
  452. UINT OtherIdHtml;
  453. // members initialized to zero
  454. UINT NameLchars;
  455. PCTSTR Name;
  456. PCTSTR IntroIdStr;
  457. PCTSTR IntroIdHtmlStr;
  458. PCTSTR OtherIdStr;
  459. PCTSTR OtherIdHtmlStr;
  460. } MSGGROUP_PROPS, *PMSGGROUP_PROPS;
  461. #define NO_TEXT(root,level) {level,root##_ROOT},
  462. #define HAS_INTRO(root,level) {level,root##_ROOT, root##_INTRO, root##_INTRO_HTML},
  463. #define HAS_OTHER(root,level) {level,root##_ROOT, 0, 0, root##_OTHER, root##_OTHER_HTML},
  464. #define HAS_BOTH(root,level) {level,root##_ROOT, root##_INTRO, root##_INTRO_HTML, root##_OTHER, root##_OTHER_HTML},
  465. MSGGROUP_PROPS g_MsgGroupRoot[] = {
  466. ROOT_MSGGROUPS /* , */
  467. {0,0,0,0,0,0}
  468. };
  469. #undef NO_TEXT
  470. #undef HAS_INTRO
  471. #undef HAS_OTHER
  472. #undef HAS_BOTH
  473. //
  474. // Declare the handler prototypes
  475. //
  476. typedef enum {
  477. INIT,
  478. PROCESS_ITEM,
  479. CLEANUP
  480. } CALLCONTEXT;
  481. typedef BOOL(SUBGROUP_HANDLER_PROTOTYPE)(
  482. IN CALLCONTEXT Context,
  483. IN PMSGGROUP_PROPS Group,
  484. IN OUT PGROWBUFFER StringBufPtr,
  485. IN PCTSTR SubGroup,
  486. IN PCTSTR Message,
  487. IN DWORD Level,
  488. IN BOOL HtmlFormat,
  489. IN OUT PVOID *State,
  490. IN DWORD Arg
  491. );
  492. typedef SUBGROUP_HANDLER_PROTOTYPE * SUBGROUP_HANDLER;
  493. #define DECLARE(msgid,fn,arg) SUBGROUP_HANDLER_PROTOTYPE fn;
  494. SUBGROUP_HANDLERS
  495. #undef DECLARE
  496. //
  497. // Declare the message ID array
  498. //
  499. #define DECLARE(msgid,fn,arg) {fn, msgid, (DWORD) (arg)},
  500. typedef struct {
  501. SUBGROUP_HANDLER Fn;
  502. UINT Id;
  503. DWORD Arg;
  504. // rest are init'd with zeros
  505. PCTSTR SubGroupStr;
  506. UINT SubGroupStrLchars;
  507. PVOID State;
  508. } HANDLER_LIST, *PHANDLER_LIST;
  509. HANDLER_LIST g_HandlerNames[] = {
  510. SUBGROUP_HANDLERS /* , */
  511. {NULL, 0, 0, 0}
  512. };
  513. #undef DECLARE
  514. //
  515. // Declare array of generic lists
  516. //
  517. typedef struct {
  518. UINT Id;
  519. PCTSTR SubGroupStr;
  520. UINT SubGroupStrLchars;
  521. UINT IntroId;
  522. UINT IntroIdHtml;
  523. UINT ConclusionId;
  524. UINT ConclusionIdHtml;
  525. PCTSTR **FormatArgs;
  526. DWORD Flags;
  527. DWORD ListLevel;
  528. } GENERIC_LIST, *PGENERIC_LIST;
  529. //
  530. // definition of flags in GENERIC_LIST.Flags
  531. //
  532. #define RF_BOLDITEMS 0x0001
  533. #define RF_ENABLEMSG 0x0002
  534. #define RF_MSGFIRST 0x0004
  535. #define RF_USEROOT 0x0008
  536. #define RF_USESUBGROUP 0x0010
  537. #define RF_INDENTMSG 0x0020
  538. //
  539. // macros to test flags
  540. //
  541. #define FLAGSET_BOLDITEMS(Flags) ((Flags & RF_BOLDITEMS) != 0)
  542. #define FLAGSET_ENABLE_MESSAGE_ITEMS(Flags) ((Flags & RF_ENABLEMSG) != 0)
  543. #define FLAGSET_MESSAGE_ITEMS_FIRST(Flags) ((Flags & RF_MSGFIRST) != 0)
  544. #define FLAGSET_USEROOT(Flags) ((Flags & RF_USEROOT) != 0)
  545. #define FLAGSET_USESUBGROUP(Flags) ((Flags & RF_USESUBGROUP) != 0)
  546. #define FLAGSET_INDENT_MESSAGE(Flags) ((Flags & RF_INDENTMSG) != 0)
  547. //
  548. // this is the array of pointers to strings for formatting MSG_BACKUP_DETECTED_SUBGROUP
  549. // they are the same for all 4 IDs (MSG_BACKUP_DETECTED_INTRO etc.)
  550. //
  551. static PCTSTR g_BackupDetectedFormatArgsAllIDs[1] = {
  552. g_Win95Name
  553. };
  554. static PCTSTR *g_BackupDetectedFormatArgs[4] = {
  555. g_BackupDetectedFormatArgsAllIDs,
  556. g_BackupDetectedFormatArgsAllIDs,
  557. g_BackupDetectedFormatArgsAllIDs,
  558. g_BackupDetectedFormatArgsAllIDs
  559. };
  560. #define DECLARE(rootid,intro,introhtml,conclusion,conclusionhtml,formatargs,flags,level) {rootid, NULL, 0, intro, introhtml, conclusion, conclusionhtml, formatargs, flags, level},
  561. GENERIC_LIST g_GenericList[] = {
  562. SUBGROUP_LISTS /* , */
  563. {0, NULL, 0, 0, 0, 0, 0, NULL, 0, 0}
  564. };
  565. #undef DECLARE
  566. #define SUBGROUP_REPORT_LEVELS \
  567. DECLARE(MSG_NETWORK_PROTOCOLS, REPORTLEVEL_WARNING) \
  568. #define ONEBITSET(x) ((x) && !((x) & ((x) - 1)))
  569. #define LEVELTOMASK(x) (((x) << 1) - 1)
  570. //
  571. // Types
  572. //
  573. typedef struct {
  574. GROWLIST List;
  575. GROWLIST MessageItems;
  576. GROWLIST Messages;
  577. } ITEMLIST, *PITEMLIST;
  578. typedef struct {
  579. TCHAR LastClass[MEMDB_MAX];
  580. } PNPFORMATSTATE, *PPNPFORMATSTATE;
  581. typedef struct {
  582. PCTSTR HtmlTagStr;
  583. UINT Length;
  584. } HTMLTAG, *PHTMLTAG;
  585. typedef struct {
  586. UINT MessageId;
  587. PCTSTR MessageStr;
  588. UINT MessageLength;
  589. DWORD ReportLevel;
  590. } MAPMESSAGETOREPORTLEVEL, *PMAPMESSAGETOREPORTLEVEL;
  591. //
  592. // Globals
  593. //
  594. static UINT g_TotalCols;
  595. static GROWBUFFER g_ReportString = GROWBUF_INIT;
  596. static TCHAR g_LastMsgGroupBuf[MAX_MSGGROUP];
  597. static BOOL g_ListFormat;
  598. #define DECLARE(messageid,reportlevel) {messageid, NULL, 0, reportlevel},
  599. static MAPMESSAGETOREPORTLEVEL g_MapMessageToLevel[] = {
  600. SUBGROUP_REPORT_LEVELS
  601. {0, NULL, 0, 0}
  602. };
  603. #undef DECLARE
  604. //
  605. // Local prototypes
  606. //
  607. VOID
  608. pLoadAllHandlerStrings (
  609. VOID
  610. );
  611. VOID
  612. pFreeAllHandlerStrings (
  613. VOID
  614. );
  615. PMSGGROUP_PROPS
  616. pFindMsgGroupStruct (
  617. IN PCTSTR MsgGroup
  618. );
  619. PMSGGROUP_PROPS
  620. pFindMsgGroupStructById (
  621. IN UINT GroupId
  622. );
  623. PCTSTR
  624. pEncodeMessage (
  625. IN PCTSTR Message,
  626. IN BOOL HtmlFormat
  627. );
  628. //
  629. // Implementation
  630. //
  631. POOLHANDLE g_MessagePool;
  632. BOOL
  633. InitCompatTable (
  634. VOID
  635. )
  636. /*++
  637. Routine Description:
  638. InitCompatTable initializes the resources needed to hold the incompatibility
  639. report in memory.
  640. Arguments:
  641. none
  642. Return Value:
  643. TRUE if the init succeeded, or FALSE if it failed.
  644. --*/
  645. {
  646. g_MessagePool = PoolMemInitNamedPool ("Incompatibility Messages");
  647. if (!g_MessagePool) {
  648. return FALSE;
  649. }
  650. pLoadAllHandlerStrings();
  651. return MemDbCreateTemporaryKey (MEMDB_CATEGORY_REPORT);
  652. }
  653. VOID
  654. FreeCompatTable (
  655. VOID
  656. )
  657. /*++
  658. Routine Description:
  659. FreeCompatTable frees the resources used to hold incompatibility messages.
  660. Arguments:
  661. none
  662. Return Value:
  663. none
  664. --*/
  665. {
  666. PoolMemDestroyPool (g_MessagePool);
  667. pFreeAllHandlerStrings();
  668. }
  669. PMAPMESSAGETOREPORTLEVEL
  670. pGetMapStructFromMessageGroup (
  671. IN PCTSTR FullMsgGroup
  672. )
  673. /*++
  674. Routine Description:
  675. pGetMapStructFromMessageGroup returns a pointer to the MAPMESSAGETOREPORTLEVEL
  676. associated with the given message group.
  677. Arguments:
  678. FullMsgGroup - Specifies the name of the message group in question
  679. Return Value:
  680. A pointer to the associated struct, if any; NULL if not found
  681. --*/
  682. {
  683. PMAPMESSAGETOREPORTLEVEL pMap;
  684. PCTSTR p;
  685. CHARTYPE ch;
  686. if (!FullMsgGroup) {
  687. return FALSE;
  688. }
  689. for (pMap = g_MapMessageToLevel; pMap->MessageId; pMap++) {
  690. if (StringIMatchLcharCount (
  691. FullMsgGroup,
  692. pMap->MessageStr,
  693. pMap->MessageLength
  694. )) {
  695. p = LcharCountToPointer (FullMsgGroup, pMap->MessageLength);
  696. ch = _tcsnextc (p);
  697. if (ch == TEXT('\\') || ch == 0) {
  698. return pMap;
  699. }
  700. }
  701. }
  702. return NULL;
  703. }
  704. BOOL
  705. pIsThisTheGenericList (
  706. IN PCTSTR FullMsgGroup,
  707. IN PGENERIC_LIST List
  708. )
  709. /*++
  710. Routine Description:
  711. pIsThisTheGenericList compares the specified message group list name
  712. against the specified message group.
  713. Arguments:
  714. FullMsgGroup - Specifies the name of the message group in question
  715. List - Specifies the message group list to compare against FullMsgGroup
  716. Return Value:
  717. TRUE if List handles the FullMsgGroup message, or FALSE if not.
  718. --*/
  719. {
  720. PCTSTR p;
  721. CHARTYPE ch;
  722. if (!List) {
  723. return FALSE;
  724. }
  725. if (StringIMatchLcharCount (
  726. FullMsgGroup,
  727. List->SubGroupStr,
  728. List->SubGroupStrLchars
  729. )) {
  730. p = LcharCountToPointer (FullMsgGroup, List->SubGroupStrLchars);
  731. ch = _tcsnextc (p);
  732. if (ch == TEXT('\\') || ch == 0) {
  733. return TRUE;
  734. }
  735. }
  736. return FALSE;
  737. }
  738. PGENERIC_LIST
  739. pSearchForGenericList (
  740. IN PCTSTR Str
  741. )
  742. /*++
  743. Routine Description:
  744. pSearchForGenericList scans the list of generic lists for one that can
  745. handle the current message group. A pointer to the generic list structure is
  746. returned.
  747. Arguments:
  748. Str - Specifies the message group to locate a handler for, and may include
  749. backslashes and subgroups.
  750. Return Value:
  751. A pointer to the generic list structure if found, or NULL if no generic
  752. list exists for the message group.
  753. --*/
  754. {
  755. PGENERIC_LIST List;
  756. for (List = g_GenericList ; List->Id ; List++) {
  757. if (pIsThisTheGenericList (Str, List)) {
  758. return List;
  759. }
  760. }
  761. return NULL;
  762. }
  763. BOOL
  764. AddBadSoftware (
  765. IN PCTSTR MessageGroup,
  766. IN PCTSTR Message, OPTIONAL
  767. IN BOOL IncludeOnShortReport
  768. )
  769. /*++
  770. Routine Description:
  771. AddBadSoftware adds a message to the incompatibility report data structures.
  772. It duplicates Message into a pool, and indexes the message by message group
  773. with memdb.
  774. Arguments:
  775. MessageGroup - Specifies the name of the message group, and may include
  776. subgroups. The root of MessageGroup must be a defined category.
  777. See ROOT_MSGGROUPS above.
  778. Message - Specifies the text for the message, if any.
  779. IncludeOnShortReport - Specifies TRUE if the message should appear in the list
  780. view of the short report.
  781. Return Value:
  782. TRUE if the operation succeeded, or FALSE if it failed.
  783. --*/
  784. {
  785. UINT BytesNeeded;
  786. PCTSTR DupMsg;
  787. TCHAR FilteredMessageGroup[MEMDB_MAX];
  788. PMSGGROUP_PROPS Group;
  789. TCHAR key[MEMDB_MAX];
  790. PGENERIC_LIST list = NULL;
  791. PTSTR p;
  792. PMAPMESSAGETOREPORTLEVEL pMap;
  793. DWORD level = REPORTLEVEL_NONE;
  794. if (Message && Message[0] == 0) {
  795. Message = NULL;
  796. }
  797. Group = pFindMsgGroupStruct (MessageGroup);
  798. if (!Group) {
  799. //
  800. // This message group is illegal. Assume that it is from a migration DLL,
  801. // and put it in the Upgrade Module Information group.
  802. //
  803. Group = pFindMsgGroupStructById (MSG_INSTALL_NOTES_ROOT);
  804. MYASSERT (Group);
  805. wsprintf (FilteredMessageGroup, TEXT("%s\\%s"), Group->Name, MessageGroup);
  806. } else {
  807. StringCopy (FilteredMessageGroup, MessageGroup);
  808. }
  809. if (Message) {
  810. BytesNeeded = SizeOfString (Message);
  811. DupMsg = PoolMemDuplicateString (g_MessagePool, Message);
  812. if (!DupMsg) {
  813. return FALSE;
  814. }
  815. DEBUGMSG ((DBG_REPORT, "%s: %s", FilteredMessageGroup, Message));
  816. } else {
  817. DupMsg = NULL;
  818. DEBUGMSG ((DBG_REPORT, "%s", FilteredMessageGroup));
  819. }
  820. //
  821. // Check to see if there is a handler for all messages in the message
  822. // group.
  823. //
  824. p = _tcschr (MessageGroup, TEXT('\\'));
  825. if (p) {
  826. p = _tcsinc (p);
  827. list = pSearchForGenericList (p);
  828. if (list) {
  829. level = list->ListLevel;
  830. }
  831. }
  832. if (level == REPORTLEVEL_NONE) {
  833. list = pSearchForGenericList (MessageGroup);
  834. if (list) {
  835. level = list->ListLevel;
  836. }
  837. }
  838. if (level == REPORTLEVEL_NONE) {
  839. pMap = pGetMapStructFromMessageGroup (MessageGroup);
  840. if (pMap) {
  841. level = pMap->ReportLevel;
  842. }
  843. }
  844. if (level == REPORTLEVEL_NONE) {
  845. level = Group->GroupLevel;
  846. }
  847. MYASSERT (ONEBITSET (level));
  848. if (IncludeOnShortReport) {
  849. level |= REPORTLEVEL_IN_SHORT_LIST;
  850. }
  851. MemDbBuildKey (key, MEMDB_CATEGORY_REPORT, FilteredMessageGroup, NULL, NULL);
  852. return MemDbSetValueAndFlags (key, (DWORD) DupMsg, level, REPORTLEVEL_ALL);
  853. }
  854. VOID
  855. pCutAfterFirstLine (
  856. IN PTSTR Message
  857. )
  858. {
  859. PTSTR p = _tcsstr (Message, TEXT("\r\n"));
  860. if (p) {
  861. *p = 0;
  862. }
  863. }
  864. BOOL
  865. pEnumMessageWorker (
  866. IN OUT PREPORT_MESSAGE_ENUM EnumPtr
  867. )
  868. /*++
  869. Routine Description:
  870. pEnumMessageWorker completes an enumeration by filling in members of the
  871. enum structure. This routine is common to message enumerations.
  872. Do not use the Message member of the enumeration structure, as its contents
  873. are undefined.
  874. Arguments:
  875. EnumPtr - Specifies a paritly completed enumeration structure that needs
  876. the rest of its members updated.
  877. Return Value:
  878. TRUE if the message should be processed, FALSE otherwise.
  879. --*/
  880. {
  881. if (!(EnumPtr->e.UserFlags & EnumPtr->EnumLevel)) {
  882. return FALSE;
  883. }
  884. StringCopy (EnumPtr->MsgGroup, EnumPtr->e.szName);
  885. //
  886. // Value has pointer to message, or is NULL
  887. //
  888. EnumPtr->Message = (PCTSTR) EnumPtr->e.dwValue;
  889. return TRUE;
  890. }
  891. BOOL
  892. EnumFirstMessage (
  893. OUT PREPORT_MESSAGE_ENUM EnumPtr,
  894. IN PCTSTR RootCategory, OPTIONAL
  895. IN DWORD LevelMask
  896. )
  897. /*++
  898. Routine Description:
  899. EnumFirstMessage begins an enumeration of all message group names in
  900. the migration report, including subgroups. The Message member of the
  901. enum structure will point to the actual message, or NULL if none
  902. exists.
  903. Arguments:
  904. EnumPtr - Receives the enumerated message, with the members set as follows:
  905. MsgGroup - Receives the name of the message group. If
  906. RootCategory is specified, MsgGroup will contain
  907. the subgroup of RootCategory. If no subgroup exists,
  908. MsgGroup will be an empty string.
  909. Message - Points to the message text, or NULL if no message text
  910. exists.
  911. RootCategory - Specifies a specific message group to enumerate. It may also
  912. contain subgroups, separated by backslashes.
  913. LevelMask - Specifies which report severity levels to list. If 0 is specified, all
  914. levels are enumerated.
  915. Return Value:
  916. TRUE if a message was enumerated, or FALSE if not.
  917. Remarks:
  918. The enumeration does not allocate any resources, so it can be abandoned at
  919. any time.
  920. --*/
  921. {
  922. EnumPtr->EnumLevel = LevelMask ? LevelMask : REPORTLEVEL_ALL;
  923. if (MemDbGetValueEx (
  924. &EnumPtr->e,
  925. MEMDB_CATEGORY_REPORT,
  926. RootCategory,
  927. NULL
  928. )) {
  929. do {
  930. if (pEnumMessageWorker (EnumPtr)) {
  931. return TRUE;
  932. }
  933. } while (MemDbEnumNextValue (&EnumPtr->e));
  934. }
  935. return FALSE;
  936. }
  937. BOOL
  938. EnumNextMessage (
  939. IN OUT PREPORT_MESSAGE_ENUM EnumPtr
  940. )
  941. /*++
  942. Routine Description:
  943. EnumNextMessage continues the enumeration started by EnumFirstMessage.
  944. Arguments:
  945. EnumPtr - Specifies the current enumeration state, receives the enumerated item.
  946. Return Value:
  947. TRUE if another message was enumerated, or FALSE if not.
  948. --*/
  949. {
  950. while (MemDbEnumNextValue (&EnumPtr->e)) {
  951. if (pEnumMessageWorker (EnumPtr)) {
  952. return TRUE;
  953. }
  954. }
  955. return FALSE;
  956. }
  957. BOOL
  958. EnumFirstRootMsgGroup (
  959. OUT PREPORT_MESSAGE_ENUM EnumPtr,
  960. IN DWORD LevelMask
  961. )
  962. /*++
  963. Routine Description:
  964. EnumFirstRootMsgGroup begins an enumeration of all message group names in
  965. the migration report, but does not enumerate subgroups.
  966. Arguments:
  967. EnumPtr - Receives the enumerated item. In particular, the MsgGroup
  968. member will contain the name of the message group.
  969. LevelMask - Specifies which error levels to enumerate (blocking, errors,
  970. warnings, etc.)
  971. Return Value:
  972. TRUE if a message group was enumerated, or FALSE if not.
  973. Remarks:
  974. The enumeration does not allocate any resources, so it can be abandoned at
  975. any time.
  976. --*/
  977. {
  978. ZeroMemory (EnumPtr, sizeof (REPORT_MESSAGE_ENUM));
  979. EnumPtr->EnumLevel = LevelMask;
  980. return EnumNextRootMsgGroup (EnumPtr);
  981. }
  982. BOOL
  983. EnumNextRootMsgGroup (
  984. IN OUT PREPORT_MESSAGE_ENUM EnumPtr
  985. )
  986. /*++
  987. Routine Description:
  988. EnumNextRootMsgGroup continues an enumeration of message group names.
  989. Arguments:
  990. EnumPtr - Specifies an enumeration structure that was first updated by
  991. EnumFirstRootMsgGroup, and optionally updated by previous
  992. calls to EnumNextRootMsgGroup.
  993. Return Value:
  994. TRUE if another message group was enumerated, or FALSE no more groups
  995. exist.
  996. --*/
  997. {
  998. REPORT_MESSAGE_ENUM e;
  999. while (g_MsgGroupRoot[EnumPtr->Index].MsgGroup) {
  1000. //
  1001. // Determine if g_MsgGroupRoot[i].Name has messages to display
  1002. //
  1003. if (EnumFirstMessage (&e, g_MsgGroupRoot[EnumPtr->Index].Name, EnumPtr->EnumLevel)) {
  1004. StringCopy (EnumPtr->MsgGroup, g_MsgGroupRoot[EnumPtr->Index].Name);
  1005. EnumPtr->Message = NULL;
  1006. EnumPtr->Index++;
  1007. return TRUE;
  1008. }
  1009. EnumPtr->Index++;
  1010. }
  1011. return FALSE;
  1012. }
  1013. BOOL
  1014. pAppendStringToGrowBuf (
  1015. IN OUT PGROWBUFFER StringBuf,
  1016. IN PCTSTR String
  1017. )
  1018. /*++
  1019. Routine Description:
  1020. pAppendStringToGrowBuf is called by handler functions
  1021. to add formatted text to a report buffer.
  1022. Arguments:
  1023. StringBuf - Specifies the current report to append text to.
  1024. String - Specifies the text to append.
  1025. Return Value:
  1026. TRUE if the allocation succeeded, or FALSE if it failed.
  1027. --*/
  1028. {
  1029. PTSTR Buf;
  1030. if (!String || *String == 0) {
  1031. return TRUE;
  1032. }
  1033. if (StringBuf->End) {
  1034. StringBuf->End -= sizeof (TCHAR);
  1035. }
  1036. Buf = (PTSTR) GrowBuffer (StringBuf, SizeOfString (String));
  1037. if (!Buf) {
  1038. return FALSE;
  1039. }
  1040. StringCopy (Buf, String);
  1041. return TRUE;
  1042. }
  1043. BOOL
  1044. pStartHeaderLine (
  1045. IN OUT PGROWBUFFER StringBuf
  1046. )
  1047. {
  1048. return pAppendStringToGrowBuf (StringBuf, TEXT("<H>"));
  1049. }
  1050. BOOL
  1051. pWriteNewLine (
  1052. IN OUT PGROWBUFFER StringBuf
  1053. )
  1054. {
  1055. return pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
  1056. }
  1057. BOOL
  1058. pDumpDwordToGrowBuf (
  1059. IN OUT PGROWBUFFER StringBuf,
  1060. IN DWORD Dword
  1061. )
  1062. {
  1063. TCHAR buffer[12];
  1064. wsprintf (buffer, TEXT("<%lu>"), Dword);
  1065. return pAppendStringToGrowBuf (StringBuf, buffer);
  1066. }
  1067. BOOL
  1068. pWrapStringToGrowBuf (
  1069. IN OUT PGROWBUFFER StringBuf,
  1070. IN PCTSTR String,
  1071. IN UINT Indent,
  1072. IN INT HangingIndent
  1073. )
  1074. /*++
  1075. Routine Description:
  1076. pWrapStringToGrowBuf is called by handler functions
  1077. to add plain text to a report buffer.
  1078. Arguments:
  1079. StringBuf - Specifies the current report to append text to.
  1080. String - Specifies the text to append.
  1081. Indent - Specifies number of characters to indent text, may
  1082. be zero.
  1083. HangingIndent - Specifies the adjustment to be made to Indent
  1084. after the first line is processed.
  1085. Return Value:
  1086. TRUE if the allocation succeeded, or FALSE if it failed.
  1087. --*/
  1088. {
  1089. PTSTR Buf;
  1090. PCTSTR WrappedStr;
  1091. if (!String || *String == 0) {
  1092. return TRUE;
  1093. }
  1094. WrappedStr = CreateIndentedString (String, Indent, HangingIndent, g_TotalCols);
  1095. if (!WrappedStr) {
  1096. return FALSE;
  1097. }
  1098. if (StringBuf->End) {
  1099. StringBuf->End -= sizeof (TCHAR);
  1100. }
  1101. Buf = (PTSTR) GrowBuffer (StringBuf, SizeOfString (WrappedStr));
  1102. if (!Buf) {
  1103. MemFree (g_hHeap, 0, WrappedStr);
  1104. return FALSE;
  1105. }
  1106. StringCopy (Buf, WrappedStr);
  1107. MemFree (g_hHeap, 0, WrappedStr);
  1108. return TRUE;
  1109. }
  1110. PTSTR
  1111. pFindEndOfGroup (
  1112. IN PCTSTR Group
  1113. )
  1114. /*++
  1115. Routine Description:
  1116. pFindEndOfGroup returns the end of a piece of a group string.
  1117. Arguments:
  1118. Group - Specifies the start of the message group to find the end of.
  1119. Return Value:
  1120. A pointer to the nul or backslash terminating the message group.
  1121. --*/
  1122. {
  1123. PTSTR p;
  1124. p = _tcschr (Group, TEXT('\\'));
  1125. if (!p) {
  1126. p = GetEndOfString (Group);
  1127. }
  1128. return p;
  1129. }
  1130. PTSTR
  1131. pFindNextGroup (
  1132. IN PCTSTR Group
  1133. )
  1134. /*++
  1135. Routine Description:
  1136. pFindNextGroup returns a pointer to the next piece of a message group. In
  1137. other words, it locates the next backslash, advances one more character, and
  1138. returns the pointer to the rest of the string.
  1139. Arguments:
  1140. Group - Specifies the start of the current message group
  1141. Return Value:
  1142. A pointer to the next piece of the group, or a pointer to the nul terminator
  1143. if no more pieces exist.
  1144. --*/
  1145. {
  1146. PTSTR Next;
  1147. Next = pFindEndOfGroup (Group);
  1148. if (*Next) {
  1149. Next = _tcsinc (Next);
  1150. }
  1151. return Next;
  1152. }
  1153. PTSTR
  1154. pExtractNextMsgGroup (
  1155. IN PCTSTR Group,
  1156. OUT PCTSTR *NextGroup OPTIONAL
  1157. )
  1158. /*++
  1159. Routine Description:
  1160. pExtractNextMsgGroup locates the start and end of the current message group
  1161. piece, copies it into a new buffer, and returns a pointer to the next piece.
  1162. Arguments:
  1163. Group - Specifies the start of the current message group piece
  1164. NextGroup - Receives a pointer to the next message group piece
  1165. Return Value:
  1166. A pointer to the newly allocated string. The caller must free this by
  1167. calling FreePathString.
  1168. --*/
  1169. {
  1170. PCTSTR p;
  1171. PTSTR Base;
  1172. p = pFindEndOfGroup (Group);
  1173. //
  1174. // Duplicate the subgroup
  1175. //
  1176. Base = AllocPathString (p - Group + 1);
  1177. if (Base) {
  1178. StringCopyAB (Base, Group, p);
  1179. }
  1180. //
  1181. // Supply caller with a pointer to the next subgroup,
  1182. // or a pointer to the nul character.
  1183. //
  1184. if (NextGroup) {
  1185. if (*p) {
  1186. p = _tcsinc (p);
  1187. }
  1188. *NextGroup = p;
  1189. }
  1190. return Base;
  1191. }
  1192. VOID
  1193. pLoadHandlerStringWorker (
  1194. IN UINT MessageId,
  1195. IN UINT RootId,
  1196. OUT PCTSTR *Str
  1197. )
  1198. {
  1199. if (MessageId && MessageId != RootId) {
  1200. *Str = GetStringResource (MessageId);
  1201. } else {
  1202. *Str = NULL;
  1203. }
  1204. }
  1205. VOID
  1206. pLoadAllHandlerStrings (
  1207. VOID
  1208. )
  1209. /*++
  1210. Routine Description:
  1211. pLoadAllHandlerStrings loads the string resources needed by all handlers.
  1212. The pointers are saved in a global array, and are used during
  1213. CreateReportText. When this module terminates, the global array is freed.
  1214. Arguments:
  1215. none
  1216. Return Value:
  1217. none
  1218. --*/
  1219. {
  1220. INT i;
  1221. PTSTR p;
  1222. for (i = 0 ; g_HandlerNames[i].Id ; i++) {
  1223. g_HandlerNames[i].SubGroupStr = GetStringResource (g_HandlerNames[i].Id);
  1224. MYASSERT (g_HandlerNames[i].SubGroupStr);
  1225. g_HandlerNames[i].SubGroupStrLchars = LcharCount (g_HandlerNames[i].SubGroupStr);
  1226. }
  1227. for (i = 0 ; g_GenericList[i].Id ; i++) {
  1228. g_GenericList[i].SubGroupStr = GetStringResource (g_GenericList[i].Id);
  1229. MYASSERT (g_GenericList[i].SubGroupStr);
  1230. g_GenericList[i].SubGroupStrLchars = LcharCount (g_GenericList[i].SubGroupStr);
  1231. }
  1232. for (i = 0 ; g_MsgGroupRoot[i].MsgGroup ; i++) {
  1233. g_MsgGroupRoot[i].Name = GetStringResource (g_MsgGroupRoot[i].MsgGroup);
  1234. MYASSERT (g_MsgGroupRoot[i].Name);
  1235. g_MsgGroupRoot[i].NameLchars = LcharCount (g_MsgGroupRoot[i].Name);
  1236. pLoadHandlerStringWorker (
  1237. g_MsgGroupRoot[i].IntroId,
  1238. g_MsgGroupRoot[i].MsgGroup,
  1239. &g_MsgGroupRoot[i].IntroIdStr
  1240. );
  1241. pLoadHandlerStringWorker (
  1242. g_MsgGroupRoot[i].IntroIdHtml,
  1243. g_MsgGroupRoot[i].MsgGroup,
  1244. &g_MsgGroupRoot[i].IntroIdHtmlStr
  1245. );
  1246. pLoadHandlerStringWorker (
  1247. g_MsgGroupRoot[i].OtherId,
  1248. g_MsgGroupRoot[i].MsgGroup,
  1249. &g_MsgGroupRoot[i].OtherIdStr
  1250. );
  1251. pLoadHandlerStringWorker (
  1252. g_MsgGroupRoot[i].OtherIdHtml,
  1253. g_MsgGroupRoot[i].MsgGroup,
  1254. &g_MsgGroupRoot[i].OtherIdHtmlStr
  1255. );
  1256. }
  1257. for (i = 0; g_MapMessageToLevel[i].MessageId; i++) {
  1258. g_MapMessageToLevel[i].MessageStr = GetStringResource (g_MapMessageToLevel[i].MessageId);
  1259. MYASSERT (g_MapMessageToLevel[i].MessageStr);
  1260. p = _tcschr (g_MapMessageToLevel[i].MessageStr, TEXT('\\'));
  1261. if (p) {
  1262. *p = 0;
  1263. }
  1264. g_MapMessageToLevel[i].MessageLength = LcharCount (g_MapMessageToLevel[i].MessageStr);
  1265. }
  1266. }
  1267. VOID
  1268. pFreeAllHandlerStrings (
  1269. VOID
  1270. )
  1271. /*++
  1272. Routine Description:
  1273. pFreeAllHandlerStrings cleans up the global array of string
  1274. resources before process termination.
  1275. Arguments:
  1276. none
  1277. Return Value:
  1278. none
  1279. --*/
  1280. {
  1281. INT i;
  1282. for (i = 0 ; g_HandlerNames[i].Id ; i++) {
  1283. FreeStringResource (g_HandlerNames[i].SubGroupStr);
  1284. g_HandlerNames[i].SubGroupStr = NULL;
  1285. g_HandlerNames[i].SubGroupStrLchars = 0;
  1286. }
  1287. for (i = 0 ; g_GenericList[i].Id ; i++) {
  1288. FreeStringResource (g_GenericList[i].SubGroupStr);
  1289. g_GenericList[i].SubGroupStr = NULL;
  1290. g_GenericList[i].SubGroupStrLchars = 0;
  1291. }
  1292. for (i = 0 ; g_MsgGroupRoot[i].MsgGroup ; i++) {
  1293. FreeStringResource (g_MsgGroupRoot[i].Name);
  1294. FreeStringResource (g_MsgGroupRoot[i].IntroIdStr);
  1295. FreeStringResource (g_MsgGroupRoot[i].IntroIdHtmlStr);
  1296. FreeStringResource (g_MsgGroupRoot[i].OtherIdStr);
  1297. FreeStringResource (g_MsgGroupRoot[i].OtherIdHtmlStr);
  1298. g_MsgGroupRoot[i].Name = NULL;
  1299. g_MsgGroupRoot[i].NameLchars = 0;
  1300. g_MsgGroupRoot[i].IntroIdStr = NULL;
  1301. g_MsgGroupRoot[i].IntroIdHtmlStr = NULL;
  1302. g_MsgGroupRoot[i].OtherIdStr = NULL;
  1303. g_MsgGroupRoot[i].OtherIdHtmlStr = NULL;
  1304. }
  1305. for (i = 0; g_MapMessageToLevel[i].MessageId; i++) {
  1306. FreeStringResource (g_MapMessageToLevel[i].MessageStr);
  1307. g_MapMessageToLevel[i].MessageStr = NULL;
  1308. }
  1309. }
  1310. BOOL
  1311. pIsThisTheHandler (
  1312. IN PCTSTR FullMsgGroup,
  1313. IN PHANDLER_LIST Handler
  1314. )
  1315. /*++
  1316. Routine Description:
  1317. pIsThisTheHandler compares the specified message group handler name against
  1318. the specified message group.
  1319. Arguments:
  1320. FullMsgGroup - Specifies the name of the message group in question
  1321. Handler - Specifies the message group handler to compare against FullMsgGroup
  1322. Return Value:
  1323. TRUE if Handler handles the FullMsgGroup message, or FALSE if not.
  1324. --*/
  1325. {
  1326. PCTSTR p;
  1327. CHARTYPE ch;
  1328. if (!Handler) {
  1329. return FALSE;
  1330. }
  1331. if (StringIMatchLcharCount (
  1332. FullMsgGroup,
  1333. Handler->SubGroupStr,
  1334. Handler->SubGroupStrLchars
  1335. )) {
  1336. p = LcharCountToPointer (FullMsgGroup, Handler->SubGroupStrLchars);
  1337. ch = _tcsnextc (p);
  1338. if (ch == TEXT('\\') || ch == 0) {
  1339. return TRUE;
  1340. }
  1341. }
  1342. return FALSE;
  1343. }
  1344. PHANDLER_LIST
  1345. pSearchForMsgGroupHandler (
  1346. IN PCTSTR Str
  1347. )
  1348. /*++
  1349. Routine Description:
  1350. pSearchForMsgGroupHandler scans the list of handlers for one that can
  1351. handle the current message group. A pointer to the handler structure is
  1352. returned.
  1353. Arguments:
  1354. Str - Specifies the message group to locate a handler for, and may include
  1355. backslashes and subgroups.
  1356. Return Value:
  1357. A pointer to the handler struct if found, or the default handler if no
  1358. handler exists for the message group.
  1359. --*/
  1360. {
  1361. PHANDLER_LIST Handler;
  1362. for (Handler = g_HandlerNames ; Handler->Id ; Handler++) {
  1363. if (pIsThisTheHandler (Str, Handler)) {
  1364. break;
  1365. }
  1366. }
  1367. return Handler;
  1368. }
  1369. PMSGGROUP_PROPS
  1370. pFindMsgGroupStruct (
  1371. IN PCTSTR MsgGroup
  1372. )
  1373. /*++
  1374. Routine Desciption:
  1375. pFindMsgGroupStruct returns the pointer to the root message group structure,
  1376. which defines attributes about the message group, such as introduction text
  1377. and intro text for the non-handled messages.
  1378. Arguments:
  1379. MsgGroup - Specifies the text name of the message group, which may include
  1380. sub groups
  1381. Return Value:
  1382. A pointer to the root message group struct, or NULL if it is not defined.
  1383. --*/
  1384. {
  1385. PCTSTR p;
  1386. CHARTYPE ch;
  1387. PMSGGROUP_PROPS Group;
  1388. for (Group = g_MsgGroupRoot ; Group->MsgGroup ; Group++) {
  1389. if (StringIMatchLcharCount (MsgGroup, Group->Name, Group->NameLchars)) {
  1390. p = LcharCountToPointer (MsgGroup, Group->NameLchars);
  1391. ch = _tcsnextc (p);
  1392. if (ch == TEXT('\\') || ch == 0) {
  1393. return Group;
  1394. }
  1395. }
  1396. }
  1397. return NULL;
  1398. }
  1399. PMSGGROUP_PROPS
  1400. pFindMsgGroupStructById (
  1401. IN UINT MsgGroupId
  1402. )
  1403. /*++
  1404. Routine Desciption:
  1405. pFindMsgGroupStructById returns the pointer to the root message group structure,
  1406. which defines attributes about the message group, such as introduction text
  1407. and intro text for the non-handled messages. It searches based on the string
  1408. ID of the group.
  1409. Arguments:
  1410. MsgGroupId - Specifies the MSG_* constant of the group to find
  1411. Return Value:
  1412. A pointer to the root message group struct, or NULL if it is not defined.
  1413. --*/
  1414. {
  1415. PMSGGROUP_PROPS Group;
  1416. for (Group = g_MsgGroupRoot ; Group->MsgGroup ; Group++) {
  1417. if (Group->MsgGroup == MsgGroupId) {
  1418. return Group;
  1419. }
  1420. }
  1421. return NULL;
  1422. }
  1423. BOOL
  1424. pAddMsgGroupString (
  1425. IN OUT PGROWBUFFER StringBuf,
  1426. IN PMSGGROUP_PROPS Group,
  1427. IN PCTSTR MsgGroup,
  1428. IN BOOL HtmlFormat,
  1429. IN DWORD Level
  1430. )
  1431. /*++
  1432. Routine Description:
  1433. pAddMsgGroupString formats a message group into a heirarchy
  1434. for the report. That is, if group is a string such as:
  1435. foo\bar\moo
  1436. then the following text is added to the report:
  1437. foo
  1438. bar
  1439. moo
  1440. If HtmlFormat is TRUE, then the bold attribute is enabled
  1441. for the group string.
  1442. Arguments:
  1443. StringBuf - Specifies the current report to append text to.
  1444. Group - Specifies the group properties of this item
  1445. MsgGroup - Specifies the message group to add to the report
  1446. HtmlFormat - Specifies TRUE if the message group should be formatted
  1447. with HTML tags, or FALSE if it should be plain
  1448. text.
  1449. Level - Specifies the severity of the message
  1450. Return Value:
  1451. TRUE if multiple lines were added, or FALSE if zero or one line was added.
  1452. --*/
  1453. {
  1454. UINT Spaces = 0;
  1455. TCHAR SpaceBuf[MAX_SPACES * 6];
  1456. PCTSTR SubMsgGroup = NULL;
  1457. PCTSTR LastSubMsgGroup = NULL;
  1458. PCTSTR NextMsgGroup;
  1459. PCTSTR LastMsgGroup;
  1460. PCTSTR Msg = NULL;
  1461. TCHAR SummaryStrHeader[512] = TEXT("");
  1462. TCHAR SummaryStrItem[512] = TEXT("");
  1463. UINT lineCount = 0;
  1464. Level &= REPORTLEVEL_ALL;
  1465. MYASSERT (ONEBITSET (Level));
  1466. NextMsgGroup = MsgGroup;
  1467. LastMsgGroup = g_LastMsgGroupBuf;
  1468. if (HtmlFormat) {
  1469. pAppendStringToGrowBuf (StringBuf, TEXT("<B>"));
  1470. }
  1471. if (g_ListFormat) {
  1472. StackStringCopy (SummaryStrHeader, Group->Name);
  1473. }
  1474. SpaceBuf[0] = 0;
  1475. while (*NextMsgGroup) {
  1476. __try {
  1477. SubMsgGroup = pExtractNextMsgGroup (NextMsgGroup, &NextMsgGroup);
  1478. if (*LastMsgGroup) {
  1479. LastSubMsgGroup = pExtractNextMsgGroup (LastMsgGroup, &LastMsgGroup);
  1480. if (StringIMatch (LastSubMsgGroup, SubMsgGroup)) {
  1481. __leave;
  1482. }
  1483. if (!Spaces) {
  1484. if (!HtmlFormat) {
  1485. pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
  1486. }
  1487. }
  1488. LastMsgGroup = GetEndOfString (LastMsgGroup);
  1489. }
  1490. //
  1491. // Add indenting
  1492. //
  1493. if (Spaces) {
  1494. pAppendStringToGrowBuf (StringBuf, SpaceBuf);
  1495. }
  1496. //
  1497. // Add subgroup
  1498. //
  1499. Msg = NULL;
  1500. if (!g_ListFormat) {
  1501. if (HtmlFormat) {
  1502. pAppendStringToGrowBuf (StringBuf, SubMsgGroup);
  1503. } else {
  1504. Msg = pEncodeMessage (SubMsgGroup, FALSE);
  1505. pAppendStringToGrowBuf (StringBuf, Msg);
  1506. }
  1507. } else {
  1508. if (*NextMsgGroup) {
  1509. StringCopy (SummaryStrHeader, SubMsgGroup);
  1510. } else {
  1511. StringCopy (SummaryStrItem, SubMsgGroup);
  1512. }
  1513. }
  1514. if (HtmlFormat) {
  1515. pAppendStringToGrowBuf (StringBuf, TEXT("<BR>\r\n"));
  1516. } else {
  1517. pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
  1518. }
  1519. }
  1520. __finally {
  1521. if (HtmlFormat) {
  1522. StringCat (SpaceBuf, TEXT("&nbsp;"));
  1523. StringCat (SpaceBuf, TEXT("&nbsp;"));
  1524. } else {
  1525. SpaceBuf[Spaces] = TEXT(' ');
  1526. SpaceBuf[Spaces+1] = TEXT(' ');
  1527. SpaceBuf[Spaces+2] = 0;
  1528. }
  1529. lineCount++;
  1530. Spaces += 2;
  1531. MYASSERT (Spaces < MAX_SPACES - 2);
  1532. FreePathString (SubMsgGroup);
  1533. FreePathString (LastSubMsgGroup);
  1534. SubMsgGroup = NULL;
  1535. LastSubMsgGroup = NULL;
  1536. if (Msg) {
  1537. FreeText (Msg);
  1538. Msg = NULL;
  1539. }
  1540. }
  1541. }
  1542. if (g_ListFormat) {
  1543. if (SummaryStrHeader[0] && SummaryStrItem[0]) {
  1544. pStartHeaderLine (StringBuf);
  1545. pDumpDwordToGrowBuf (StringBuf, Level);
  1546. Msg = pEncodeMessage (SummaryStrHeader, FALSE);
  1547. pAppendStringToGrowBuf (StringBuf, Msg);
  1548. FreeText (Msg);
  1549. pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
  1550. pDumpDwordToGrowBuf (StringBuf, Level);
  1551. Msg = pEncodeMessage (SummaryStrItem, FALSE);
  1552. pAppendStringToGrowBuf (StringBuf, Msg);
  1553. FreeText (Msg);
  1554. pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
  1555. }
  1556. }
  1557. if (HtmlFormat) {
  1558. //
  1559. // Add a closing </B> just in case title is missing it.
  1560. //
  1561. pAppendStringToGrowBuf (StringBuf, TEXT("</B>"));
  1562. }
  1563. StringCopy (g_LastMsgGroupBuf, MsgGroup);
  1564. return lineCount != 1;
  1565. }
  1566. PCTSTR
  1567. pEscapeHtmlTitleText (
  1568. IN PCTSTR UnescapedText
  1569. )
  1570. {
  1571. UINT Count;
  1572. PCTSTR p;
  1573. PTSTR Buf;
  1574. PTSTR q;
  1575. CHARTYPE ch;
  1576. BOOL Escape;
  1577. //
  1578. // Escape everything EXCEPT the <B> and </B> tags, which are used in
  1579. // subgroup formatting. This includes ampersands.
  1580. //
  1581. //
  1582. // count all of the tags that we wish to change < into &lt;
  1583. //
  1584. Count = 1;
  1585. p = UnescapedText;
  1586. while (*p) {
  1587. ch = _tcsnextc (p);
  1588. if (ch == TEXT('<')) {
  1589. if (!StringIMatchTcharCount (p, TEXT("</B>"), 4) &&
  1590. !StringIMatchTcharCount (p, TEXT("<B>"), 3)
  1591. ) {
  1592. Count++;
  1593. }
  1594. } else if (ch == TEXT('&')) {
  1595. Count++;
  1596. }
  1597. p = _tcsinc (p);
  1598. }
  1599. //
  1600. // Allocate a dest buffer
  1601. //
  1602. Buf = AllocText (LcharCount (UnescapedText) + (Count * 5) + 1);
  1603. MYASSERT (Buf);
  1604. p = UnescapedText;
  1605. q = Buf;
  1606. //
  1607. // Transfer unescaped text into output buffer, leaving only
  1608. // the tags we want. Convert ampersand into &amp;.
  1609. //
  1610. while (*p) {
  1611. ch = _tcsnextc (p);
  1612. Escape = FALSE;
  1613. if (ch == TEXT('<')) {
  1614. if (!StringIMatchTcharCount (p, TEXT("</B>"), 4) &&
  1615. !StringIMatchTcharCount (p, TEXT("<B>"), 3)
  1616. ) {
  1617. Escape = TRUE;
  1618. }
  1619. } else if (ch == TEXT('&')) {
  1620. Escape = TRUE;
  1621. }
  1622. if (Escape) {
  1623. if (ch == TEXT('<')) {
  1624. StringCopy (q, TEXT("&lt;"));
  1625. } else {
  1626. StringCopy (q, TEXT("&amp;"));
  1627. }
  1628. q = GetEndOfString (q);
  1629. } else {
  1630. _copytchar (q, p);
  1631. q = _tcsinc (q);
  1632. }
  1633. p = _tcsinc (p);
  1634. }
  1635. *q = 0;
  1636. return Buf;
  1637. }
  1638. BOOL
  1639. pGenericItemList (
  1640. IN CALLCONTEXT Context,
  1641. IN OUT PGROWBUFFER StringBuf,
  1642. IN PCTSTR SubGroup,
  1643. IN PCTSTR Message,
  1644. IN DWORD Level,
  1645. IN BOOL HtmlFormat,
  1646. IN OUT PVOID *State,
  1647. IN UINT MsgIdTop,
  1648. IN UINT MsgIdTopHtml,
  1649. IN UINT MsgIdBottom,
  1650. IN UINT MsgIdBottomHtml,
  1651. IN PCTSTR **FormatArgs, OPTIONAL
  1652. IN DWORD Flags,
  1653. IN PMSGGROUP_PROPS Props
  1654. )
  1655. /*++
  1656. Routine Description:
  1657. pGenericItemList formats a group of messages in the following
  1658. format:
  1659. Group
  1660. SubGroup
  1661. <intro>
  1662. Item 1
  1663. Item 2
  1664. Item n
  1665. <conclusion>
  1666. The <intro> and <conclusion> are optional.
  1667. The subgroups are declared in SUBGROUP_LISTS at the top of this
  1668. file.
  1669. Arguments:
  1670. Context - Specifies the way the handler is being called,
  1671. either to initialize, process an item or clean up.
  1672. StringBuf - Specifies the current report. Append text to
  1673. this buffer via the pAppendStringToGrowBuf
  1674. routine.
  1675. SubGroup - Specifies the text of the subgrup
  1676. Message - Specifies the message text
  1677. Level - Specifies the severity level of Message (info, error, etc.)
  1678. HtmlFormat - Specifies TRUE if the text should be written
  1679. with HTML formatting tags, or FALSE if the
  1680. text should be formatted as plain text.
  1681. (See CreateReport comments for HTML tag
  1682. info.)
  1683. State - A pointer to state, defined by the handler. State
  1684. holds an arbitrary 32-bit value that the handler
  1685. maintains. Typically the handler allocates a
  1686. struct when Context is INIT, then uses the struct
  1687. for each PROCESS_ITEM, and finally cleans up the
  1688. allocation when Context is CLEANUP.
  1689. MsgIdTop - Specifies the ID of text that should appear above
  1690. the top of the list. This includes the section
  1691. title. This message ID should contain plain text.
  1692. Zero indicates no text.
  1693. MsgIdTopHtml - Specifies the ID of text that is the same as
  1694. MsgIdTop, except it must be formatted with
  1695. HTML tags. If MsgIdTop is zero, MsgIdTopHtml
  1696. must also be zero.
  1697. MsgIdBottom - Similar to MsgIdTop, except specifies ID of
  1698. text at the bottom of the list. Zero indicates
  1699. no text.
  1700. MsgIdBottomHtml - Specifies the same text as MsgIdBottom, except
  1701. formatted with HTML tags. If MsgIdBottom is
  1702. zero, MsgIdBottomHtml must also be zero.
  1703. FormatArgs - Specifies an optional pointer to an array of 4 pointers,
  1704. each associated with the previous MsgIds (first with MsgIdTop a.s.o.)
  1705. If not NULL, each one points to an array of actual strings to replace
  1706. the placeholders in the message (%1 -> first string in this array a.s.o.)
  1707. Flags - Specifies a list of flags used for formatting (like bold case items)
  1708. Return Value:
  1709. TRUE if the handler was successful, or FALSE if an
  1710. error occurs.
  1711. --*/
  1712. {
  1713. PITEMLIST Items;
  1714. PCTSTR Msg;
  1715. UINT Count;
  1716. UINT u;
  1717. PCTSTR EncodedText;
  1718. PGROWLIST currentList;
  1719. BOOL bMessageItems;
  1720. INT pass;
  1721. PCTSTR altMsg;
  1722. BOOL headerAdded = FALSE;
  1723. BOOL footerAdded = FALSE;
  1724. Level &= REPORTLEVEL_ALL;
  1725. MYASSERT (ONEBITSET (Level));
  1726. Items = *((PITEMLIST *) State);
  1727. switch (Context) {
  1728. case INIT:
  1729. //
  1730. // Allocate grow list to hold all hardware
  1731. //
  1732. MYASSERT (!Items);
  1733. Items = (PITEMLIST) MemAlloc (
  1734. g_hHeap,
  1735. HEAP_ZERO_MEMORY,
  1736. sizeof (ITEMLIST)
  1737. );
  1738. break;
  1739. case PROCESS_ITEM:
  1740. MYASSERT (Items);
  1741. //
  1742. // Add the subgroup to the grow list
  1743. //
  1744. if (HtmlFormat) {
  1745. Msg = pEscapeHtmlTitleText (SubGroup);
  1746. } else {
  1747. Msg = pEncodeMessage (SubGroup, FALSE);
  1748. }
  1749. if (FLAGSET_ENABLE_MESSAGE_ITEMS (Flags) && Message && *Message) {
  1750. GrowListAppendString (&Items->MessageItems, Msg);
  1751. GrowListAppendString (&Items->Messages, Message);
  1752. } else {
  1753. GrowListAppendString (&Items->List, Msg);
  1754. }
  1755. FreeText (Msg);
  1756. break;
  1757. case CLEANUP:
  1758. MYASSERT (Items);
  1759. //
  1760. // Add instructions, then add each item in the grow list
  1761. //
  1762. for (pass = 0; pass < 2; pass++) {
  1763. //
  1764. // are we processing message items this step, or just list entries?
  1765. //
  1766. bMessageItems = FLAGSET_MESSAGE_ITEMS_FIRST (Flags) && pass == 0 ||
  1767. !FLAGSET_MESSAGE_ITEMS_FIRST (Flags) && pass != 0;
  1768. if (bMessageItems) {
  1769. currentList = &Items->MessageItems;
  1770. } else {
  1771. currentList = &Items->List;
  1772. }
  1773. Count = GrowListGetSize (currentList);
  1774. if (Count) {
  1775. if (HtmlFormat) {
  1776. //pAppendStringToGrowBuf (StringBuf, TEXT("<UL>"));
  1777. if (bMessageItems) {
  1778. if (HtmlFormat && FLAGSET_INDENT_MESSAGE (Flags)) {
  1779. pAppendStringToGrowBuf (StringBuf, TEXT("<UL>"));
  1780. }
  1781. }
  1782. }
  1783. if (!headerAdded) {
  1784. headerAdded = TRUE;
  1785. if (MsgIdTop && MsgIdTopHtml) {
  1786. //
  1787. // check if FormatArgs and the corresponding pointer for MsgId are not NULL
  1788. //
  1789. if (FormatArgs && (HtmlFormat ? FormatArgs[1] : FormatArgs[0])) {
  1790. Msg = ParseMessageID (
  1791. HtmlFormat ? MsgIdTopHtml : MsgIdTop,
  1792. HtmlFormat ? FormatArgs[1] : FormatArgs[0]
  1793. );
  1794. } else {
  1795. Msg = GetStringResource (HtmlFormat ? MsgIdTopHtml : MsgIdTop);
  1796. }
  1797. if (Msg) {
  1798. altMsg = Msg;
  1799. if (g_ListFormat) {
  1800. pStartHeaderLine (StringBuf);
  1801. pDumpDwordToGrowBuf (StringBuf, Level);
  1802. //
  1803. // Determine heading from the root group, the subgroup
  1804. // or the message text (depending on macro expansion
  1805. // list flags)
  1806. //
  1807. if (FLAGSET_USESUBGROUP (Flags)) {
  1808. //
  1809. // Get text from the root group
  1810. //
  1811. MYASSERT (!FLAGSET_USEROOT (Flags));
  1812. altMsg = SubGroup;
  1813. } else if (FLAGSET_USEROOT (Flags)) {
  1814. //
  1815. // Get text from the subgroup
  1816. //
  1817. MYASSERT (!FLAGSET_USESUBGROUP (Flags));
  1818. altMsg = Props->Name;
  1819. } else {
  1820. //
  1821. // We assume that the plain text message has a heading
  1822. // that gives the text to put in the list view.
  1823. //
  1824. pCutAfterFirstLine ((PTSTR)altMsg);
  1825. }
  1826. altMsg = pEncodeMessage (altMsg, FALSE);
  1827. }
  1828. if (HtmlFormat) {
  1829. pAppendStringToGrowBuf (StringBuf, altMsg);
  1830. } else {
  1831. pWrapStringToGrowBuf (StringBuf, altMsg, 0, 0);
  1832. }
  1833. if (g_ListFormat) {
  1834. pWriteNewLine (StringBuf);
  1835. FreeText (altMsg);
  1836. }
  1837. FreeStringResource (Msg);
  1838. }
  1839. } else if (g_ListFormat) {
  1840. //
  1841. // No detailed heading; get list view text (just
  1842. // like above). NOTE: there is no message text.
  1843. //
  1844. pStartHeaderLine (StringBuf);
  1845. pDumpDwordToGrowBuf (StringBuf, Level);
  1846. if (FLAGSET_USESUBGROUP (Flags)) {
  1847. MYASSERT (!FLAGSET_USEROOT (Flags));
  1848. altMsg = SubGroup;
  1849. } else {
  1850. MYASSERT (FLAGSET_USEROOT (Flags));
  1851. altMsg = Props->Name;
  1852. }
  1853. pAppendStringToGrowBuf (StringBuf, altMsg);
  1854. pWriteNewLine (StringBuf);
  1855. }
  1856. }
  1857. for (u = 0 ; u < Count ; u++) {
  1858. if (g_ListFormat) {
  1859. pDumpDwordToGrowBuf (StringBuf, Level);
  1860. }
  1861. if (!bMessageItems) {
  1862. if (HtmlFormat) {
  1863. if (FLAGSET_BOLDITEMS(Flags)) {
  1864. pAppendStringToGrowBuf (StringBuf, TEXT("<B>"));
  1865. }
  1866. pAppendStringToGrowBuf (StringBuf, GrowListGetString (currentList, u));
  1867. if (FLAGSET_BOLDITEMS(Flags)) {
  1868. pAppendStringToGrowBuf (StringBuf, TEXT("</B>"));
  1869. }
  1870. pAppendStringToGrowBuf (StringBuf, TEXT("<BR>"));
  1871. } else {
  1872. EncodedText = pEncodeMessage (GrowListGetString (currentList, u), FALSE);
  1873. if (EncodedText) {
  1874. pWrapStringToGrowBuf (StringBuf, EncodedText, 4, 2);
  1875. FreeText (EncodedText);
  1876. }
  1877. }
  1878. } else {
  1879. if (HtmlFormat) {
  1880. if (FLAGSET_BOLDITEMS(Flags)) {
  1881. pAppendStringToGrowBuf (StringBuf, TEXT("<B>"));
  1882. }
  1883. }
  1884. if (HtmlFormat) {
  1885. pAppendStringToGrowBuf (StringBuf, GrowListGetString (currentList, u));
  1886. } else {
  1887. EncodedText = pEncodeMessage (GrowListGetString (currentList, u), FALSE);
  1888. if (EncodedText) {
  1889. pWrapStringToGrowBuf (StringBuf, EncodedText, 0, 0);
  1890. FreeText (EncodedText);
  1891. }
  1892. }
  1893. if (HtmlFormat) {
  1894. if (FLAGSET_BOLDITEMS(Flags)) {
  1895. pAppendStringToGrowBuf (StringBuf, TEXT("</B>"));
  1896. }
  1897. pAppendStringToGrowBuf (StringBuf, TEXT("<BR>"));
  1898. }
  1899. //
  1900. // now add the message itself
  1901. //
  1902. if (!g_ListFormat) {
  1903. EncodedText = pEncodeMessage (GrowListGetString (&Items->Messages, u), HtmlFormat);
  1904. if (EncodedText) {
  1905. pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
  1906. if (HtmlFormat) {
  1907. pAppendStringToGrowBuf (StringBuf, EncodedText);
  1908. if (Count == (u - 1) && FLAGSET_INDENT_MESSAGE (Flags)) {
  1909. pAppendStringToGrowBuf (StringBuf, TEXT("<BR>"));
  1910. } else {
  1911. pAppendStringToGrowBuf (StringBuf, TEXT("<P>"));
  1912. }
  1913. } else {
  1914. pWrapStringToGrowBuf (StringBuf, EncodedText, 4, 0);
  1915. pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
  1916. }
  1917. FreeText (EncodedText);
  1918. }
  1919. }
  1920. }
  1921. pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
  1922. }
  1923. if (!bMessageItems) {
  1924. //
  1925. // Terminate the list
  1926. //
  1927. if (HtmlFormat) {
  1928. if (FLAGSET_BOLDITEMS(Flags)) {
  1929. pAppendStringToGrowBuf (StringBuf, TEXT("</B>"));
  1930. }
  1931. pAppendStringToGrowBuf (StringBuf, TEXT("</UL>"));
  1932. }
  1933. pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
  1934. } else {
  1935. //
  1936. // Terminate the messages
  1937. //
  1938. if (HtmlFormat) {
  1939. //pAppendStringToGrowBuf (StringBuf, TEXT("</UL>"));
  1940. if (FLAGSET_INDENT_MESSAGE (Flags)) {
  1941. pAppendStringToGrowBuf (StringBuf, TEXT("</UL>"));
  1942. }
  1943. }
  1944. pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
  1945. }
  1946. if (!g_ListFormat && !footerAdded) {
  1947. footerAdded = TRUE;
  1948. if (MsgIdBottom && MsgIdBottomHtml) {
  1949. //
  1950. // check if FormatArgs and the corresponding pointer for MsgId are not NULL
  1951. //
  1952. if (FormatArgs && (HtmlFormat ? FormatArgs[3] : FormatArgs[2])) {
  1953. Msg = ParseMessageID (
  1954. HtmlFormat ? MsgIdBottomHtml : MsgIdBottom,
  1955. HtmlFormat ? FormatArgs[3] : FormatArgs[2]
  1956. );
  1957. } else {
  1958. Msg = GetStringResource (HtmlFormat ? MsgIdBottomHtml : MsgIdBottom);
  1959. }
  1960. if (Msg) {
  1961. if (HtmlFormat) {
  1962. pAppendStringToGrowBuf (StringBuf, Msg);
  1963. } else {
  1964. pWrapStringToGrowBuf (StringBuf, Msg, 0, 0);
  1965. }
  1966. FreeStringResource (Msg);
  1967. }
  1968. }
  1969. }
  1970. }
  1971. }
  1972. //
  1973. // Free the grow list
  1974. //
  1975. FreeGrowList (&Items->List);
  1976. FreeGrowList (&Items->MessageItems);
  1977. FreeGrowList (&Items->Messages);
  1978. MemFree (g_hHeap, 0, Items);
  1979. Items = NULL;
  1980. break;
  1981. }
  1982. *((PITEMLIST *) State) = Items;
  1983. return TRUE;
  1984. }
  1985. VOID
  1986. pCleanUpOtherDevices (
  1987. VOID
  1988. )
  1989. {
  1990. MEMDB_ENUM e;
  1991. HASHTABLE Table;
  1992. PCTSTR Str;
  1993. TCHAR ReportRoot[MEMDB_MAX];
  1994. TCHAR Pattern[MEMDB_MAX];
  1995. TCHAR OtherDevices[MEMDB_MAX];
  1996. UINT Bytes;
  1997. PCTSTR p;
  1998. //
  1999. // Prepare the report root Hardware\Incompatible Hardware\Other devices
  2000. //
  2001. Str = GetStringResource (MSG_INCOMPATIBLE_HARDWARE_ROOT);
  2002. MYASSERT (Str);
  2003. if (!Str) {
  2004. return;
  2005. }
  2006. StringCopy (ReportRoot, Str);
  2007. FreeStringResource (Str);
  2008. Str = GetStringResource (MSG_INCOMPATIBLE_HARDWARE_PNP_SUBGROUP);
  2009. MYASSERT (Str);
  2010. if (!Str) {
  2011. return;
  2012. }
  2013. StringCopy (AppendWack (ReportRoot), Str);
  2014. FreeStringResource (Str);
  2015. Str = GetStringResource (MSG_UNKNOWN_DEVICE_CLASS);
  2016. MYASSERT (Str);
  2017. if (!Str) {
  2018. return;
  2019. }
  2020. StringCopy (OtherDevices, Str);
  2021. FreeStringResource (Str);
  2022. //
  2023. // Enumerate the entries in this root
  2024. //
  2025. if (MemDbGetValueEx (&e, MEMDB_CATEGORY_REPORT, ReportRoot, OtherDevices)) {
  2026. Table = HtAlloc();
  2027. do {
  2028. //
  2029. // Add the device name to the table
  2030. //
  2031. HtAddString (Table, e.szName);
  2032. } while (MemDbEnumNextValue (&e));
  2033. //
  2034. // Now search all other classes of the report
  2035. //
  2036. MemDbBuildKey (Pattern, MEMDB_CATEGORY_REPORT, ReportRoot, TEXT("*"), NULL);
  2037. AppendWack (OtherDevices);
  2038. Bytes = (PBYTE) GetEndOfString (OtherDevices) - (PBYTE) OtherDevices;
  2039. if (MemDbEnumFirstValue (&e, Pattern, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) {
  2040. do {
  2041. //
  2042. // Skip "Other devices"
  2043. //
  2044. if (StringIMatchByteCount (e.szName, OtherDevices, Bytes)) {
  2045. continue;
  2046. }
  2047. p = _tcschr (e.szName, TEXT('\\'));
  2048. MYASSERT (p);
  2049. if (p) {
  2050. p = _tcsinc (p);
  2051. if (HtFindString (Table, p)) {
  2052. //
  2053. // This is a match, so remove the Other devices entry
  2054. //
  2055. StringCopy (Pattern, MEMDB_CATEGORY_REPORT);
  2056. StringCopy (AppendWack (Pattern), ReportRoot);
  2057. StringCopy (AppendWack (Pattern), OtherDevices);
  2058. StringCopy (AppendWack (Pattern), p);
  2059. //
  2060. // NOTE: This delete is safe because we know we cannot be
  2061. // enumerating this node.
  2062. //
  2063. MemDbDeleteValue (Pattern);
  2064. }
  2065. }
  2066. } while (MemDbEnumNextValue (&e));
  2067. }
  2068. }
  2069. }
  2070. BOOL
  2071. pAddPnpHardwareToReport (
  2072. IN CALLCONTEXT Context,
  2073. IN PMSGGROUP_PROPS Group,
  2074. IN OUT PGROWBUFFER StringBuf,
  2075. IN PCTSTR SubGroup,
  2076. IN PCTSTR Message,
  2077. IN DWORD Level,
  2078. IN BOOL HtmlFormat,
  2079. IN OUT PVOID *State,
  2080. IN DWORD Arg
  2081. )
  2082. /*++
  2083. Routine Description:
  2084. pAddPnpHardwareToReport formats the incompatible PNP hardware differently
  2085. than the generic lists. The format includes the hardware class, followed
  2086. by device names, which are indented an extra two spaces.
  2087. Arguments:
  2088. Context - Specifies the way the handler is being called, either to
  2089. initialize, process an item or clean up.
  2090. StringBuf - Specifies the current report. Append text to this buffer via
  2091. the pAppendStringToGrowBuf routine.
  2092. Group - Specifies the group properties of this item
  2093. SubGroup - Specifies the message subgroup, and does not include the root
  2094. message group.
  2095. Message - Specifies the message text
  2096. Level - Specifies the severity level of Message (info, error, etc.)
  2097. HtmlFormat - Specifies TRUE if the text should be written with HTML
  2098. formatting tags, or FALSE if the text should be formatted as
  2099. plain text. (See CreateReport comments for HTML tag info.)
  2100. State - Holds a pointer to the formatting state.
  2101. Arg - The DWORD argument from the macro expansion list
  2102. Return Value:
  2103. TRUE if the handler was successful, or FALSE if an error occurs.
  2104. --*/
  2105. {
  2106. PPNPFORMATSTATE FormatState;
  2107. PCTSTR p;
  2108. PCTSTR Msg;
  2109. TCHAR Class[MEMDB_MAX];
  2110. UINT MsgIdTop;
  2111. UINT MsgIdTopHtml;
  2112. UINT MsgIdBottom;
  2113. UINT MsgIdBottomHtml;
  2114. PCTSTR EncodedText;
  2115. TCHAR fmtLine[1024];
  2116. PCTSTR msg;
  2117. Level &= REPORTLEVEL_ALL;
  2118. MYASSERT (ONEBITSET (Level));
  2119. switch (Arg) {
  2120. case 0:
  2121. MsgIdTop = MSG_HARDWARE_UNSUPPORTED_INSTRUCTIONS;
  2122. MsgIdTopHtml = MSG_HARDWARE_UNSUPPORTED_INSTRUCTIONS_HTML;
  2123. MsgIdBottom = MSG_HARDWARE_UNSUPPORTED_INSTRUCTIONS2;
  2124. MsgIdBottomHtml = MSG_HARDWARE_UNSUPPORTED_INSTRUCTIONS_HTML2;
  2125. break;
  2126. default:
  2127. case 1:
  2128. MsgIdTop = MSG_HARDWARE_PNP_INSTRUCTIONS;
  2129. MsgIdTopHtml = MSG_HARDWARE_PNP_INSTRUCTIONS_HTML;
  2130. MsgIdBottom = MSG_HARDWARE_PNP_INSTRUCTIONS2;
  2131. MsgIdBottomHtml = MSG_HARDWARE_PNP_INSTRUCTIONS2_HTML;
  2132. break;
  2133. case 2:
  2134. MsgIdTop = MSG_HARDWARE_REINSTALL_PNP_INSTRUCTIONS;
  2135. MsgIdTopHtml = MSG_HARDWARE_REINSTALL_PNP_INSTRUCTIONS_HTML;
  2136. MsgIdBottom = MSG_HARDWARE_REINSTALL_PNP_INSTRUCTIONS2;
  2137. MsgIdBottomHtml = MSG_HARDWARE_REINSTALL_PNP_INSTRUCTIONS_HTML2;
  2138. //
  2139. // make this a warning icon
  2140. //
  2141. Level = REPORTLEVEL_WARNING;
  2142. break;
  2143. }
  2144. switch (Context) {
  2145. case INIT:
  2146. FormatState = MemAlloc (g_hHeap, 0, sizeof (PNPFORMATSTATE));
  2147. ZeroMemory (FormatState, sizeof (PNPFORMATSTATE));
  2148. *State = FormatState;
  2149. //
  2150. // Special filtering is performed on Other devices, to remove
  2151. // duplicates. If we find that something in Other devices is
  2152. // listed in another device class, we remove the copy in
  2153. // Other devices.
  2154. //
  2155. pCleanUpOtherDevices();
  2156. break;
  2157. case PROCESS_ITEM:
  2158. FormatState = *((PPNPFORMATSTATE *) State);
  2159. p = _tcschr (SubGroup, TEXT('\\'));
  2160. MYASSERT (p);
  2161. if (!p) {
  2162. break;
  2163. }
  2164. StringCopyAB (Class, SubGroup, p);
  2165. p = _tcsinc (p);
  2166. if (!StringMatch (Class, FormatState->LastClass)) {
  2167. //
  2168. // End the previous class
  2169. //
  2170. if (*FormatState->LastClass) {
  2171. if (HtmlFormat) {
  2172. pAppendStringToGrowBuf (StringBuf, TEXT("<BR></UL>\r\n"));
  2173. } else {
  2174. pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
  2175. }
  2176. } else if (MsgIdTop) {
  2177. //
  2178. // The very first message gets a heading
  2179. //
  2180. Msg = GetStringResource (HtmlFormat ? MsgIdTopHtml : MsgIdTop);
  2181. if (Msg) {
  2182. if (g_ListFormat) {
  2183. pStartHeaderLine (StringBuf);
  2184. pDumpDwordToGrowBuf (StringBuf, Level);
  2185. pCutAfterFirstLine ((PTSTR)Msg);
  2186. msg = pEncodeMessage (Msg, FALSE);
  2187. }
  2188. if (HtmlFormat) {
  2189. pAppendStringToGrowBuf (StringBuf, Msg);
  2190. } else {
  2191. pWrapStringToGrowBuf (StringBuf, Msg, 0, 0);
  2192. }
  2193. if (g_ListFormat) {
  2194. pWriteNewLine (StringBuf);
  2195. FreeText (msg);
  2196. }
  2197. FreeStringResource (Msg);
  2198. }
  2199. }
  2200. //
  2201. // Begin a new class
  2202. //
  2203. StringCopy (FormatState->LastClass, Class);
  2204. if (!g_ListFormat) {
  2205. if (HtmlFormat) {
  2206. pAppendStringToGrowBuf (StringBuf, TEXT("<UL><B>"));
  2207. pAppendStringToGrowBuf (StringBuf, Class);
  2208. pAppendStringToGrowBuf (StringBuf, TEXT("</B><BR>"));
  2209. } else {
  2210. EncodedText = pEncodeMessage (Class, FALSE);
  2211. if (EncodedText) {
  2212. pWrapStringToGrowBuf (StringBuf, EncodedText, 4, 2);
  2213. FreeText (EncodedText);
  2214. }
  2215. }
  2216. }
  2217. pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
  2218. }
  2219. //
  2220. // Add the device name
  2221. //
  2222. if (!g_ListFormat) {
  2223. if (HtmlFormat) {
  2224. pAppendStringToGrowBuf (StringBuf, TEXT("&nbsp;&nbsp;"));
  2225. }
  2226. if (HtmlFormat) {
  2227. pAppendStringToGrowBuf (StringBuf, p);
  2228. pAppendStringToGrowBuf (StringBuf, TEXT("<BR>"));
  2229. } else {
  2230. EncodedText = pEncodeMessage (p, HtmlFormat);
  2231. if (EncodedText) {
  2232. pWrapStringToGrowBuf (StringBuf, EncodedText, 6, 2);
  2233. FreeText (EncodedText);
  2234. }
  2235. }
  2236. } else {
  2237. pDumpDwordToGrowBuf (StringBuf, Level);
  2238. EncodedText = pEncodeMessage (p, FALSE);
  2239. _sntprintf (fmtLine, 1024, TEXT("%s (%s)"), EncodedText ? EncodedText : p, Class);
  2240. FreeText (EncodedText);
  2241. pAppendStringToGrowBuf (StringBuf, fmtLine);
  2242. }
  2243. pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
  2244. break;
  2245. case CLEANUP:
  2246. FormatState = *((PPNPFORMATSTATE *) State);
  2247. if (FormatState) {
  2248. if (!g_ListFormat) {
  2249. if (*FormatState->LastClass) {
  2250. if (HtmlFormat) {
  2251. pAppendStringToGrowBuf (StringBuf, TEXT("</UL>\r\n"));
  2252. } else {
  2253. pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
  2254. }
  2255. }
  2256. //
  2257. // Append optional footer text
  2258. //
  2259. if (MsgIdBottom) {
  2260. Msg = GetStringResource (HtmlFormat ? MsgIdBottomHtml : MsgIdBottom);
  2261. if (Msg) {
  2262. if (HtmlFormat) {
  2263. pAppendStringToGrowBuf (StringBuf, Msg);
  2264. } else {
  2265. pWrapStringToGrowBuf (StringBuf, Msg, 0, 0);
  2266. }
  2267. FreeStringResource (Msg);
  2268. }
  2269. }
  2270. }
  2271. MemFree (g_hHeap, 0, FormatState);
  2272. }
  2273. break;
  2274. }
  2275. return TRUE;
  2276. }
  2277. PCTSTR
  2278. pFindEndOfTag (
  2279. IN PCTSTR Tag
  2280. )
  2281. {
  2282. BOOL quoteMode;
  2283. CHARTYPE ch;
  2284. quoteMode = FALSE;
  2285. do {
  2286. Tag = _tcsinc (Tag);
  2287. ch = _tcsnextc (Tag);
  2288. if (ch == TEXT('\"')) {
  2289. quoteMode = !quoteMode;
  2290. } else if (!quoteMode && ch == TEXT('>')) {
  2291. break;
  2292. }
  2293. } while (ch);
  2294. return Tag;
  2295. }
  2296. PCTSTR
  2297. pEncodeMessage (
  2298. IN PCTSTR Message,
  2299. IN BOOL HtmlFormat
  2300. )
  2301. /*++
  2302. Routine Description:
  2303. pEncodeMessage removes the unsupported HTML tags from Message, and returns a
  2304. text pool string. If plain text is required, all HTML tags are removed from
  2305. Message, and all HTML-escaped characters are converted into normal text.
  2306. If Message contains leading space, and the caller wants an HTML return
  2307. string, then the leading space is converted in to non-breaking space
  2308. characters.
  2309. Arguments:
  2310. Message - Specifies the text to convert
  2311. HtmlFormat - Specifies TRUE if the return value should be in HTML, or FALSE
  2312. if it should be in plain text.
  2313. Return Value:
  2314. A pointer to a text pool allocated string. The caller must
  2315. free this pointer with FreeText.
  2316. --*/
  2317. {
  2318. PCTSTR p, r;
  2319. PCTSTR mnemonic;
  2320. PTSTR q;
  2321. BOOL processed;
  2322. PTSTR Buf;
  2323. CHARTYPE ch;
  2324. BOOL Copy;
  2325. PCTSTR semicolon;
  2326. PCTSTR closeBracket;
  2327. PCTSTR endOfMnemonic;
  2328. UINT leadingSpaces;
  2329. leadingSpaces = 0;
  2330. if (HtmlFormat) {
  2331. p = Message;
  2332. while (*p) {
  2333. if (_istspace (_tcsnextc (Message))) {
  2334. leadingSpaces++;
  2335. } else if (_tcsnextc (Message) == TEXT('<')) {
  2336. // ignore html tags
  2337. p = pFindEndOfTag (p);
  2338. } else {
  2339. // first printable character -- stop
  2340. break;
  2341. }
  2342. p = _tcsinc (p);
  2343. }
  2344. }
  2345. //
  2346. // Allocate an output buffer. AllocText takes the number of logical
  2347. // characters as input; the terminating nul is a character.
  2348. //
  2349. Buf = AllocText (LcharCount (Message) + (leadingSpaces * 6) + 1);
  2350. if (!Buf) {
  2351. return NULL;
  2352. }
  2353. p = Message;
  2354. q = Buf;
  2355. while (*p) {
  2356. ch = _tcsnextc (p);
  2357. processed = FALSE;
  2358. //
  2359. // If caller wants plain text, remove HTML encodings and tags
  2360. //
  2361. if (!HtmlFormat) {
  2362. if (ch == TEXT('&')) {
  2363. //
  2364. // Convert ampersand-encoded characters
  2365. //
  2366. semicolon = _tcschr (p + 1, TEXT(';'));
  2367. mnemonic = p + 1;
  2368. if (semicolon) {
  2369. processed = TRUE;
  2370. if (StringMatchAB (TEXT("lt"), mnemonic, semicolon)) {
  2371. *q++ = TEXT('<');
  2372. } else if (StringMatchAB (TEXT("gt"), mnemonic, semicolon)) {
  2373. *q++ = TEXT('>');
  2374. } else if (StringMatchAB (TEXT("amp"), mnemonic, semicolon)) {
  2375. *q++ = TEXT('&');
  2376. } else if (StringMatchAB (TEXT("quot"), mnemonic, semicolon)) {
  2377. *q++ = TEXT('\"');
  2378. } else if (StringMatchAB (TEXT("apos"), mnemonic, semicolon)) {
  2379. *q++ = TEXT('\'');
  2380. } else if (StringMatchAB (TEXT("nbsp"), mnemonic, semicolon)) {
  2381. *q++ = TEXT(' ');
  2382. } else {
  2383. processed = FALSE;
  2384. }
  2385. if (processed) {
  2386. // move p to the last character of the mnemonic
  2387. p = semicolon;
  2388. }
  2389. }
  2390. } else if (ch == TEXT('<')) {
  2391. //
  2392. // Hop over HTML tag and its arguments, leaving p on the
  2393. // closing angle bracket or terminating nul
  2394. //
  2395. p = pFindEndOfTag (p);
  2396. processed = TRUE;
  2397. }
  2398. }
  2399. //
  2400. // If the caller wants an HTML return string, strip out all
  2401. // unsupported tags. Convert leading spaces into &nbsp;.
  2402. //
  2403. else {
  2404. if (ch == TEXT('<')) {
  2405. closeBracket = pFindEndOfTag (p);
  2406. mnemonic = p + 1;
  2407. endOfMnemonic = p;
  2408. while (!_istspace (_tcsnextc (endOfMnemonic))) {
  2409. endOfMnemonic = _tcsinc (endOfMnemonic);
  2410. if (endOfMnemonic == closeBracket) {
  2411. break;
  2412. }
  2413. }
  2414. //
  2415. // if a known good tag, copy it, otherwise skip it
  2416. //
  2417. if (StringIMatchAB (TEXT("A"), mnemonic, endOfMnemonic) ||
  2418. StringIMatchAB (TEXT("/A"), mnemonic, endOfMnemonic) ||
  2419. StringIMatchAB (TEXT("P"), mnemonic, endOfMnemonic) ||
  2420. StringIMatchAB (TEXT("/P"), mnemonic, endOfMnemonic) ||
  2421. StringIMatchAB (TEXT("BR"), mnemonic, endOfMnemonic) ||
  2422. StringIMatchAB (TEXT("/BR"), mnemonic, endOfMnemonic)
  2423. ) {
  2424. StringCopyAB (q, p, _tcsinc (closeBracket));
  2425. q = GetEndOfString (q);
  2426. processed = TRUE;
  2427. }
  2428. p = closeBracket;
  2429. } else if (leadingSpaces && _istspace (ch)) {
  2430. StringCopy (q, TEXT("&nbsp;"));
  2431. q = GetEndOfString (q);
  2432. processed = TRUE;
  2433. } else {
  2434. // first printable character -- turn off leading space conversion
  2435. leadingSpaces = 0;
  2436. }
  2437. }
  2438. //
  2439. // If not processed, copy the character
  2440. //
  2441. if (!processed) {
  2442. _copytchar (q, p);
  2443. q = _tcsinc (q);
  2444. }
  2445. if (*p) {
  2446. p = _tcsinc (p);
  2447. }
  2448. }
  2449. *q = 0;
  2450. return Buf;
  2451. }
  2452. BOOL
  2453. pDefaultHandler (
  2454. IN CALLCONTEXT Context,
  2455. IN PMSGGROUP_PROPS Group,
  2456. IN OUT PGROWBUFFER StringBuf,
  2457. IN PCTSTR SubGroup,
  2458. IN PCTSTR Message,
  2459. IN DWORD Level,
  2460. IN BOOL HtmlFormat,
  2461. IN OUT PVOID *State,
  2462. IN DWORD Arg
  2463. )
  2464. /*++
  2465. Routine Description:
  2466. pDefaultHandler formats all messages that are not handled
  2467. in some other way. The formatting is simple -- the message group
  2468. is added to the report in bold, and the text is placed
  2469. below the message group.
  2470. All text for the default handler appears at the end of the
  2471. incompatibility report.
  2472. Arguments:
  2473. Context - Specifies the way the handler is being called, either to
  2474. initialize, process an item or clean up.
  2475. Group - Specifies the group properties of this item
  2476. StringBuf - Specifies the current report. Append text to this buffer via
  2477. the pAppendStringToGrowBuf routine.
  2478. SubGroup - Specifies the message subgroup, and does not include the root
  2479. message group.
  2480. Message - Specifies the message text
  2481. Level - Specifies the severity level of Message (info, error, etc.)
  2482. HtmlFormat - Specifies TRUE if the text should be written with HTML
  2483. formatting tags, or FALSE if the text should be formatted as
  2484. plain text. (See CreateReport comments for HTML tag info.)
  2485. State - A pointer to state, defined by the handler. State holds an arbitrary
  2486. 32-bit value that the handler maintains. Typically the handler
  2487. allocates a struct when Context is INIT, then uses the struct for
  2488. each PROCESS_ITEM, and finally cleans up the allocation when Context
  2489. is CLEANUP.
  2490. Arg - The DWORD argument from the macro expansion list
  2491. Return Value:
  2492. TRUE if the handler was successful, or FALSE if an error occurs.
  2493. --*/
  2494. {
  2495. PCTSTR EncodedMessage;
  2496. BOOL indent;
  2497. Level &= REPORTLEVEL_ALL;
  2498. MYASSERT (ONEBITSET (Level));
  2499. if (Context != PROCESS_ITEM) {
  2500. return TRUE;
  2501. }
  2502. //
  2503. // Build message group string
  2504. //
  2505. if (HtmlFormat) {
  2506. pAppendStringToGrowBuf (StringBuf, TEXT("<UL>"));
  2507. }
  2508. indent = pAddMsgGroupString (StringBuf, Group, SubGroup, HtmlFormat, Level);
  2509. if (Message) {
  2510. //
  2511. // Add details
  2512. //
  2513. if (!g_ListFormat) {
  2514. if (HtmlFormat) {
  2515. if (indent) {
  2516. pAppendStringToGrowBuf (StringBuf, TEXT("<UL>"));
  2517. }
  2518. } else {
  2519. pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
  2520. }
  2521. EncodedMessage = pEncodeMessage (Message, HtmlFormat);
  2522. if (EncodedMessage) {
  2523. if (HtmlFormat) {
  2524. pAppendStringToGrowBuf (StringBuf, EncodedMessage);
  2525. pAppendStringToGrowBuf (StringBuf, TEXT("<BR>\r\n"));
  2526. } else {
  2527. pWrapStringToGrowBuf (StringBuf, EncodedMessage, 4, 0);
  2528. }
  2529. FreeText (EncodedMessage);
  2530. }
  2531. if (HtmlFormat) {
  2532. if (indent) {
  2533. pAppendStringToGrowBuf (StringBuf, TEXT("</UL>"));
  2534. }
  2535. } else {
  2536. pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
  2537. }
  2538. }
  2539. }
  2540. if (HtmlFormat) {
  2541. pAppendStringToGrowBuf (StringBuf, TEXT("</UL>"));
  2542. }
  2543. pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
  2544. return TRUE;
  2545. }
  2546. BOOL
  2547. pProcessGenericList (
  2548. IN OUT PGROWBUFFER StringBuf,
  2549. IN BOOL HtmlFormat,
  2550. IN PMSGGROUP_PROPS Props,
  2551. IN PGENERIC_LIST List,
  2552. IN DWORD LevelMask
  2553. )
  2554. /*++
  2555. Routine Description:
  2556. pProcessGenericList calls the pGenericItemList handler for every message in the
  2557. message group/subgroup.
  2558. An example of a group:
  2559. Installation Notes
  2560. An example of a subgroup:
  2561. Name Changes
  2562. The combined name, as stored in memdb:
  2563. Installation Notes\Name Changes
  2564. All messages for a group are processed if the message group name (specified
  2565. by Props->Name) and subgroup name (specified by List->SubGroupStr) are identical.
  2566. If they are different, only the subgroup messages are processed, providing the
  2567. capatiblity to format a single message group in multiple ways.
  2568. Arguments:
  2569. StringBuf - Specifies the GROWBUFFER holding the current report.
  2570. Receives all additional text.
  2571. HtmlFormat - Specifies TRUE if the caller wants the text to
  2572. contain HTML characters, or FALSE if not.
  2573. Props - Specifies the poperties of the group to process.
  2574. List - Specifies the generic list attributes, including the subgroup name and
  2575. intro/conclusion text ids.
  2576. LevelMask - Specifies the severity mask of the messages to process
  2577. Return Value:
  2578. TRUE if at least one message was processed, FALSE otherwise.
  2579. --*/
  2580. {
  2581. REPORT_MESSAGE_ENUM e;
  2582. TCHAR Node[MEMDB_MAX];
  2583. PVOID State = NULL;
  2584. BOOL result = FALSE;
  2585. MYASSERT (List->SubGroupStr);
  2586. if (!StringMatch (List->SubGroupStr, Props->Name)) {
  2587. wsprintf (Node, TEXT("%s\\%s"), Props->Name, List->SubGroupStr);
  2588. } else {
  2589. StringCopy (Node, List->SubGroupStr);
  2590. }
  2591. if (EnumFirstMessage (&e, Node, LevelMask)) {
  2592. result = TRUE;
  2593. pGenericItemList (
  2594. INIT,
  2595. StringBuf,
  2596. List->SubGroupStr,
  2597. NULL,
  2598. e.e.UserFlags,
  2599. HtmlFormat,
  2600. &State,
  2601. List->IntroId,
  2602. List->IntroIdHtml,
  2603. List->ConclusionId,
  2604. List->ConclusionIdHtml,
  2605. List->FormatArgs,
  2606. List->Flags,
  2607. Props
  2608. );
  2609. do {
  2610. pGenericItemList (
  2611. PROCESS_ITEM,
  2612. StringBuf,
  2613. e.MsgGroup,
  2614. e.Message,
  2615. e.e.UserFlags,
  2616. HtmlFormat,
  2617. &State,
  2618. List->IntroId,
  2619. List->IntroIdHtml,
  2620. List->ConclusionId,
  2621. List->ConclusionIdHtml,
  2622. List->FormatArgs,
  2623. List->Flags,
  2624. Props
  2625. );
  2626. } while (EnumNextMessage (&e));
  2627. pGenericItemList (
  2628. CLEANUP,
  2629. StringBuf,
  2630. List->SubGroupStr,
  2631. NULL,
  2632. e.e.UserFlags,
  2633. HtmlFormat,
  2634. &State,
  2635. List->IntroId,
  2636. List->IntroIdHtml,
  2637. List->ConclusionId,
  2638. List->ConclusionIdHtml,
  2639. List->FormatArgs,
  2640. List->Flags,
  2641. Props
  2642. );
  2643. }
  2644. return result;
  2645. }
  2646. BOOL
  2647. pProcessMessageHandler (
  2648. IN OUT PGROWBUFFER StringBuf,
  2649. IN BOOL HtmlFormat,
  2650. IN PMSGGROUP_PROPS Props,
  2651. IN PHANDLER_LIST Handler,
  2652. IN DWORD LevelMask
  2653. )
  2654. /*++
  2655. Routine Description:
  2656. pProcessMessageHandler calls the handler for every message in the
  2657. message group/subgroup that the function wants to handle.
  2658. An example of a group:
  2659. Installation Notes
  2660. An example of a subgroup:
  2661. Name Changes
  2662. The combined name, as stored in memdb:
  2663. Installation Notes\Name Changes
  2664. All messages for a group are processed if the message group name (specified
  2665. by Props->Name) and subgroup name (specified by List->SubGroupStr) are identical.
  2666. If they are different, only the subgroup messages are processed, providing the
  2667. capatiblity to format a single message group in multiple ways.
  2668. Arguments:
  2669. StringBuf - Specifies the GROWBUFFER holding the current report.
  2670. Receives all additional text.
  2671. HtmlFormat - Specifies TRUE if the caller wants the text to
  2672. contain HTML characters, or FALSE if not.
  2673. Props - Specifies the poperties of the group to process.
  2674. Handler - Specifies the handler attributes, including the function name
  2675. and subgroup name.
  2676. LevelMask - Specifies the severity of the messages to include, or 0 to
  2677. include all messages
  2678. Return Value:
  2679. TRUE if at least one message was processed, FALSE otherwise.
  2680. --*/
  2681. {
  2682. REPORT_MESSAGE_ENUM e;
  2683. TCHAR Node[MEMDB_MAX];
  2684. BOOL result = FALSE;
  2685. MYASSERT (Handler->SubGroupStr);
  2686. MYASSERT (Handler->Fn);
  2687. if (!StringMatch (Handler->SubGroupStr, Props->Name)) {
  2688. wsprintf (Node, TEXT("%s\\%s"), Props->Name, Handler->SubGroupStr);
  2689. } else {
  2690. StringCopy (Node, Handler->SubGroupStr);
  2691. }
  2692. if (EnumFirstMessage (&e, Node, LevelMask)) {
  2693. result = TRUE;
  2694. Handler->Fn (
  2695. INIT,
  2696. Props,
  2697. StringBuf,
  2698. NULL,
  2699. NULL,
  2700. e.e.UserFlags,
  2701. HtmlFormat,
  2702. &Handler->State,
  2703. Handler->Arg
  2704. );
  2705. do {
  2706. Handler->Fn (
  2707. PROCESS_ITEM,
  2708. Props,
  2709. StringBuf,
  2710. e.MsgGroup,
  2711. e.Message,
  2712. e.e.UserFlags,
  2713. HtmlFormat,
  2714. &Handler->State,
  2715. Handler->Arg
  2716. );
  2717. } while (EnumNextMessage (&e));
  2718. Handler->Fn (
  2719. CLEANUP,
  2720. Props,
  2721. StringBuf,
  2722. NULL,
  2723. NULL,
  2724. e.e.UserFlags,
  2725. HtmlFormat,
  2726. &Handler->State,
  2727. Handler->Arg
  2728. );
  2729. }
  2730. return result;
  2731. }
  2732. BOOL
  2733. pAddMsgGroupToReport (
  2734. IN OUT PGROWBUFFER StringBuf,
  2735. IN BOOL HtmlFormat,
  2736. IN PMSGGROUP_PROPS Props,
  2737. IN DWORD LevelMask
  2738. )
  2739. /*++
  2740. Routine Description:
  2741. pAddMsgGroupToReport adds messages for the specified message group
  2742. to the report. It first enumerates all messages in the group,
  2743. and each one that has a handler is processed first. After that,
  2744. any remaining messages are put in an "other" section.
  2745. Arguments:
  2746. StringBuf - Specifies the GROWBUFFER holding the current report.
  2747. Receives all additional text.
  2748. HtmlFormat - Specifies TRUE if the caller wants the text to
  2749. contain HTML characters, or FALSE if not.
  2750. Props - Specifies the poperties of the group to process.
  2751. LevelMask - Specifies a mask to restrict processing, or 0 to
  2752. process all messages
  2753. Return Value:
  2754. TRUE if at least one message was added, FALSE otherwise.
  2755. --*/
  2756. {
  2757. REPORT_MESSAGE_ENUM e;
  2758. UINT Pass;
  2759. BOOL AddOtherText;
  2760. PHANDLER_LIST Handler;
  2761. PGENERIC_LIST List;
  2762. BOOL result = FALSE;
  2763. //
  2764. // Check to see if there is a handler for all messages in the message
  2765. // group.
  2766. //
  2767. List = pSearchForGenericList (Props->Name);
  2768. if (List) {
  2769. return pProcessGenericList (
  2770. StringBuf,
  2771. HtmlFormat,
  2772. Props,
  2773. List,
  2774. LevelMask
  2775. );
  2776. }
  2777. Handler = pSearchForMsgGroupHandler (Props->Name);
  2778. if (Handler->Fn != pDefaultHandler) {
  2779. return pProcessMessageHandler (
  2780. StringBuf,
  2781. HtmlFormat,
  2782. Props,
  2783. Handler,
  2784. LevelMask
  2785. );
  2786. }
  2787. //
  2788. // Since there is no handler for all messages, call the handlers for
  2789. // subgroups, then call the default handler for the unhandled messages.
  2790. //
  2791. // Two passes, one for the handled messages, and another for the
  2792. // unhandled messages.
  2793. //
  2794. for (Pass = 1 ; Pass <= 2 ; Pass++) {
  2795. AddOtherText = (Pass == 2);
  2796. //
  2797. // Enumerate all messages in the group
  2798. //
  2799. Handler = NULL;
  2800. List = NULL;
  2801. if (EnumFirstMessage (&e, Props->Name, LevelMask)) {
  2802. result = TRUE;
  2803. do {
  2804. //
  2805. // Is this the same message group that was used last time through
  2806. // the loop? If so, continue enumerating, because this
  2807. // message group is done.
  2808. //
  2809. if ((Handler && pIsThisTheHandler (e.MsgGroup, Handler)) ||
  2810. (List && pIsThisTheGenericList (e.MsgGroup, List))
  2811. ) {
  2812. continue;
  2813. }
  2814. //
  2815. // Is this group a generic list? For Pass 1, we add the list; for
  2816. // pass 2 we just continue;
  2817. //
  2818. List = pSearchForGenericList (e.MsgGroup);
  2819. if (List) {
  2820. Handler = NULL;
  2821. if (Pass == 1) {
  2822. pProcessGenericList (
  2823. StringBuf,
  2824. HtmlFormat,
  2825. Props,
  2826. List,
  2827. LevelMask
  2828. );
  2829. }
  2830. continue;
  2831. }
  2832. //
  2833. // Does this group have a handler? Pass 1 requires one, and
  2834. // pass 2 requires no handler.
  2835. //
  2836. Handler = pSearchForMsgGroupHandler (e.MsgGroup);
  2837. if (Handler->Fn == pDefaultHandler) {
  2838. if (Pass == 1) {
  2839. continue;
  2840. }
  2841. } else if (Pass == 2) {
  2842. continue;
  2843. }
  2844. //
  2845. // Add the "other" intro?
  2846. //
  2847. if (!g_ListFormat) {
  2848. if (AddOtherText) {
  2849. AddOtherText = FALSE;
  2850. if (HtmlFormat && Props->OtherIdHtmlStr) {
  2851. pAppendStringToGrowBuf (StringBuf, Props->OtherIdHtmlStr);
  2852. } else if (!HtmlFormat && Props->OtherIdStr) {
  2853. pWrapStringToGrowBuf (StringBuf, Props->OtherIdStr, 0, 0);
  2854. }
  2855. }
  2856. }
  2857. //
  2858. // If handler exists, process all messages
  2859. //
  2860. if (Handler->Fn != pDefaultHandler) {
  2861. pProcessMessageHandler (
  2862. StringBuf,
  2863. HtmlFormat,
  2864. Props,
  2865. Handler,
  2866. LevelMask
  2867. );
  2868. } else {
  2869. //
  2870. // Call the default handler
  2871. //
  2872. Handler->Fn (
  2873. PROCESS_ITEM,
  2874. Props,
  2875. StringBuf,
  2876. e.MsgGroup,
  2877. e.Message,
  2878. e.e.UserFlags,
  2879. HtmlFormat,
  2880. &Handler->State,
  2881. Handler->Arg
  2882. );
  2883. }
  2884. } while (EnumNextMessage (&e));
  2885. }
  2886. }
  2887. return result;
  2888. }
  2889. typedef enum {
  2890. FORMAT_HTML,
  2891. FORMAT_PLAIN_TEXT,
  2892. FORMAT_LIST
  2893. } REPORTFORMAT;
  2894. BOOL
  2895. pCreateReportTextWorker (
  2896. IN REPORTFORMAT Format,
  2897. IN UINT TotalCols, OPTIONAL
  2898. IN DWORD LevelMask
  2899. )
  2900. /*++
  2901. Routine Description:
  2902. pCreateReportTextWorker prepares a buffer for the incompatibility report
  2903. text. It enumerates messages that match the severity requested, then
  2904. performs special formatting, and dumps the remaining incompatibilities to
  2905. a global buffer. The buffer is then used to display, print or save.
  2906. A subset of HTML is supported if HtmlFormat is TRUE. Specifically, the
  2907. following tags are inserted into the report text:
  2908. <B> - Bold
  2909. <U> - Underline
  2910. <HR> - Line break
  2911. <UL> - Indented list
  2912. No other HTML tags are recognized.
  2913. The caller must free the return buffer by calling FreeReportText. Also,
  2914. CreateReportText uses a single global buffer and therefore cannot be called
  2915. more than once. Instead, the caller must use the text and/or duplicate it,
  2916. then call FreeReportText, before calling CreateReportText a second time.
  2917. Arguments:
  2918. Format - Specifies which type of report to generate
  2919. TotalCols - Specifies the number of cols for a plain text report
  2920. LevelMask - Specifies which severity levels to add (verbose, error,
  2921. blocking) or zero for all levels
  2922. Return Value:
  2923. TRUE if at least one message was added, FALSE otherwise.
  2924. --*/
  2925. {
  2926. REPORT_MESSAGE_ENUM MsgGroups;
  2927. PMSGGROUP_PROPS Props;
  2928. REPORT_MESSAGE_ENUM e;
  2929. PTSTR TempStr;
  2930. BOOL HtmlFormat;
  2931. UINT oldEnd;
  2932. BOOL result = FALSE;
  2933. if (!LevelMask) {
  2934. return FALSE;
  2935. }
  2936. HtmlFormat = (Format == FORMAT_HTML);
  2937. //
  2938. // Add report details
  2939. //
  2940. if (EnumFirstRootMsgGroup (&e, LevelMask)) {
  2941. do {
  2942. g_LastMsgGroupBuf[0] = 0;
  2943. //
  2944. // Obtain message group properties. If no properties exist, then ignore the message.
  2945. //
  2946. Props = pFindMsgGroupStruct (e.MsgGroup);
  2947. if (!Props) {
  2948. DEBUGMSG ((DBG_WHOOPS, "Group %s is not supported as a root", e.MsgGroup));
  2949. continue;
  2950. }
  2951. //
  2952. // Add bookmark for base group
  2953. //
  2954. oldEnd = g_ReportString.End;
  2955. if (!g_ListFormat) {
  2956. if (HtmlFormat) {
  2957. TempStr = AllocText (6 + LcharCount (e.MsgGroup) + 1);
  2958. if (TempStr) {
  2959. wsprintf (TempStr, TEXT("<A NAME=\"%s\">"), e.MsgGroup);
  2960. pAppendStringToGrowBuf (&g_ReportString, TempStr);
  2961. FreeText (TempStr);
  2962. }
  2963. }
  2964. //
  2965. // Is there an intro string? If so, add it.
  2966. //
  2967. if (HtmlFormat && Props->IntroIdHtmlStr) {
  2968. pAppendStringToGrowBuf (&g_ReportString, Props->IntroIdHtmlStr);
  2969. } else if (!HtmlFormat && Props->IntroIdStr) {
  2970. pWrapStringToGrowBuf (&g_ReportString, Props->IntroIdStr, 0, 0);
  2971. }
  2972. }
  2973. //
  2974. // Add all messages in this group to the report
  2975. //
  2976. if (!pAddMsgGroupToReport (
  2977. &g_ReportString,
  2978. HtmlFormat,
  2979. Props,
  2980. LevelMask
  2981. )) {
  2982. //
  2983. // No messages -- back out heading text
  2984. //
  2985. if (oldEnd) {
  2986. g_ReportString.End = 0;
  2987. } else {
  2988. FreeGrowBuffer (&g_ReportString);
  2989. }
  2990. } else {
  2991. result = TRUE;
  2992. }
  2993. } while (EnumNextRootMsgGroup (&e));
  2994. }
  2995. return result;
  2996. }
  2997. VOID
  2998. FreeReportText (
  2999. VOID
  3000. )
  3001. /*++
  3002. Routine Description:
  3003. FreeReportText frees the memory allocated by CreateReportText.
  3004. Arguments:
  3005. none
  3006. Return Value:
  3007. none
  3008. --*/
  3009. {
  3010. FreeGrowBuffer (&g_ReportString);
  3011. }
  3012. VOID
  3013. pAddHeadingToReport (
  3014. IN OUT PGROWBUFFER Buffer,
  3015. IN BOOL HtmlFormat,
  3016. IN UINT PlainTextId,
  3017. IN UINT HtmlId
  3018. )
  3019. {
  3020. PCTSTR msg;
  3021. if (HtmlFormat) {
  3022. msg = GetStringResource (HtmlId);
  3023. pAppendStringToGrowBuf (Buffer, msg);
  3024. } else {
  3025. msg = GetStringResource (PlainTextId);
  3026. pWrapStringToGrowBuf (Buffer, msg, 0, 0);
  3027. }
  3028. FreeStringResource (msg);
  3029. }
  3030. VOID
  3031. pMoveReportTextToGrowBuf (
  3032. IN OUT PGROWBUFFER SourceBuffer,
  3033. IN OUT PGROWBUFFER DestBuffer
  3034. )
  3035. {
  3036. UINT end;
  3037. UINT trim = 0;
  3038. end = DestBuffer->End;
  3039. if (end) {
  3040. trim = sizeof (TCHAR);
  3041. end -= trim;
  3042. }
  3043. if (!GrowBuffer (DestBuffer, SourceBuffer->End - trim)) {
  3044. return;
  3045. }
  3046. CopyMemory (DestBuffer->Buf + end, SourceBuffer->Buf, SourceBuffer->End);
  3047. SourceBuffer->End = 0;
  3048. }
  3049. VOID
  3050. pAddTocEntry (
  3051. IN OUT PGROWBUFFER Buffer,
  3052. IN PCTSTR Bookmark,
  3053. IN UINT MessageId,
  3054. IN BOOL HtmlFormat
  3055. )
  3056. {
  3057. PCTSTR msg;
  3058. msg = GetStringResource (MessageId);
  3059. if (HtmlFormat) {
  3060. pAppendStringToGrowBuf (Buffer, TEXT("<A HREF=\"#"));
  3061. pAppendStringToGrowBuf (Buffer, Bookmark);
  3062. pAppendStringToGrowBuf (Buffer, TEXT("\">"));
  3063. pAppendStringToGrowBuf (Buffer, msg);
  3064. pAppendStringToGrowBuf (Buffer, TEXT("</A><BR>\r\n"));
  3065. } else {
  3066. pWrapStringToGrowBuf (Buffer, msg, 8, 2);
  3067. pAppendStringToGrowBuf (Buffer, TEXT("\r\n"));
  3068. }
  3069. }
  3070. BOOL
  3071. AreThereAnyBlockingIssues(
  3072. VOID
  3073. )
  3074. {
  3075. REPORT_MESSAGE_ENUM e;
  3076. if(EnumFirstRootMsgGroup (&e, REPORTLEVEL_BLOCKING)) {
  3077. return TRUE;
  3078. }
  3079. return FALSE;
  3080. }
  3081. PCTSTR
  3082. CreateReportText (
  3083. IN BOOL HtmlFormat,
  3084. IN UINT TotalCols,
  3085. IN DWORD Level,
  3086. IN BOOL ListFormat
  3087. )
  3088. {
  3089. REPORTFORMAT format;
  3090. GROWBUFFER fullReport = GROWBUF_INIT;
  3091. PCTSTR argArray[1];
  3092. PCTSTR msg;
  3093. PCTSTR subMsg = NULL;
  3094. REPORT_MESSAGE_ENUM msgGroups;
  3095. PBYTE dest;
  3096. DWORD levelMask;
  3097. UINT end;
  3098. BOOL blocking = FALSE;
  3099. BOOL warning = FALSE;
  3100. BOOL info = FALSE;
  3101. //
  3102. // Intialize
  3103. //
  3104. if (HtmlFormat) {
  3105. format = FORMAT_HTML;
  3106. } else if (ListFormat) {
  3107. format = FORMAT_LIST;
  3108. } else {
  3109. format = FORMAT_PLAIN_TEXT;
  3110. }
  3111. g_ListFormat = ListFormat;
  3112. if (TotalCols) {
  3113. g_TotalCols = TotalCols;
  3114. } else {
  3115. g_TotalCols = g_ListFormat ? 0x7fffffff : 70;
  3116. }
  3117. //
  3118. // Create the report body
  3119. //
  3120. FreeReportText();
  3121. //
  3122. // test for no incompatibilities
  3123. //
  3124. MYASSERT (ONEBITSET (Level));
  3125. levelMask = LEVELTOMASK (Level);
  3126. if (ListFormat) {
  3127. levelMask |= REPORTLEVEL_IN_SHORT_LIST;
  3128. }
  3129. if (!EnumFirstRootMsgGroup (&msgGroups, levelMask)) {
  3130. if (!ListFormat) {
  3131. pAddHeadingToReport (
  3132. &g_ReportString,
  3133. HtmlFormat,
  3134. MSG_NO_INCOMPATIBILITIES,
  3135. MSG_NO_INCOMPATIBILITIES
  3136. );
  3137. }
  3138. return (PCTSTR) g_ReportString.Buf;
  3139. }
  3140. if (ListFormat) {
  3141. //
  3142. // In list format, create the report in one pass
  3143. //
  3144. pCreateReportTextWorker (format, TotalCols, levelMask);
  3145. return (PCTSTR) g_ReportString.Buf;
  3146. }
  3147. //
  3148. // In HTML or plain text, create the body of the report by making 3
  3149. // passes. The first pass is for blocking issues, the second is for
  3150. // warnings, and the third is for information.
  3151. //
  3152. // We put the report in a temporary buffer (fullReport), because after the
  3153. // body is prepared, we then can prepare the table of contents.
  3154. //
  3155. // blocking section
  3156. if (pCreateReportTextWorker (
  3157. format,
  3158. TotalCols,
  3159. REPORTLEVEL_BLOCKING & levelMask
  3160. )) {
  3161. blocking = TRUE;
  3162. if (HtmlFormat) {
  3163. pAppendStringToGrowBuf (&fullReport, TEXT("<A NAME=\"blocking\">"));
  3164. }
  3165. pAddHeadingToReport (
  3166. &fullReport,
  3167. HtmlFormat,
  3168. MSG_BLOCKING_INTRO,
  3169. MSG_BLOCKING_INTRO_HTML
  3170. );
  3171. pMoveReportTextToGrowBuf (&g_ReportString, &fullReport);
  3172. }
  3173. // warning section
  3174. if (pCreateReportTextWorker (
  3175. format,
  3176. TotalCols,
  3177. (REPORTLEVEL_ERROR|REPORTLEVEL_WARNING) & levelMask
  3178. )) {
  3179. warning = TRUE;
  3180. if (HtmlFormat) {
  3181. pAppendStringToGrowBuf (&fullReport, TEXT("<A NAME=\"warning\">"));
  3182. }
  3183. pAddHeadingToReport (
  3184. &fullReport,
  3185. HtmlFormat,
  3186. MSG_WARNING_INTRO,
  3187. MSG_WARNING_INTRO_HTML
  3188. );
  3189. pMoveReportTextToGrowBuf (&g_ReportString, &fullReport);
  3190. }
  3191. // info section
  3192. if (pCreateReportTextWorker (
  3193. format,
  3194. TotalCols,
  3195. (REPORTLEVEL_INFORMATION|REPORTLEVEL_VERBOSE) & levelMask
  3196. )) {
  3197. info = TRUE;
  3198. if (HtmlFormat) {
  3199. pAppendStringToGrowBuf (&fullReport, TEXT("<A NAME=\"info\">"));
  3200. }
  3201. pAddHeadingToReport (
  3202. &fullReport,
  3203. HtmlFormat,
  3204. MSG_INFO_INTRO,
  3205. MSG_INFO_INTRO_HTML
  3206. );
  3207. pMoveReportTextToGrowBuf (&g_ReportString, &fullReport);
  3208. }
  3209. //
  3210. // Now produce the complete report (with table of contents)
  3211. //
  3212. MYASSERT (!g_ReportString.End);
  3213. MYASSERT (fullReport.End);
  3214. //
  3215. // add the heading text
  3216. //
  3217. if (HtmlFormat) {
  3218. pAppendStringToGrowBuf (&g_ReportString, TEXT("<A NAME=\"top\">"));
  3219. }
  3220. if (blocking) {
  3221. //
  3222. // add instructions based on the presence of blocking issues
  3223. //
  3224. if (HtmlFormat) {
  3225. argArray[0] = GetStringResource (g_PersonalSKU ?
  3226. MSG_PER_SUPPORT_LINK_HTML :
  3227. MSG_PRO_SUPPORT_LINK_HTML
  3228. );
  3229. msg = ParseMessageID (MSG_REPORT_BLOCKING_INSTRUCTIONS_HTML, argArray);
  3230. pAppendStringToGrowBuf (&g_ReportString, msg);
  3231. } else{
  3232. argArray[0] = GetStringResource (g_PersonalSKU ?
  3233. MSG_PER_SUPPORT_LINK :
  3234. MSG_PRO_SUPPORT_LINK
  3235. );
  3236. msg = ParseMessageID (MSG_REPORT_BLOCKING_INSTRUCTIONS, argArray);
  3237. pWrapStringToGrowBuf (&g_ReportString, msg, 0, 0);
  3238. }
  3239. FreeStringResource (argArray[0]);
  3240. FreeStringResource (msg);
  3241. } else {
  3242. //
  3243. // add instructions for just warnings and information
  3244. //
  3245. if (HtmlFormat) {
  3246. msg = GetStringResource (MSG_REPORT_GENERAL_INSTRUCTIONS_HTML);
  3247. pAppendStringToGrowBuf (&g_ReportString, msg);
  3248. } else {
  3249. msg = GetStringResource (MSG_REPORT_GENERAL_INSTRUCTIONS);
  3250. pWrapStringToGrowBuf (&g_ReportString, msg, 0, 0);
  3251. }
  3252. FreeStringResource (msg);
  3253. if (HtmlFormat) {
  3254. msg = GetStringResource (MSG_CONTENTS_TITLE_HTML);
  3255. pAppendStringToGrowBuf (&g_ReportString, msg);
  3256. } else {
  3257. msg = GetStringResource (MSG_CONTENTS_TITLE);
  3258. pWrapStringToGrowBuf (&g_ReportString, msg, 0, 0);
  3259. }
  3260. FreeStringResource (msg);
  3261. }
  3262. //
  3263. // add table of contents
  3264. //
  3265. if (HtmlFormat) {
  3266. pAppendStringToGrowBuf (&g_ReportString, TEXT("<UL>"));
  3267. }
  3268. if (blocking) {
  3269. pAddTocEntry (&g_ReportString, TEXT("blocking"), MSG_BLOCKING_TOC, HtmlFormat);
  3270. }
  3271. if (warning) {
  3272. pAddTocEntry (&g_ReportString, TEXT("warning"), MSG_WARNING_TOC, HtmlFormat);
  3273. }
  3274. if (info) {
  3275. pAddTocEntry (&g_ReportString, TEXT("info"), MSG_INFO_TOC, HtmlFormat);
  3276. }
  3277. if (HtmlFormat) {
  3278. pAppendStringToGrowBuf (&g_ReportString, TEXT("</UL>"));
  3279. }
  3280. pAppendStringToGrowBuf (&g_ReportString, TEXT("\r\n"));
  3281. //
  3282. // add bottom of heading text
  3283. //
  3284. if (!blocking) {
  3285. if (HtmlFormat) {
  3286. argArray[0] = GetStringResource (g_PersonalSKU ?
  3287. MSG_PER_SUPPORT_LINK_HTML :
  3288. MSG_PRO_SUPPORT_LINK_HTML
  3289. );
  3290. msg = ParseMessageID (MSG_REPORT_GENERAL_INSTRUCTIONS_END_HTML, argArray);
  3291. pAppendStringToGrowBuf (&g_ReportString, msg);
  3292. } else{
  3293. argArray[0] = GetStringResource (g_PersonalSKU ?
  3294. MSG_PER_SUPPORT_LINK :
  3295. MSG_PRO_SUPPORT_LINK
  3296. );
  3297. msg = ParseMessageID (MSG_REPORT_GENERAL_INSTRUCTIONS_END, argArray);
  3298. pWrapStringToGrowBuf (&g_ReportString, msg, 0, 0);
  3299. }
  3300. FreeStringResource (argArray[0]);
  3301. FreeStringResource (msg);
  3302. if (g_ConfigOptions.EnableBackup) {
  3303. if (HtmlFormat) {
  3304. subMsg = GetStringResource (MSG_REPORT_BACKUP_INSTRUCTIONS_HTML);
  3305. pAppendStringToGrowBuf (&g_ReportString, subMsg);
  3306. } else {
  3307. subMsg = GetStringResource (MSG_REPORT_BACKUP_INSTRUCTIONS);
  3308. pWrapStringToGrowBuf (&g_ReportString, subMsg, 0, 0);
  3309. }
  3310. FreeStringResource (subMsg);
  3311. }
  3312. }
  3313. //
  3314. // add body text
  3315. //
  3316. pMoveReportTextToGrowBuf (&fullReport, &g_ReportString);
  3317. //
  3318. // Clean up temp buffer and return
  3319. //
  3320. FreeGrowBuffer (&fullReport);
  3321. return (PCTSTR) g_ReportString.Buf;
  3322. }
  3323. BOOL
  3324. IsIncompatibleHardwarePresent (
  3325. VOID
  3326. )
  3327. {
  3328. REPORT_MESSAGE_ENUM msgGroups;
  3329. PCTSTR msg;
  3330. BOOL result = FALSE;
  3331. if (EnumFirstRootMsgGroup (&msgGroups, 0)) {
  3332. msg = GetStringResource (MSG_INCOMPATIBLE_HARDWARE_ROOT);
  3333. do {
  3334. if (StringMatch (msg, msgGroups.MsgGroup)){
  3335. result = TRUE;
  3336. break;
  3337. }
  3338. } while (EnumNextRootMsgGroup (&msgGroups));
  3339. FreeStringResource (msg);
  3340. }
  3341. return result;
  3342. }
  3343. PCTSTR
  3344. BuildMessageGroup (
  3345. IN UINT RootGroupId,
  3346. IN UINT SubGroupId, OPTIONAL
  3347. IN PCTSTR Item OPTIONAL
  3348. )
  3349. /*++
  3350. Routine Description:
  3351. BuildMessageGroup returns a string generated by loading the string resources
  3352. for the specified group ID, subgroup ID and Item string.
  3353. Arguments:
  3354. RootGroupId - Specifies the message resource ID of the root group. Must be
  3355. one of the defined roots. (See the top of this file.)
  3356. SubGroup - Specifies a message resource ID of a string to append to the
  3357. root.
  3358. Item - Specifies a string to append to the end of the string, used to
  3359. uniquely identify a message.
  3360. Return Value:
  3361. A pointer to the message group string. Caller must free the string via
  3362. FreeText.
  3363. --*/
  3364. {
  3365. PCTSTR RootGroup;
  3366. PCTSTR SubGroup;
  3367. PCTSTR Group;
  3368. RootGroup = GetStringResource (RootGroupId);
  3369. MYASSERT (RootGroup);
  3370. if (!RootGroup) {
  3371. return NULL;
  3372. }
  3373. if (SubGroupId) {
  3374. SubGroup = GetStringResource (SubGroupId);
  3375. MYASSERT (SubGroup);
  3376. } else {
  3377. SubGroup = NULL;
  3378. }
  3379. if (SubGroup) {
  3380. Group = JoinTextEx (NULL, RootGroup, SubGroup, TEXT("\\"), 0, NULL);
  3381. MYASSERT (Group);
  3382. FreeStringResource (SubGroup);
  3383. } else {
  3384. Group = DuplicateText (RootGroup);
  3385. }
  3386. FreeStringResource (RootGroup);
  3387. if (Item) {
  3388. RootGroup = Group;
  3389. Group = JoinTextEx (NULL, RootGroup, Item, TEXT("\\"), 0, NULL);
  3390. MYASSERT (Group);
  3391. FreeText (RootGroup);
  3392. }
  3393. return Group;
  3394. }
  3395. BOOL
  3396. IsPreDefinedMessageGroup (
  3397. IN PCTSTR Group
  3398. )
  3399. {
  3400. return pFindMsgGroupStruct (Group) != NULL;
  3401. }
  3402. PCTSTR
  3403. GetPreDefinedMessageGroupText (
  3404. IN UINT GroupNumber
  3405. )
  3406. {
  3407. PMSGGROUP_PROPS Props;
  3408. //
  3409. // GroupNumber is an externally used value. Migration DLLs may hard-code
  3410. // this number. If necessary, here is where translation is done when
  3411. // the groups change.
  3412. //
  3413. // No translation is necessary today
  3414. Props = pFindMsgGroupStructById (GroupNumber);
  3415. return Props ? Props->Name : NULL;
  3416. }
  3417. BOOL
  3418. IsReportEmpty (
  3419. IN DWORD Level
  3420. )
  3421. {
  3422. REPORT_MESSAGE_ENUM e;
  3423. DWORD levelMask;
  3424. MYASSERT (ONEBITSET (Level));
  3425. levelMask = LEVELTOMASK (Level);
  3426. return !EnumFirstMessage (&e, NULL, levelMask);
  3427. }
  3428. BOOL
  3429. EnumFirstListEntry (
  3430. OUT PLISTREPORTENTRY_ENUM EnumPtr,
  3431. IN PCTSTR ListReportText
  3432. )
  3433. {
  3434. if (!ListReportText || !*ListReportText) {
  3435. return FALSE;
  3436. }
  3437. EnumPtr->Next = (PTSTR)ListReportText;
  3438. EnumPtr->ReplacedChar = 0;
  3439. return EnumNextListEntry (EnumPtr);
  3440. }
  3441. BOOL
  3442. EnumNextListEntry (
  3443. IN OUT PLISTREPORTENTRY_ENUM EnumPtr
  3444. )
  3445. {
  3446. INT n;
  3447. if (!EnumPtr->Next) {
  3448. return FALSE;
  3449. }
  3450. EnumPtr->Entry = EnumPtr->Next;
  3451. if (EnumPtr->ReplacedChar) {
  3452. *EnumPtr->Next = EnumPtr->ReplacedChar;
  3453. EnumPtr->ReplacedChar = 0;
  3454. EnumPtr->Entry += sizeof (TEXT("\r\n")) / sizeof (TCHAR) - 1;
  3455. }
  3456. EnumPtr->Next = _tcsstr (EnumPtr->Entry, TEXT("\r\n"));
  3457. if (EnumPtr->Next) {
  3458. EnumPtr->ReplacedChar = *EnumPtr->Next;
  3459. *EnumPtr->Next = 0;
  3460. }
  3461. EnumPtr->Entry = SkipSpace (EnumPtr->Entry);
  3462. EnumPtr->Header = StringMatchTcharCount (
  3463. EnumPtr->Entry,
  3464. TEXT("<H>"),
  3465. sizeof (TEXT("<H>")) / sizeof (TCHAR) - 1
  3466. );
  3467. if (EnumPtr->Header) {
  3468. EnumPtr->Entry += sizeof (TEXT("<H>")) / sizeof (TCHAR) - 1;
  3469. }
  3470. if (_stscanf (EnumPtr->Entry, TEXT("<%lu>%n"), &EnumPtr->Level, &n) == 1) {
  3471. EnumPtr->Entry += n;
  3472. } else {
  3473. EnumPtr->Level = REPORTLEVEL_NONE;
  3474. }
  3475. EnumPtr->Level &= REPORTLEVEL_ALL; // screen out REPORTLEVEL_IN_SHORT_LIST
  3476. EnumPtr->Entry = SkipSpace (EnumPtr->Entry);
  3477. return TRUE;
  3478. }