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
4538 lines
126 KiB
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
report.c
|
|
|
|
Abstract:
|
|
|
|
This module contains routines that prepare the text that goes
|
|
in the incompatibility report. The text is displayed as details.
|
|
|
|
Author:
|
|
|
|
Jim Schmidt (jimschm) 28-Oct-1997
|
|
|
|
Revision History:
|
|
|
|
ovidiut 27-Sep-2000 Added Level member to GENERIC_LIST struct
|
|
ovidiut 20-May-1999 Added Flags member to GENERIC_LIST struct
|
|
jimschm 23-Sep-1998 TWAIN group
|
|
jimschm 02-Mar-1998 Added Auto Uninstall group
|
|
jimschm 12-Jan-1998 Reorganized to force messages into a defined message group
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
#include "uip.h"
|
|
|
|
#define DBG_REPORT "ReportMsg"
|
|
|
|
#define MAX_SPACES 32
|
|
#define MAX_MSGGROUP 2048
|
|
|
|
|
|
|
|
//
|
|
// ROOT_MSGGROUPS lists all well-defined message groups. These are the only groups
|
|
// that can appear as a root in the report. Root groups are formatted so their name
|
|
// appears with a horizontal line directly under it. Subgroups are then listed for
|
|
// the group.
|
|
//
|
|
// For example, Installation Notes is a root-level group. There are several subgroups
|
|
// of Installation Notes, and each subgroup might be formatted differently. Some may
|
|
// list several items, while others might have indented detailed descriptions.
|
|
//
|
|
// Each group can be split into zero or more defined subgroups, and zero or more
|
|
// undefined subgroups. A defined subgroup is one that either has a handler declared
|
|
// below in SUBGROUP_HANDLERS, or it is a list, defined in SUBGROUP_LISTS below.
|
|
// If a group is split this way, then there can be introduction text placed above
|
|
// all defined subgroups, and there can also be introduction text placed above
|
|
// all undefined subgroups.
|
|
//
|
|
// For example, the Incompatible Hardware category might be split as follows:
|
|
//
|
|
// Incompatible Hardware (A)
|
|
// ---------------------
|
|
// The following hardware is bad: (B)
|
|
// a (S)
|
|
// b (S)
|
|
// c (S)
|
|
//
|
|
// Contact the manufacturer. (B)
|
|
//
|
|
// The following hardware has limited functionality: (A)
|
|
//
|
|
// foo video (S)
|
|
// This device does not have 1280x1024 mode on NT. (M)
|
|
//
|
|
// In the example above, the text "Incompatible Hardware" and the underline would
|
|
// come from the group's intro text. The text "The following hardware is bad"
|
|
// comes from the hardware list subgroup intro. Then a, b and c come from the
|
|
// subgroup's subgroup. "Contact the manuafacturer" comes from the subgroup's conclusion
|
|
// text. Then "The following hardware" text comes from the group's other intro. And
|
|
// finally the remaining text comes from undefined subgroups (foo video in this case).
|
|
//
|
|
// NOTES:
|
|
// (A) indicates the message was specified in the ROOT_MSGGROUPS macro
|
|
// (B) indicates the message was specified in the SUBGROUPS_LIST macro
|
|
// (S) indicates the text was specified as the last subgroup in a MsgMgr call
|
|
// (M) indicates the message was specified in a MsgMgr call
|
|
//
|
|
// DO NOT change ROOT_MSGGROUPS unless you know what you are doing.
|
|
//
|
|
// Syntax: (one of the following)
|
|
//
|
|
// NO_TEXT(<msgid>) - Group has no intro text
|
|
// HAS_INTRO(<msgid>) - Group has intro text
|
|
// HAS_OTHER(<msgid>) - Group has intro text for undefined subgroups
|
|
// HAS_BOTH(<msgid) - Group has both types of intro text
|
|
//
|
|
// The following MSG_* strings are required to be defined in msg.mc:
|
|
//
|
|
// NO_TEXT - <msgid>_ROOT
|
|
// HAS_INTRO - <msgid>_ROOT, <msgid>_INTRO, <msgid>_INTRO_HTML
|
|
// HAS_OTHER - <msgid>_ROOT, <msgid>_OTHER, <msgid>_OTHER_HTML
|
|
// HAS_BOTH - <msgid>_ROOT, <msgid>_INTRO, <msgid>_INTRO_HTML, <msgid>_OTHER, <msgid>_OTHER_HTML
|
|
//
|
|
|
|
//
|
|
// REMEMBER: _ROOT, _INTRO, _INTRO_HTML, _OTHER and _OTHER_HTML are appended to
|
|
// the constants in ROOT_MSGGROUPS, as described above.
|
|
//
|
|
|
|
#if 0
|
|
|
|
HAS_INTRO(MSG_MUST_UNINSTALL, REPORTLEVEL_BLOCKING) \
|
|
NO_TEXT(MSG_REINSTALL_BLOCK, REPORTLEVEL_BLOCKING) \
|
|
|
|
#endif
|
|
|
|
#define ROOT_MSGGROUPS \
|
|
HAS_OTHER(MSG_BLOCKING_ITEMS, REPORTLEVEL_BLOCKING) \
|
|
HAS_BOTH(MSG_INCOMPATIBLE_HARDWARE, REPORTLEVEL_ERROR) \
|
|
HAS_INTRO(MSG_INCOMPATIBLE, REPORTLEVEL_WARNING) \
|
|
HAS_INTRO(MSG_REINSTALL, REPORTLEVEL_WARNING) \
|
|
HAS_INTRO(MSG_LOSTSETTINGS, REPORTLEVEL_WARNING) \
|
|
HAS_INTRO(MSG_MISC_WARNINGS, REPORTLEVEL_WARNING) \
|
|
HAS_INTRO(MSG_MINOR_PROBLEM, REPORTLEVEL_VERBOSE) \
|
|
HAS_INTRO(MSG_DOS_DESIGNED, REPORTLEVEL_VERBOSE) \
|
|
HAS_INTRO(MSG_INSTALL_NOTES, REPORTLEVEL_VERBOSE) \
|
|
HAS_INTRO(MSG_MIGDLL, REPORTLEVEL_VERBOSE) \
|
|
NO_TEXT(MSG_UNKNOWN, REPORTLEVEL_VERBOSE) \
|
|
|
|
|
|
//
|
|
// SUBGROUP_HANDLERS declares special formatting handlers, which format text of subgroup
|
|
// messages. There are two common default handlers that are used by most subgroups --
|
|
// the generic list handler and the default message handler. If your subgroup requires
|
|
// unique message formatting, define your handler in SUBGROUP_HANDLERS.
|
|
//
|
|
// Syntax:
|
|
//
|
|
// DECLARE(<groupid>, fn, <DWORD arg>)
|
|
//
|
|
// groupid specifies the exact text used for display and can be either a group or
|
|
// a subgroup of one of the groups defined above in ROOT_MSGGROUPS. The report
|
|
// generation code will search each segment of the group name for the specified string.
|
|
//
|
|
// For example, MSG_NAMECHANGE_WARNING_GROUP is "Names That Will Change" and is in the
|
|
// Installation Notes group. By specifying this string ID, and a handler function,
|
|
// the handler is called all name change messages, and the report generation code
|
|
// will process the name change messages during the formatting of Installation Notes.
|
|
//
|
|
|
|
|
|
#define SUBGROUP_HANDLERS \
|
|
DECLARE(MSG_UNSUPPORTED_HARDWARE_PNP_SUBGROUP, pAddPnpHardwareToReport, 0) \
|
|
DECLARE(MSG_INCOMPATIBLE_HARDWARE_PNP_SUBGROUP, pAddPnpHardwareToReport, 1) \
|
|
DECLARE(MSG_REINSTALL_HARDWARE_PNP_SUBGROUP, pAddPnpHardwareToReport, 2) \
|
|
DECLARE(0, pDefaultHandler, 0)
|
|
|
|
|
|
//
|
|
// *This is where changes are commonly made*
|
|
//
|
|
// SUBGROUP_LISTS define the string IDs for the intro text and summary text for
|
|
// the message subgroup (text not needed for root groups). This step puts your subgroup
|
|
// in the correct root-level group.
|
|
//
|
|
// If you have a simple list, you only need to add an entry to this macro expansion
|
|
// list, then you're done. The generic list handler will be processed for your
|
|
// subgroup.
|
|
//
|
|
// NOTE: You must put all info in the group when calling message manager APIs. That
|
|
// is, the group should be formatted as <group>\<subgroup>\<list-item>.
|
|
//
|
|
// Syntax:
|
|
//
|
|
// DECLARE(<rootid>, <toptext>, <toptext-html>, <bottomtext>, <bottomtext-html>, <formatargs>, <flags>, <level>)
|
|
//
|
|
// Specify zero for no message. If you specify zero for plain text, you must
|
|
// also specify zero for the html text.
|
|
//
|
|
// Use flags to specify additional features, like RF_BOLDITEMS to display items in
|
|
// bold case.
|
|
//
|
|
|
|
#define SUBGROUP_LISTS \
|
|
DECLARE( \
|
|
MSG_BLOCKING_HARDWARE_SUBGROUP, \
|
|
MSG_BLOCKING_HARDWARE_INTRO, \
|
|
MSG_BLOCKING_HARDWARE_INTRO_HTML, \
|
|
MSG_UNINDENT, \
|
|
MSG_UNINDENT_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS | RF_ENABLEMSG | RF_USEROOT, \
|
|
REPORTLEVEL_BLOCKING \
|
|
) \
|
|
DECLARE( \
|
|
MSG_MUST_UNINSTALL_ROOT, \
|
|
MSG_MUST_UNINSTALL_INTRO, \
|
|
MSG_MUST_UNINSTALL_INTRO_HTML, \
|
|
MSG_UNINDENT, \
|
|
MSG_UNINDENT_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS | RF_ENABLEMSG | RF_USEROOT, \
|
|
REPORTLEVEL_BLOCKING \
|
|
) \
|
|
DECLARE( \
|
|
MSG_REINSTALL_BLOCK_ROOT, \
|
|
MSG_REPORT_REINSTALL_BLOCK_INSTRUCTIONS, \
|
|
MSG_REPORT_REINSTALL_BLOCK_INSTRUCTIONS_HTML, \
|
|
MSG_UNINDENT, \
|
|
MSG_UNINDENT_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS | RF_ENABLEMSG | RF_USEROOT, \
|
|
REPORTLEVEL_BLOCKING \
|
|
) \
|
|
DECLARE( \
|
|
MSG_INCOMPATIBLE_DETAIL_SUBGROUP, \
|
|
MSG_REPORT_BEGIN_INDENT, \
|
|
MSG_REPORT_BEGIN_INDENT_HTML, \
|
|
MSG_UNINDENT, \
|
|
MSG_UNINDENT_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS | RF_ENABLEMSG | RF_USEROOT, \
|
|
REPORTLEVEL_WARNING \
|
|
) \
|
|
DECLARE( \
|
|
MSG_REINSTALL_DETAIL_SUBGROUP, \
|
|
MSG_REPORT_BEGIN_INDENT, \
|
|
MSG_REPORT_BEGIN_INDENT_HTML, \
|
|
MSG_UNINDENT, \
|
|
MSG_UNINDENT_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS | RF_ENABLEMSG | RF_USEROOT, \
|
|
REPORTLEVEL_WARNING \
|
|
) \
|
|
DECLARE( \
|
|
MSG_REINSTALL_LIST_SUBGROUP, \
|
|
MSG_REPORT_BEGIN_INDENT, \
|
|
MSG_REPORT_BEGIN_INDENT_HTML, \
|
|
MSG_UNINDENT, \
|
|
MSG_UNINDENT_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS | RF_USEROOT, \
|
|
REPORTLEVEL_WARNING \
|
|
) \
|
|
DECLARE( \
|
|
MSG_UNKNOWN_ROOT, \
|
|
MSG_REPORT_UNKNOWN_INSTRUCTIONS, \
|
|
MSG_REPORT_UNKNOWN_INSTRUCTIONS_HTML, \
|
|
0, \
|
|
0, \
|
|
NULL, \
|
|
RF_BOLDITEMS, \
|
|
REPORTLEVEL_VERBOSE \
|
|
) \
|
|
DECLARE( \
|
|
MSG_HWPROFILES_SUBGROUP, \
|
|
MSG_HWPROFILES_INTRO, \
|
|
MSG_HWPROFILES_INTRO_HTML, \
|
|
MSG_UNINDENT2, \
|
|
MSG_UNINDENT2_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS, \
|
|
REPORTLEVEL_VERBOSE \
|
|
) \
|
|
DECLARE( \
|
|
MSG_REG_SETTINGS_SUBGROUP, \
|
|
MSG_REG_SETTINGS_INCOMPLETE, \
|
|
MSG_REG_SETTINGS_INCOMPLETE_HTML, \
|
|
MSG_UNINDENT2, \
|
|
MSG_UNINDENT2_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS, \
|
|
REPORTLEVEL_INFORMATION \
|
|
) \
|
|
DECLARE( \
|
|
MSG_SHELL_SETTINGS_SUBGROUP, \
|
|
MSG_SHELL_SETTINGS_INCOMPLETE, \
|
|
MSG_SHELL_SETTINGS_INCOMPLETE_HTML, \
|
|
MSG_UNINDENT2, \
|
|
MSG_UNINDENT2_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS, \
|
|
REPORTLEVEL_INFORMATION \
|
|
) \
|
|
DECLARE( \
|
|
MSG_DRIVE_EXCLUDED_SUBGROUP, \
|
|
MSG_DRIVE_EXCLUDED_INTRO, \
|
|
MSG_DRIVE_EXCLUDED_INTRO_HTML, \
|
|
MSG_UNINDENT2, \
|
|
MSG_UNINDENT2_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS, \
|
|
REPORTLEVEL_VERBOSE \
|
|
) \
|
|
DECLARE( \
|
|
MSG_DRIVE_NETWORK_SUBGROUP, \
|
|
MSG_DRIVE_NETWORK_INTRO, \
|
|
MSG_DRIVE_NETWORK_INTRO_HTML, \
|
|
MSG_UNINDENT2, \
|
|
MSG_UNINDENT2_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS, \
|
|
REPORTLEVEL_VERBOSE \
|
|
) \
|
|
DECLARE( \
|
|
MSG_DRIVE_RAM_SUBGROUP, \
|
|
MSG_DRIVE_RAM_INTRO, \
|
|
MSG_DRIVE_RAM_INTRO_HTML, \
|
|
MSG_UNINDENT2, \
|
|
MSG_UNINDENT2_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS, \
|
|
REPORTLEVEL_VERBOSE \
|
|
) \
|
|
DECLARE( \
|
|
MSG_DRIVE_SUBST_SUBGROUP, \
|
|
MSG_DRIVE_SUBST_INTRO, \
|
|
MSG_DRIVE_SUBST_INTRO_HTML, \
|
|
MSG_UNINDENT2, \
|
|
MSG_UNINDENT2_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS, \
|
|
REPORTLEVEL_VERBOSE \
|
|
) \
|
|
DECLARE( \
|
|
MSG_DRIVE_INACCESSIBLE_SUBGROUP, \
|
|
MSG_DRIVE_INACCESSIBLE_INTRO, \
|
|
MSG_DRIVE_INACCESSIBLE_INTRO_HTML, \
|
|
MSG_UNINDENT2, \
|
|
MSG_UNINDENT2_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS, \
|
|
REPORTLEVEL_VERBOSE \
|
|
) \
|
|
DECLARE( \
|
|
MSG_HELPFILES_SUBGROUP, \
|
|
MSG_HELPFILES_INTRO, \
|
|
MSG_HELPFILES_INTRO_HTML, \
|
|
MSG_UNINDENT2, \
|
|
MSG_UNINDENT2_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS, \
|
|
REPORTLEVEL_VERBOSE \
|
|
) \
|
|
DECLARE( \
|
|
MSG_TWAIN_SUBGROUP, \
|
|
MSG_TWAIN_DEVICES_UNKNOWN, \
|
|
MSG_TWAIN_DEVICES_UNKNOWN_HTML, \
|
|
MSG_UNINDENT2, \
|
|
MSG_UNINDENT2_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS, \
|
|
REPORTLEVEL_ERROR \
|
|
) \
|
|
DECLARE( \
|
|
MSG_JOYSTICK_SUBGROUP, \
|
|
MSG_JOYSTICK_DEVICE_UNKNOWN, \
|
|
MSG_JOYSTICK_DEVICE_UNKNOWN_HTML, \
|
|
MSG_UNINDENT2, \
|
|
MSG_UNINDENT2_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS, \
|
|
REPORTLEVEL_ERROR \
|
|
) \
|
|
DECLARE( \
|
|
MSG_CONNECTION_BADPROTOCOL_SUBGROUP, \
|
|
MSG_CONNECTION_BADPROTOCOL_INTRO, \
|
|
MSG_CONNECTION_BADPROTOCOL_INTRO_HTML, \
|
|
MSG_UNINDENT2, \
|
|
MSG_UNINDENT2_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS, \
|
|
REPORTLEVEL_WARNING \
|
|
) \
|
|
DECLARE( \
|
|
MSG_CONNECTION_PASSWORD_SUBGROUP, \
|
|
MSG_CONNECTION_PASSWORD_INTRO, \
|
|
MSG_CONNECTION_PASSWORD_INTRO_HTML, \
|
|
MSG_UNINDENT2, \
|
|
MSG_UNINDENT2_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS, \
|
|
REPORTLEVEL_WARNING \
|
|
) \
|
|
DECLARE( \
|
|
MSG_RUNNING_MIGRATION_DLLS_SUBGROUP, \
|
|
MSG_RUNNING_MIGRATION_DLLS_INTRO, \
|
|
MSG_RUNNING_MIGRATION_DLLS_INTRO_HTML, \
|
|
MSG_UNINDENT2, \
|
|
MSG_UNINDENT2_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS, \
|
|
REPORTLEVEL_VERBOSE \
|
|
) \
|
|
DECLARE( \
|
|
MSG_TOTALLY_INCOMPATIBLE_SUBGROUP, \
|
|
MSG_TOTALLY_INCOMPATIBLE_INTRO, \
|
|
MSG_TOTALLY_INCOMPATIBLE_INTRO_HTML, \
|
|
MSG_TOTALLY_INCOMPATIBLE_TRAIL, \
|
|
MSG_TOTALLY_INCOMPATIBLE_TRAIL_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS | RF_ENABLEMSG, \
|
|
REPORTLEVEL_WARNING \
|
|
) \
|
|
DECLARE( \
|
|
MSG_INCOMPATIBLE_PREINSTALLED_UTIL_SUBGROUP, \
|
|
MSG_PREINSTALLED_UTIL_INSTRUCTIONS, \
|
|
MSG_PREINSTALLED_UTIL_INSTRUCTIONS_HTML, \
|
|
MSG_UNINDENT2, \
|
|
MSG_UNINDENT2_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS, \
|
|
REPORTLEVEL_VERBOSE \
|
|
) \
|
|
DECLARE( \
|
|
MSG_INCOMPATIBLE_UTIL_SIMILAR_FEATURE_SUBGROUP, \
|
|
MSG_PREINSTALLED_SIMILAR_FEATURE, \
|
|
MSG_PREINSTALLED_SIMILAR_FEATURE_HTML, \
|
|
MSG_UNINDENT2, \
|
|
MSG_UNINDENT2_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS, \
|
|
REPORTLEVEL_VERBOSE \
|
|
) \
|
|
DECLARE( \
|
|
MSG_INCOMPATIBLE_HW_UTIL_SUBGROUP, \
|
|
MSG_HW_UTIL_INTRO, \
|
|
MSG_HW_UTIL_INTRO_HTML, \
|
|
MSG_UNINDENT2, \
|
|
MSG_UNINDENT2_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS, \
|
|
REPORTLEVEL_VERBOSE \
|
|
) \
|
|
DECLARE( \
|
|
MSG_REMOVED_FOR_SAFETY_SUBGROUP, \
|
|
MSG_REMOVED_FOR_SAFETY_INTRO, \
|
|
MSG_REMOVED_FOR_SAFETY_INTRO_HTML, \
|
|
MSG_UNINDENT, \
|
|
MSG_UNINDENT_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS, \
|
|
REPORTLEVEL_INFORMATION \
|
|
) \
|
|
DECLARE( \
|
|
MSG_DIRECTORY_COLLISION_SUBGROUP, \
|
|
MSG_DIRECTORY_COLLISION_INTRO, \
|
|
MSG_DIRECTORY_COLLISION_INTRO_HTML, \
|
|
MSG_UNINDENT, \
|
|
MSG_UNINDENT_HTML, \
|
|
NULL, \
|
|
RF_USESUBGROUP, \
|
|
REPORTLEVEL_VERBOSE \
|
|
) \
|
|
DECLARE( \
|
|
MSG_BACKUP_DETECTED_LIST_SUBGROUP, \
|
|
MSG_BACKUP_DETECTED_INTRO, \
|
|
MSG_BACKUP_DETECTED_INTRO_HTML, \
|
|
MSG_BACKUP_DETECTED_TRAIL, \
|
|
MSG_BACKUP_DETECTED_TRAIL_HTML, \
|
|
g_BackupDetectedFormatArgs, \
|
|
RF_BOLDITEMS, \
|
|
REPORTLEVEL_VERBOSE \
|
|
) \
|
|
DECLARE( \
|
|
MSG_NAMECHANGE_WARNING_GROUP, \
|
|
MSG_REPORT_NAMECHANGE, \
|
|
MSG_REPORT_NAMECHANGE_HTML, \
|
|
MSG_UNINDENT2, \
|
|
MSG_UNINDENT2_HTML, \
|
|
NULL, \
|
|
RF_BOLDITEMS, \
|
|
REPORTLEVEL_INFORMATION \
|
|
) \
|
|
|
|
|
|
|
|
//
|
|
// Declare an array of message groups
|
|
//
|
|
|
|
typedef struct {
|
|
DWORD GroupLevel;
|
|
UINT MsgGroup;
|
|
UINT IntroId;
|
|
UINT IntroIdHtml;
|
|
UINT OtherId;
|
|
UINT OtherIdHtml;
|
|
|
|
// members initialized to zero
|
|
UINT NameLchars;
|
|
PCTSTR Name;
|
|
PCTSTR IntroIdStr;
|
|
PCTSTR IntroIdHtmlStr;
|
|
PCTSTR OtherIdStr;
|
|
PCTSTR OtherIdHtmlStr;
|
|
} MSGGROUP_PROPS, *PMSGGROUP_PROPS;
|
|
|
|
|
|
|
|
|
|
#define NO_TEXT(root,level) {level,root##_ROOT},
|
|
#define HAS_INTRO(root,level) {level,root##_ROOT, root##_INTRO, root##_INTRO_HTML},
|
|
#define HAS_OTHER(root,level) {level,root##_ROOT, 0, 0, root##_OTHER, root##_OTHER_HTML},
|
|
#define HAS_BOTH(root,level) {level,root##_ROOT, root##_INTRO, root##_INTRO_HTML, root##_OTHER, root##_OTHER_HTML},
|
|
|
|
MSGGROUP_PROPS g_MsgGroupRoot[] = {
|
|
ROOT_MSGGROUPS /* , */
|
|
{0,0,0,0,0,0}
|
|
};
|
|
|
|
#undef NO_TEXT
|
|
#undef HAS_INTRO
|
|
#undef HAS_OTHER
|
|
#undef HAS_BOTH
|
|
|
|
|
|
//
|
|
// Declare the handler prototypes
|
|
//
|
|
|
|
typedef enum {
|
|
INIT,
|
|
PROCESS_ITEM,
|
|
CLEANUP
|
|
} CALLCONTEXT;
|
|
|
|
typedef BOOL(SUBGROUP_HANDLER_PROTOTYPE)(
|
|
IN CALLCONTEXT Context,
|
|
IN PMSGGROUP_PROPS Group,
|
|
IN OUT PGROWBUFFER StringBufPtr,
|
|
IN PCTSTR SubGroup,
|
|
IN PCTSTR Message,
|
|
IN DWORD Level,
|
|
IN BOOL HtmlFormat,
|
|
IN OUT PVOID *State,
|
|
IN DWORD Arg
|
|
);
|
|
|
|
typedef SUBGROUP_HANDLER_PROTOTYPE * SUBGROUP_HANDLER;
|
|
|
|
#define DECLARE(msgid,fn,arg) SUBGROUP_HANDLER_PROTOTYPE fn;
|
|
|
|
SUBGROUP_HANDLERS
|
|
|
|
#undef DECLARE
|
|
|
|
//
|
|
// Declare the message ID array
|
|
//
|
|
|
|
#define DECLARE(msgid,fn,arg) {fn, msgid, (DWORD) (arg)},
|
|
|
|
typedef struct {
|
|
SUBGROUP_HANDLER Fn;
|
|
UINT Id;
|
|
DWORD Arg;
|
|
|
|
// rest are init'd with zeros
|
|
PCTSTR SubGroupStr;
|
|
UINT SubGroupStrLchars;
|
|
PVOID State;
|
|
} HANDLER_LIST, *PHANDLER_LIST;
|
|
|
|
HANDLER_LIST g_HandlerNames[] = {
|
|
SUBGROUP_HANDLERS /* , */
|
|
{NULL, 0, 0, 0}
|
|
};
|
|
|
|
#undef DECLARE
|
|
|
|
//
|
|
// Declare array of generic lists
|
|
//
|
|
|
|
typedef struct {
|
|
UINT Id;
|
|
PCTSTR SubGroupStr;
|
|
UINT SubGroupStrLchars;
|
|
UINT IntroId;
|
|
UINT IntroIdHtml;
|
|
UINT ConclusionId;
|
|
UINT ConclusionIdHtml;
|
|
PCTSTR **FormatArgs;
|
|
DWORD Flags;
|
|
DWORD ListLevel;
|
|
} GENERIC_LIST, *PGENERIC_LIST;
|
|
|
|
//
|
|
// definition of flags in GENERIC_LIST.Flags
|
|
//
|
|
#define RF_BOLDITEMS 0x0001
|
|
#define RF_ENABLEMSG 0x0002
|
|
#define RF_MSGFIRST 0x0004
|
|
#define RF_USEROOT 0x0008
|
|
#define RF_USESUBGROUP 0x0010
|
|
#define RF_INDENTMSG 0x0020
|
|
|
|
//
|
|
// macros to test flags
|
|
//
|
|
#define FLAGSET_BOLDITEMS(Flags) ((Flags & RF_BOLDITEMS) != 0)
|
|
#define FLAGSET_ENABLE_MESSAGE_ITEMS(Flags) ((Flags & RF_ENABLEMSG) != 0)
|
|
#define FLAGSET_MESSAGE_ITEMS_FIRST(Flags) ((Flags & RF_MSGFIRST) != 0)
|
|
#define FLAGSET_USEROOT(Flags) ((Flags & RF_USEROOT) != 0)
|
|
#define FLAGSET_USESUBGROUP(Flags) ((Flags & RF_USESUBGROUP) != 0)
|
|
#define FLAGSET_INDENT_MESSAGE(Flags) ((Flags & RF_INDENTMSG) != 0)
|
|
|
|
//
|
|
// this is the array of pointers to strings for formatting MSG_BACKUP_DETECTED_SUBGROUP
|
|
// they are the same for all 4 IDs (MSG_BACKUP_DETECTED_INTRO etc.)
|
|
//
|
|
static PCTSTR g_BackupDetectedFormatArgsAllIDs[1] = {
|
|
g_Win95Name
|
|
};
|
|
|
|
static PCTSTR *g_BackupDetectedFormatArgs[4] = {
|
|
g_BackupDetectedFormatArgsAllIDs,
|
|
g_BackupDetectedFormatArgsAllIDs,
|
|
g_BackupDetectedFormatArgsAllIDs,
|
|
g_BackupDetectedFormatArgsAllIDs
|
|
};
|
|
|
|
#define DECLARE(rootid,intro,introhtml,conclusion,conclusionhtml,formatargs,flags,level) {rootid, NULL, 0, intro, introhtml, conclusion, conclusionhtml, formatargs, flags, level},
|
|
|
|
GENERIC_LIST g_GenericList[] = {
|
|
SUBGROUP_LISTS /* , */
|
|
{0, NULL, 0, 0, 0, 0, 0, NULL, 0, 0}
|
|
};
|
|
|
|
#undef DECLARE
|
|
|
|
|
|
|
|
#define SUBGROUP_REPORT_LEVELS \
|
|
DECLARE(MSG_NETWORK_PROTOCOLS, REPORTLEVEL_WARNING) \
|
|
|
|
|
|
#define ONEBITSET(x) ((x) && !((x) & ((x) - 1)))
|
|
#define LEVELTOMASK(x) (((x) << 1) - 1)
|
|
|
|
//
|
|
// Types
|
|
//
|
|
|
|
typedef struct {
|
|
GROWLIST List;
|
|
GROWLIST MessageItems;
|
|
GROWLIST Messages;
|
|
} ITEMLIST, *PITEMLIST;
|
|
|
|
typedef struct {
|
|
TCHAR LastClass[MEMDB_MAX];
|
|
} PNPFORMATSTATE, *PPNPFORMATSTATE;
|
|
|
|
|
|
typedef struct {
|
|
PCTSTR HtmlTagStr;
|
|
UINT Length;
|
|
} HTMLTAG, *PHTMLTAG;
|
|
|
|
typedef struct {
|
|
UINT MessageId;
|
|
PCTSTR MessageStr;
|
|
UINT MessageLength;
|
|
DWORD ReportLevel;
|
|
} MAPMESSAGETOREPORTLEVEL, *PMAPMESSAGETOREPORTLEVEL;
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
|
|
static UINT g_TotalCols;
|
|
static GROWBUFFER g_ReportString = GROWBUF_INIT;
|
|
static TCHAR g_LastMsgGroupBuf[MAX_MSGGROUP];
|
|
static BOOL g_ListFormat;
|
|
|
|
#define DECLARE(messageid,reportlevel) {messageid, NULL, 0, reportlevel},
|
|
|
|
static MAPMESSAGETOREPORTLEVEL g_MapMessageToLevel[] = {
|
|
SUBGROUP_REPORT_LEVELS
|
|
{0, NULL, 0, 0}
|
|
};
|
|
|
|
#undef DECLARE
|
|
|
|
|
|
//
|
|
// Local prototypes
|
|
//
|
|
|
|
VOID
|
|
pLoadAllHandlerStrings (
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
pFreeAllHandlerStrings (
|
|
VOID
|
|
);
|
|
|
|
PMSGGROUP_PROPS
|
|
pFindMsgGroupStruct (
|
|
IN PCTSTR MsgGroup
|
|
);
|
|
|
|
PMSGGROUP_PROPS
|
|
pFindMsgGroupStructById (
|
|
IN UINT GroupId
|
|
);
|
|
|
|
PCTSTR
|
|
pEncodeMessage (
|
|
IN PCTSTR Message,
|
|
IN BOOL HtmlFormat
|
|
);
|
|
|
|
//
|
|
// Implementation
|
|
//
|
|
|
|
|
|
|
|
POOLHANDLE g_MessagePool;
|
|
|
|
BOOL
|
|
InitCompatTable (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
InitCompatTable initializes the resources needed to hold the incompatibility
|
|
report in memory.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
TRUE if the init succeeded, or FALSE if it failed.
|
|
|
|
--*/
|
|
|
|
{
|
|
g_MessagePool = PoolMemInitNamedPool ("Incompatibility Messages");
|
|
if (!g_MessagePool) {
|
|
return FALSE;
|
|
}
|
|
|
|
pLoadAllHandlerStrings();
|
|
|
|
return MemDbCreateTemporaryKey (MEMDB_CATEGORY_REPORT);
|
|
}
|
|
|
|
|
|
VOID
|
|
FreeCompatTable (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
FreeCompatTable frees the resources used to hold incompatibility messages.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
PoolMemDestroyPool (g_MessagePool);
|
|
pFreeAllHandlerStrings();
|
|
}
|
|
|
|
|
|
PMAPMESSAGETOREPORTLEVEL
|
|
pGetMapStructFromMessageGroup (
|
|
IN PCTSTR FullMsgGroup
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pGetMapStructFromMessageGroup returns a pointer to the MAPMESSAGETOREPORTLEVEL
|
|
associated with the given message group.
|
|
|
|
Arguments:
|
|
|
|
FullMsgGroup - Specifies the name of the message group in question
|
|
|
|
Return Value:
|
|
|
|
A pointer to the associated struct, if any; NULL if not found
|
|
|
|
--*/
|
|
|
|
{
|
|
PMAPMESSAGETOREPORTLEVEL pMap;
|
|
PCTSTR p;
|
|
CHARTYPE ch;
|
|
|
|
if (!FullMsgGroup) {
|
|
return FALSE;
|
|
}
|
|
|
|
for (pMap = g_MapMessageToLevel; pMap->MessageId; pMap++) {
|
|
|
|
if (StringIMatchLcharCount (
|
|
FullMsgGroup,
|
|
pMap->MessageStr,
|
|
pMap->MessageLength
|
|
)) {
|
|
|
|
p = LcharCountToPointer (FullMsgGroup, pMap->MessageLength);
|
|
ch = _tcsnextc (p);
|
|
|
|
if (ch == TEXT('\\') || ch == 0) {
|
|
return pMap;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pIsThisTheGenericList (
|
|
IN PCTSTR FullMsgGroup,
|
|
IN PGENERIC_LIST List
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pIsThisTheGenericList compares the specified message group list name
|
|
against the specified message group.
|
|
|
|
Arguments:
|
|
|
|
FullMsgGroup - Specifies the name of the message group in question
|
|
|
|
List - Specifies the message group list to compare against FullMsgGroup
|
|
|
|
Return Value:
|
|
|
|
TRUE if List handles the FullMsgGroup message, or FALSE if not.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCTSTR p;
|
|
CHARTYPE ch;
|
|
|
|
if (!List) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (StringIMatchLcharCount (
|
|
FullMsgGroup,
|
|
List->SubGroupStr,
|
|
List->SubGroupStrLchars
|
|
)) {
|
|
|
|
p = LcharCountToPointer (FullMsgGroup, List->SubGroupStrLchars);
|
|
ch = _tcsnextc (p);
|
|
|
|
if (ch == TEXT('\\') || ch == 0) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
PGENERIC_LIST
|
|
pSearchForGenericList (
|
|
IN PCTSTR Str
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pSearchForGenericList scans the list of generic lists for one that can
|
|
handle the current message group. A pointer to the generic list structure is
|
|
returned.
|
|
|
|
Arguments:
|
|
|
|
Str - Specifies the message group to locate a handler for, and may include
|
|
backslashes and subgroups.
|
|
|
|
Return Value:
|
|
|
|
A pointer to the generic list structure if found, or NULL if no generic
|
|
list exists for the message group.
|
|
|
|
--*/
|
|
|
|
{
|
|
PGENERIC_LIST List;
|
|
|
|
for (List = g_GenericList ; List->Id ; List++) {
|
|
if (pIsThisTheGenericList (Str, List)) {
|
|
return List;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
AddBadSoftware (
|
|
IN PCTSTR MessageGroup,
|
|
IN PCTSTR Message, OPTIONAL
|
|
IN BOOL IncludeOnShortReport
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
AddBadSoftware adds a message to the incompatibility report data structures.
|
|
It duplicates Message into a pool, and indexes the message by message group
|
|
with memdb.
|
|
|
|
Arguments:
|
|
|
|
MessageGroup - Specifies the name of the message group, and may include
|
|
subgroups. The root of MessageGroup must be a defined category.
|
|
See ROOT_MSGGROUPS above.
|
|
|
|
Message - Specifies the text for the message, if any.
|
|
|
|
IncludeOnShortReport - Specifies TRUE if the message should appear in the list
|
|
view of the short report.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the operation succeeded, or FALSE if it failed.
|
|
|
|
--*/
|
|
|
|
{
|
|
UINT BytesNeeded;
|
|
PCTSTR DupMsg;
|
|
TCHAR FilteredMessageGroup[MEMDB_MAX];
|
|
PMSGGROUP_PROPS Group;
|
|
TCHAR key[MEMDB_MAX];
|
|
PGENERIC_LIST list = NULL;
|
|
PTSTR p;
|
|
PMAPMESSAGETOREPORTLEVEL pMap;
|
|
DWORD level = REPORTLEVEL_NONE;
|
|
|
|
if (Message && Message[0] == 0) {
|
|
Message = NULL;
|
|
}
|
|
|
|
Group = pFindMsgGroupStruct (MessageGroup);
|
|
if (!Group) {
|
|
//
|
|
// This message group is illegal. Assume that it is from a migration DLL,
|
|
// and put it in the Upgrade Module Information group.
|
|
//
|
|
|
|
Group = pFindMsgGroupStructById (MSG_INSTALL_NOTES_ROOT);
|
|
MYASSERT (Group);
|
|
|
|
wsprintf (FilteredMessageGroup, TEXT("%s\\%s"), Group->Name, MessageGroup);
|
|
} else {
|
|
StringCopy (FilteredMessageGroup, MessageGroup);
|
|
}
|
|
|
|
if (Message) {
|
|
BytesNeeded = SizeOfString (Message);
|
|
|
|
DupMsg = PoolMemDuplicateString (g_MessagePool, Message);
|
|
if (!DupMsg) {
|
|
return FALSE;
|
|
}
|
|
|
|
DEBUGMSG ((DBG_REPORT, "%s: %s", FilteredMessageGroup, Message));
|
|
|
|
} else {
|
|
DupMsg = NULL;
|
|
DEBUGMSG ((DBG_REPORT, "%s", FilteredMessageGroup));
|
|
}
|
|
|
|
//
|
|
// Check to see if there is a handler for all messages in the message
|
|
// group.
|
|
//
|
|
|
|
p = _tcschr (MessageGroup, TEXT('\\'));
|
|
if (p) {
|
|
p = _tcsinc (p);
|
|
list = pSearchForGenericList (p);
|
|
if (list) {
|
|
level = list->ListLevel;
|
|
}
|
|
}
|
|
if (level == REPORTLEVEL_NONE) {
|
|
list = pSearchForGenericList (MessageGroup);
|
|
if (list) {
|
|
level = list->ListLevel;
|
|
}
|
|
}
|
|
if (level == REPORTLEVEL_NONE) {
|
|
pMap = pGetMapStructFromMessageGroup (MessageGroup);
|
|
if (pMap) {
|
|
level = pMap->ReportLevel;
|
|
}
|
|
}
|
|
if (level == REPORTLEVEL_NONE) {
|
|
level = Group->GroupLevel;
|
|
}
|
|
|
|
MYASSERT (ONEBITSET (level));
|
|
|
|
if (IncludeOnShortReport) {
|
|
level |= REPORTLEVEL_IN_SHORT_LIST;
|
|
}
|
|
|
|
MemDbBuildKey (key, MEMDB_CATEGORY_REPORT, FilteredMessageGroup, NULL, NULL);
|
|
return MemDbSetValueAndFlags (key, (DWORD) DupMsg, level, REPORTLEVEL_ALL);
|
|
}
|
|
|
|
|
|
VOID
|
|
pCutAfterFirstLine (
|
|
IN PTSTR Message
|
|
)
|
|
{
|
|
PTSTR p = _tcsstr (Message, TEXT("\r\n"));
|
|
if (p) {
|
|
*p = 0;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
pEnumMessageWorker (
|
|
IN OUT PREPORT_MESSAGE_ENUM EnumPtr
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pEnumMessageWorker completes an enumeration by filling in members of the
|
|
enum structure. This routine is common to message enumerations.
|
|
|
|
Do not use the Message member of the enumeration structure, as its contents
|
|
are undefined.
|
|
|
|
Arguments:
|
|
|
|
EnumPtr - Specifies a paritly completed enumeration structure that needs
|
|
the rest of its members updated.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the message should be processed, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
if (!(EnumPtr->e.UserFlags & EnumPtr->EnumLevel)) {
|
|
return FALSE;
|
|
}
|
|
|
|
StringCopy (EnumPtr->MsgGroup, EnumPtr->e.szName);
|
|
|
|
//
|
|
// Value has pointer to message, or is NULL
|
|
//
|
|
|
|
EnumPtr->Message = (PCTSTR) EnumPtr->e.dwValue;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
EnumFirstMessage (
|
|
OUT PREPORT_MESSAGE_ENUM EnumPtr,
|
|
IN PCTSTR RootCategory, OPTIONAL
|
|
IN DWORD LevelMask
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
EnumFirstMessage begins an enumeration of all message group names in
|
|
the migration report, including subgroups. The Message member of the
|
|
enum structure will point to the actual message, or NULL if none
|
|
exists.
|
|
|
|
Arguments:
|
|
|
|
EnumPtr - Receives the enumerated message, with the members set as follows:
|
|
|
|
MsgGroup - Receives the name of the message group. If
|
|
RootCategory is specified, MsgGroup will contain
|
|
the subgroup of RootCategory. If no subgroup exists,
|
|
MsgGroup will be an empty string.
|
|
|
|
Message - Points to the message text, or NULL if no message text
|
|
exists.
|
|
|
|
RootCategory - Specifies a specific message group to enumerate. It may also
|
|
contain subgroups, separated by backslashes.
|
|
|
|
LevelMask - Specifies which report severity levels to list. If 0 is specified, all
|
|
levels are enumerated.
|
|
|
|
Return Value:
|
|
|
|
TRUE if a message was enumerated, or FALSE if not.
|
|
|
|
Remarks:
|
|
|
|
The enumeration does not allocate any resources, so it can be abandoned at
|
|
any time.
|
|
|
|
--*/
|
|
|
|
{
|
|
EnumPtr->EnumLevel = LevelMask ? LevelMask : REPORTLEVEL_ALL;
|
|
|
|
if (MemDbGetValueEx (
|
|
&EnumPtr->e,
|
|
MEMDB_CATEGORY_REPORT,
|
|
RootCategory,
|
|
NULL
|
|
)) {
|
|
do {
|
|
if (pEnumMessageWorker (EnumPtr)) {
|
|
return TRUE;
|
|
}
|
|
} while (MemDbEnumNextValue (&EnumPtr->e));
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
EnumNextMessage (
|
|
IN OUT PREPORT_MESSAGE_ENUM EnumPtr
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
EnumNextMessage continues the enumeration started by EnumFirstMessage.
|
|
|
|
Arguments:
|
|
|
|
EnumPtr - Specifies the current enumeration state, receives the enumerated item.
|
|
|
|
Return Value:
|
|
|
|
TRUE if another message was enumerated, or FALSE if not.
|
|
|
|
--*/
|
|
|
|
{
|
|
while (MemDbEnumNextValue (&EnumPtr->e)) {
|
|
if (pEnumMessageWorker (EnumPtr)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
EnumFirstRootMsgGroup (
|
|
OUT PREPORT_MESSAGE_ENUM EnumPtr,
|
|
IN DWORD LevelMask
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
EnumFirstRootMsgGroup begins an enumeration of all message group names in
|
|
the migration report, but does not enumerate subgroups.
|
|
|
|
Arguments:
|
|
|
|
EnumPtr - Receives the enumerated item. In particular, the MsgGroup
|
|
member will contain the name of the message group.
|
|
|
|
LevelMask - Specifies which error levels to enumerate (blocking, errors,
|
|
warnings, etc.)
|
|
|
|
Return Value:
|
|
|
|
TRUE if a message group was enumerated, or FALSE if not.
|
|
|
|
Remarks:
|
|
|
|
The enumeration does not allocate any resources, so it can be abandoned at
|
|
any time.
|
|
|
|
--*/
|
|
|
|
{
|
|
ZeroMemory (EnumPtr, sizeof (REPORT_MESSAGE_ENUM));
|
|
EnumPtr->EnumLevel = LevelMask;
|
|
|
|
return EnumNextRootMsgGroup (EnumPtr);
|
|
}
|
|
|
|
|
|
BOOL
|
|
EnumNextRootMsgGroup (
|
|
IN OUT PREPORT_MESSAGE_ENUM EnumPtr
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
EnumNextRootMsgGroup continues an enumeration of message group names.
|
|
|
|
Arguments:
|
|
|
|
EnumPtr - Specifies an enumeration structure that was first updated by
|
|
EnumFirstRootMsgGroup, and optionally updated by previous
|
|
calls to EnumNextRootMsgGroup.
|
|
|
|
Return Value:
|
|
|
|
TRUE if another message group was enumerated, or FALSE no more groups
|
|
exist.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
REPORT_MESSAGE_ENUM e;
|
|
|
|
while (g_MsgGroupRoot[EnumPtr->Index].MsgGroup) {
|
|
//
|
|
// Determine if g_MsgGroupRoot[i].Name has messages to display
|
|
//
|
|
|
|
if (EnumFirstMessage (&e, g_MsgGroupRoot[EnumPtr->Index].Name, EnumPtr->EnumLevel)) {
|
|
StringCopy (EnumPtr->MsgGroup, g_MsgGroupRoot[EnumPtr->Index].Name);
|
|
EnumPtr->Message = NULL;
|
|
|
|
EnumPtr->Index++;
|
|
return TRUE;
|
|
}
|
|
|
|
EnumPtr->Index++;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pAppendStringToGrowBuf (
|
|
IN OUT PGROWBUFFER StringBuf,
|
|
IN PCTSTR String
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pAppendStringToGrowBuf is called by handler functions
|
|
to add formatted text to a report buffer.
|
|
|
|
Arguments:
|
|
|
|
StringBuf - Specifies the current report to append text to.
|
|
|
|
String - Specifies the text to append.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the allocation succeeded, or FALSE if it failed.
|
|
|
|
--*/
|
|
|
|
{
|
|
PTSTR Buf;
|
|
|
|
if (!String || *String == 0) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (StringBuf->End) {
|
|
StringBuf->End -= sizeof (TCHAR);
|
|
}
|
|
|
|
Buf = (PTSTR) GrowBuffer (StringBuf, SizeOfString (String));
|
|
if (!Buf) {
|
|
return FALSE;
|
|
}
|
|
|
|
StringCopy (Buf, String);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pStartHeaderLine (
|
|
IN OUT PGROWBUFFER StringBuf
|
|
)
|
|
{
|
|
return pAppendStringToGrowBuf (StringBuf, TEXT("<H>"));
|
|
}
|
|
|
|
BOOL
|
|
pWriteNewLine (
|
|
IN OUT PGROWBUFFER StringBuf
|
|
)
|
|
{
|
|
return pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
|
|
}
|
|
|
|
BOOL
|
|
pDumpDwordToGrowBuf (
|
|
IN OUT PGROWBUFFER StringBuf,
|
|
IN DWORD Dword
|
|
)
|
|
{
|
|
TCHAR buffer[12];
|
|
|
|
wsprintf (buffer, TEXT("<%lu>"), Dword);
|
|
return pAppendStringToGrowBuf (StringBuf, buffer);
|
|
}
|
|
|
|
BOOL
|
|
pWrapStringToGrowBuf (
|
|
IN OUT PGROWBUFFER StringBuf,
|
|
IN PCTSTR String,
|
|
IN UINT Indent,
|
|
IN INT HangingIndent
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pWrapStringToGrowBuf is called by handler functions
|
|
to add plain text to a report buffer.
|
|
|
|
Arguments:
|
|
|
|
StringBuf - Specifies the current report to append text to.
|
|
|
|
String - Specifies the text to append.
|
|
|
|
Indent - Specifies number of characters to indent text, may
|
|
be zero.
|
|
|
|
HangingIndent - Specifies the adjustment to be made to Indent
|
|
after the first line is processed.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the allocation succeeded, or FALSE if it failed.
|
|
|
|
--*/
|
|
|
|
{
|
|
PTSTR Buf;
|
|
PCTSTR WrappedStr;
|
|
|
|
if (!String || *String == 0) {
|
|
return TRUE;
|
|
}
|
|
|
|
WrappedStr = CreateIndentedString (String, Indent, HangingIndent, g_TotalCols);
|
|
if (!WrappedStr) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (StringBuf->End) {
|
|
StringBuf->End -= sizeof (TCHAR);
|
|
}
|
|
|
|
Buf = (PTSTR) GrowBuffer (StringBuf, SizeOfString (WrappedStr));
|
|
|
|
if (!Buf) {
|
|
MemFree (g_hHeap, 0, WrappedStr);
|
|
return FALSE;
|
|
}
|
|
|
|
StringCopy (Buf, WrappedStr);
|
|
MemFree (g_hHeap, 0, WrappedStr);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
PTSTR
|
|
pFindEndOfGroup (
|
|
IN PCTSTR Group
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pFindEndOfGroup returns the end of a piece of a group string.
|
|
|
|
Arguments:
|
|
|
|
Group - Specifies the start of the message group to find the end of.
|
|
|
|
Return Value:
|
|
|
|
A pointer to the nul or backslash terminating the message group.
|
|
|
|
--*/
|
|
|
|
{
|
|
PTSTR p;
|
|
|
|
p = _tcschr (Group, TEXT('\\'));
|
|
if (!p) {
|
|
p = GetEndOfString (Group);
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
|
|
PTSTR
|
|
pFindNextGroup (
|
|
IN PCTSTR Group
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pFindNextGroup returns a pointer to the next piece of a message group. In
|
|
other words, it locates the next backslash, advances one more character, and
|
|
returns the pointer to the rest of the string.
|
|
|
|
Arguments:
|
|
|
|
Group - Specifies the start of the current message group
|
|
|
|
Return Value:
|
|
|
|
A pointer to the next piece of the group, or a pointer to the nul terminator
|
|
if no more pieces exist.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
PTSTR Next;
|
|
|
|
Next = pFindEndOfGroup (Group);
|
|
if (*Next) {
|
|
Next = _tcsinc (Next);
|
|
}
|
|
|
|
return Next;
|
|
}
|
|
|
|
|
|
PTSTR
|
|
pExtractNextMsgGroup (
|
|
IN PCTSTR Group,
|
|
OUT PCTSTR *NextGroup OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pExtractNextMsgGroup locates the start and end of the current message group
|
|
piece, copies it into a new buffer, and returns a pointer to the next piece.
|
|
|
|
Arguments:
|
|
|
|
Group - Specifies the start of the current message group piece
|
|
|
|
NextGroup - Receives a pointer to the next message group piece
|
|
|
|
Return Value:
|
|
|
|
A pointer to the newly allocated string. The caller must free this by
|
|
calling FreePathString.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
PCTSTR p;
|
|
PTSTR Base;
|
|
|
|
p = pFindEndOfGroup (Group);
|
|
|
|
//
|
|
// Duplicate the subgroup
|
|
//
|
|
|
|
Base = AllocPathString (p - Group + 1);
|
|
if (Base) {
|
|
StringCopyAB (Base, Group, p);
|
|
}
|
|
|
|
//
|
|
// Supply caller with a pointer to the next subgroup,
|
|
// or a pointer to the nul character.
|
|
//
|
|
|
|
if (NextGroup) {
|
|
if (*p) {
|
|
p = _tcsinc (p);
|
|
}
|
|
|
|
*NextGroup = p;
|
|
}
|
|
|
|
return Base;
|
|
}
|
|
|
|
|
|
VOID
|
|
pLoadHandlerStringWorker (
|
|
IN UINT MessageId,
|
|
IN UINT RootId,
|
|
OUT PCTSTR *Str
|
|
)
|
|
{
|
|
if (MessageId && MessageId != RootId) {
|
|
*Str = GetStringResource (MessageId);
|
|
} else {
|
|
*Str = NULL;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
pLoadAllHandlerStrings (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pLoadAllHandlerStrings loads the string resources needed by all handlers.
|
|
The pointers are saved in a global array, and are used during
|
|
CreateReportText. When this module terminates, the global array is freed.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
INT i;
|
|
PTSTR p;
|
|
|
|
for (i = 0 ; g_HandlerNames[i].Id ; i++) {
|
|
g_HandlerNames[i].SubGroupStr = GetStringResource (g_HandlerNames[i].Id);
|
|
MYASSERT (g_HandlerNames[i].SubGroupStr);
|
|
g_HandlerNames[i].SubGroupStrLchars = LcharCount (g_HandlerNames[i].SubGroupStr);
|
|
}
|
|
|
|
for (i = 0 ; g_GenericList[i].Id ; i++) {
|
|
g_GenericList[i].SubGroupStr = GetStringResource (g_GenericList[i].Id);
|
|
MYASSERT (g_GenericList[i].SubGroupStr);
|
|
g_GenericList[i].SubGroupStrLchars = LcharCount (g_GenericList[i].SubGroupStr);
|
|
}
|
|
|
|
for (i = 0 ; g_MsgGroupRoot[i].MsgGroup ; i++) {
|
|
g_MsgGroupRoot[i].Name = GetStringResource (g_MsgGroupRoot[i].MsgGroup);
|
|
MYASSERT (g_MsgGroupRoot[i].Name);
|
|
g_MsgGroupRoot[i].NameLchars = LcharCount (g_MsgGroupRoot[i].Name);
|
|
|
|
pLoadHandlerStringWorker (
|
|
g_MsgGroupRoot[i].IntroId,
|
|
g_MsgGroupRoot[i].MsgGroup,
|
|
&g_MsgGroupRoot[i].IntroIdStr
|
|
);
|
|
|
|
pLoadHandlerStringWorker (
|
|
g_MsgGroupRoot[i].IntroIdHtml,
|
|
g_MsgGroupRoot[i].MsgGroup,
|
|
&g_MsgGroupRoot[i].IntroIdHtmlStr
|
|
);
|
|
|
|
pLoadHandlerStringWorker (
|
|
g_MsgGroupRoot[i].OtherId,
|
|
g_MsgGroupRoot[i].MsgGroup,
|
|
&g_MsgGroupRoot[i].OtherIdStr
|
|
);
|
|
|
|
pLoadHandlerStringWorker (
|
|
g_MsgGroupRoot[i].OtherIdHtml,
|
|
g_MsgGroupRoot[i].MsgGroup,
|
|
&g_MsgGroupRoot[i].OtherIdHtmlStr
|
|
);
|
|
}
|
|
|
|
for (i = 0; g_MapMessageToLevel[i].MessageId; i++) {
|
|
g_MapMessageToLevel[i].MessageStr = GetStringResource (g_MapMessageToLevel[i].MessageId);
|
|
MYASSERT (g_MapMessageToLevel[i].MessageStr);
|
|
p = _tcschr (g_MapMessageToLevel[i].MessageStr, TEXT('\\'));
|
|
if (p) {
|
|
*p = 0;
|
|
}
|
|
g_MapMessageToLevel[i].MessageLength = LcharCount (g_MapMessageToLevel[i].MessageStr);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
pFreeAllHandlerStrings (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pFreeAllHandlerStrings cleans up the global array of string
|
|
resources before process termination.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
INT i;
|
|
|
|
for (i = 0 ; g_HandlerNames[i].Id ; i++) {
|
|
FreeStringResource (g_HandlerNames[i].SubGroupStr);
|
|
g_HandlerNames[i].SubGroupStr = NULL;
|
|
g_HandlerNames[i].SubGroupStrLchars = 0;
|
|
}
|
|
|
|
for (i = 0 ; g_GenericList[i].Id ; i++) {
|
|
FreeStringResource (g_GenericList[i].SubGroupStr);
|
|
g_GenericList[i].SubGroupStr = NULL;
|
|
g_GenericList[i].SubGroupStrLchars = 0;
|
|
}
|
|
|
|
for (i = 0 ; g_MsgGroupRoot[i].MsgGroup ; i++) {
|
|
FreeStringResource (g_MsgGroupRoot[i].Name);
|
|
FreeStringResource (g_MsgGroupRoot[i].IntroIdStr);
|
|
FreeStringResource (g_MsgGroupRoot[i].IntroIdHtmlStr);
|
|
FreeStringResource (g_MsgGroupRoot[i].OtherIdStr);
|
|
FreeStringResource (g_MsgGroupRoot[i].OtherIdHtmlStr);
|
|
|
|
g_MsgGroupRoot[i].Name = NULL;
|
|
g_MsgGroupRoot[i].NameLchars = 0;
|
|
g_MsgGroupRoot[i].IntroIdStr = NULL;
|
|
g_MsgGroupRoot[i].IntroIdHtmlStr = NULL;
|
|
g_MsgGroupRoot[i].OtherIdStr = NULL;
|
|
g_MsgGroupRoot[i].OtherIdHtmlStr = NULL;
|
|
}
|
|
|
|
for (i = 0; g_MapMessageToLevel[i].MessageId; i++) {
|
|
FreeStringResource (g_MapMessageToLevel[i].MessageStr);
|
|
g_MapMessageToLevel[i].MessageStr = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
pIsThisTheHandler (
|
|
IN PCTSTR FullMsgGroup,
|
|
IN PHANDLER_LIST Handler
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pIsThisTheHandler compares the specified message group handler name against
|
|
the specified message group.
|
|
|
|
Arguments:
|
|
|
|
FullMsgGroup - Specifies the name of the message group in question
|
|
|
|
Handler - Specifies the message group handler to compare against FullMsgGroup
|
|
|
|
Return Value:
|
|
|
|
TRUE if Handler handles the FullMsgGroup message, or FALSE if not.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCTSTR p;
|
|
CHARTYPE ch;
|
|
|
|
if (!Handler) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (StringIMatchLcharCount (
|
|
FullMsgGroup,
|
|
Handler->SubGroupStr,
|
|
Handler->SubGroupStrLchars
|
|
)) {
|
|
|
|
p = LcharCountToPointer (FullMsgGroup, Handler->SubGroupStrLchars);
|
|
ch = _tcsnextc (p);
|
|
|
|
if (ch == TEXT('\\') || ch == 0) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
PHANDLER_LIST
|
|
pSearchForMsgGroupHandler (
|
|
IN PCTSTR Str
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pSearchForMsgGroupHandler scans the list of handlers for one that can
|
|
handle the current message group. A pointer to the handler structure is
|
|
returned.
|
|
|
|
Arguments:
|
|
|
|
Str - Specifies the message group to locate a handler for, and may include
|
|
backslashes and subgroups.
|
|
|
|
Return Value:
|
|
|
|
A pointer to the handler struct if found, or the default handler if no
|
|
handler exists for the message group.
|
|
|
|
--*/
|
|
|
|
{
|
|
PHANDLER_LIST Handler;
|
|
|
|
for (Handler = g_HandlerNames ; Handler->Id ; Handler++) {
|
|
if (pIsThisTheHandler (Str, Handler)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Handler;
|
|
}
|
|
|
|
|
|
PMSGGROUP_PROPS
|
|
pFindMsgGroupStruct (
|
|
IN PCTSTR MsgGroup
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Desciption:
|
|
|
|
pFindMsgGroupStruct returns the pointer to the root message group structure,
|
|
which defines attributes about the message group, such as introduction text
|
|
and intro text for the non-handled messages.
|
|
|
|
Arguments:
|
|
|
|
MsgGroup - Specifies the text name of the message group, which may include
|
|
sub groups
|
|
|
|
Return Value:
|
|
|
|
A pointer to the root message group struct, or NULL if it is not defined.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCTSTR p;
|
|
CHARTYPE ch;
|
|
PMSGGROUP_PROPS Group;
|
|
|
|
for (Group = g_MsgGroupRoot ; Group->MsgGroup ; Group++) {
|
|
if (StringIMatchLcharCount (MsgGroup, Group->Name, Group->NameLchars)) {
|
|
p = LcharCountToPointer (MsgGroup, Group->NameLchars);
|
|
ch = _tcsnextc (p);
|
|
if (ch == TEXT('\\') || ch == 0) {
|
|
return Group;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PMSGGROUP_PROPS
|
|
pFindMsgGroupStructById (
|
|
IN UINT MsgGroupId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Desciption:
|
|
|
|
pFindMsgGroupStructById returns the pointer to the root message group structure,
|
|
which defines attributes about the message group, such as introduction text
|
|
and intro text for the non-handled messages. It searches based on the string
|
|
ID of the group.
|
|
|
|
Arguments:
|
|
|
|
MsgGroupId - Specifies the MSG_* constant of the group to find
|
|
|
|
Return Value:
|
|
|
|
A pointer to the root message group struct, or NULL if it is not defined.
|
|
|
|
--*/
|
|
|
|
{
|
|
PMSGGROUP_PROPS Group;
|
|
|
|
for (Group = g_MsgGroupRoot ; Group->MsgGroup ; Group++) {
|
|
if (Group->MsgGroup == MsgGroupId) {
|
|
return Group;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pAddMsgGroupString (
|
|
IN OUT PGROWBUFFER StringBuf,
|
|
IN PMSGGROUP_PROPS Group,
|
|
IN PCTSTR MsgGroup,
|
|
IN BOOL HtmlFormat,
|
|
IN DWORD Level
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pAddMsgGroupString formats a message group into a heirarchy
|
|
for the report. That is, if group is a string such as:
|
|
|
|
foo\bar\moo
|
|
|
|
then the following text is added to the report:
|
|
|
|
foo
|
|
bar
|
|
moo
|
|
|
|
If HtmlFormat is TRUE, then the bold attribute is enabled
|
|
for the group string.
|
|
|
|
Arguments:
|
|
|
|
StringBuf - Specifies the current report to append text to.
|
|
|
|
Group - Specifies the group properties of this item
|
|
|
|
MsgGroup - Specifies the message group to add to the report
|
|
|
|
HtmlFormat - Specifies TRUE if the message group should be formatted
|
|
with HTML tags, or FALSE if it should be plain
|
|
text.
|
|
|
|
Level - Specifies the severity of the message
|
|
|
|
Return Value:
|
|
|
|
TRUE if multiple lines were added, or FALSE if zero or one line was added.
|
|
|
|
--*/
|
|
|
|
{
|
|
UINT Spaces = 0;
|
|
TCHAR SpaceBuf[MAX_SPACES * 6];
|
|
PCTSTR SubMsgGroup = NULL;
|
|
PCTSTR LastSubMsgGroup = NULL;
|
|
PCTSTR NextMsgGroup;
|
|
PCTSTR LastMsgGroup;
|
|
PCTSTR Msg = NULL;
|
|
TCHAR SummaryStrHeader[512] = TEXT("");
|
|
TCHAR SummaryStrItem[512] = TEXT("");
|
|
UINT lineCount = 0;
|
|
|
|
Level &= REPORTLEVEL_ALL;
|
|
MYASSERT (ONEBITSET (Level));
|
|
|
|
NextMsgGroup = MsgGroup;
|
|
LastMsgGroup = g_LastMsgGroupBuf;
|
|
|
|
if (HtmlFormat) {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("<B>"));
|
|
}
|
|
|
|
if (g_ListFormat) {
|
|
StackStringCopy (SummaryStrHeader, Group->Name);
|
|
}
|
|
|
|
SpaceBuf[0] = 0;
|
|
|
|
while (*NextMsgGroup) {
|
|
__try {
|
|
SubMsgGroup = pExtractNextMsgGroup (NextMsgGroup, &NextMsgGroup);
|
|
if (*LastMsgGroup) {
|
|
LastSubMsgGroup = pExtractNextMsgGroup (LastMsgGroup, &LastMsgGroup);
|
|
|
|
if (StringIMatch (LastSubMsgGroup, SubMsgGroup)) {
|
|
__leave;
|
|
}
|
|
|
|
if (!Spaces) {
|
|
if (!HtmlFormat) {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
|
|
}
|
|
}
|
|
|
|
LastMsgGroup = GetEndOfString (LastMsgGroup);
|
|
}
|
|
|
|
//
|
|
// Add indenting
|
|
//
|
|
|
|
if (Spaces) {
|
|
pAppendStringToGrowBuf (StringBuf, SpaceBuf);
|
|
}
|
|
|
|
//
|
|
// Add subgroup
|
|
//
|
|
|
|
Msg = NULL;
|
|
if (!g_ListFormat) {
|
|
if (HtmlFormat) {
|
|
pAppendStringToGrowBuf (StringBuf, SubMsgGroup);
|
|
} else {
|
|
Msg = pEncodeMessage (SubMsgGroup, FALSE);
|
|
pAppendStringToGrowBuf (StringBuf, Msg);
|
|
}
|
|
} else {
|
|
if (*NextMsgGroup) {
|
|
StringCopy (SummaryStrHeader, SubMsgGroup);
|
|
} else {
|
|
StringCopy (SummaryStrItem, SubMsgGroup);
|
|
}
|
|
}
|
|
|
|
if (HtmlFormat) {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("<BR>\r\n"));
|
|
} else {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
|
|
}
|
|
}
|
|
|
|
__finally {
|
|
|
|
if (HtmlFormat) {
|
|
StringCat (SpaceBuf, TEXT(" "));
|
|
StringCat (SpaceBuf, TEXT(" "));
|
|
} else {
|
|
SpaceBuf[Spaces] = TEXT(' ');
|
|
SpaceBuf[Spaces+1] = TEXT(' ');
|
|
SpaceBuf[Spaces+2] = 0;
|
|
}
|
|
|
|
lineCount++;
|
|
Spaces += 2;
|
|
MYASSERT (Spaces < MAX_SPACES - 2);
|
|
|
|
FreePathString (SubMsgGroup);
|
|
FreePathString (LastSubMsgGroup);
|
|
|
|
SubMsgGroup = NULL;
|
|
LastSubMsgGroup = NULL;
|
|
|
|
if (Msg) {
|
|
FreeText (Msg);
|
|
Msg = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (g_ListFormat) {
|
|
if (SummaryStrHeader[0] && SummaryStrItem[0]) {
|
|
pStartHeaderLine (StringBuf);
|
|
pDumpDwordToGrowBuf (StringBuf, Level);
|
|
Msg = pEncodeMessage (SummaryStrHeader, FALSE);
|
|
pAppendStringToGrowBuf (StringBuf, Msg);
|
|
FreeText (Msg);
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
|
|
|
|
pDumpDwordToGrowBuf (StringBuf, Level);
|
|
Msg = pEncodeMessage (SummaryStrItem, FALSE);
|
|
pAppendStringToGrowBuf (StringBuf, Msg);
|
|
FreeText (Msg);
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
|
|
}
|
|
}
|
|
|
|
if (HtmlFormat) {
|
|
//
|
|
// Add a closing </B> just in case title is missing it.
|
|
//
|
|
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("</B>"));
|
|
}
|
|
|
|
StringCopy (g_LastMsgGroupBuf, MsgGroup);
|
|
return lineCount != 1;
|
|
}
|
|
|
|
|
|
PCTSTR
|
|
pEscapeHtmlTitleText (
|
|
IN PCTSTR UnescapedText
|
|
)
|
|
{
|
|
UINT Count;
|
|
PCTSTR p;
|
|
PTSTR Buf;
|
|
PTSTR q;
|
|
CHARTYPE ch;
|
|
BOOL Escape;
|
|
|
|
//
|
|
// Escape everything EXCEPT the <B> and </B> tags, which are used in
|
|
// subgroup formatting. This includes ampersands.
|
|
//
|
|
|
|
//
|
|
// count all of the tags that we wish to change < into <
|
|
//
|
|
|
|
Count = 1;
|
|
p = UnescapedText;
|
|
while (*p) {
|
|
ch = _tcsnextc (p);
|
|
|
|
if (ch == TEXT('<')) {
|
|
if (!StringIMatchTcharCount (p, TEXT("</B>"), 4) &&
|
|
!StringIMatchTcharCount (p, TEXT("<B>"), 3)
|
|
) {
|
|
Count++;
|
|
}
|
|
} else if (ch == TEXT('&')) {
|
|
Count++;
|
|
}
|
|
|
|
p = _tcsinc (p);
|
|
}
|
|
|
|
//
|
|
// Allocate a dest buffer
|
|
//
|
|
|
|
Buf = AllocText (LcharCount (UnescapedText) + (Count * 5) + 1);
|
|
MYASSERT (Buf);
|
|
|
|
p = UnescapedText;
|
|
q = Buf;
|
|
|
|
//
|
|
// Transfer unescaped text into output buffer, leaving only
|
|
// the tags we want. Convert ampersand into &.
|
|
//
|
|
|
|
while (*p) {
|
|
ch = _tcsnextc (p);
|
|
Escape = FALSE;
|
|
|
|
if (ch == TEXT('<')) {
|
|
if (!StringIMatchTcharCount (p, TEXT("</B>"), 4) &&
|
|
!StringIMatchTcharCount (p, TEXT("<B>"), 3)
|
|
) {
|
|
Escape = TRUE;
|
|
}
|
|
} else if (ch == TEXT('&')) {
|
|
Escape = TRUE;
|
|
}
|
|
|
|
if (Escape) {
|
|
if (ch == TEXT('<')) {
|
|
StringCopy (q, TEXT("<"));
|
|
} else {
|
|
StringCopy (q, TEXT("&"));
|
|
}
|
|
|
|
q = GetEndOfString (q);
|
|
} else {
|
|
_copytchar (q, p);
|
|
q = _tcsinc (q);
|
|
}
|
|
|
|
p = _tcsinc (p);
|
|
}
|
|
|
|
*q = 0;
|
|
|
|
return Buf;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pGenericItemList (
|
|
IN CALLCONTEXT Context,
|
|
IN OUT PGROWBUFFER StringBuf,
|
|
IN PCTSTR SubGroup,
|
|
IN PCTSTR Message,
|
|
IN DWORD Level,
|
|
IN BOOL HtmlFormat,
|
|
IN OUT PVOID *State,
|
|
IN UINT MsgIdTop,
|
|
IN UINT MsgIdTopHtml,
|
|
IN UINT MsgIdBottom,
|
|
IN UINT MsgIdBottomHtml,
|
|
IN PCTSTR **FormatArgs, OPTIONAL
|
|
IN DWORD Flags,
|
|
IN PMSGGROUP_PROPS Props
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pGenericItemList formats a group of messages in the following
|
|
format:
|
|
|
|
Group
|
|
SubGroup
|
|
|
|
<intro>
|
|
|
|
Item 1
|
|
Item 2
|
|
Item n
|
|
|
|
<conclusion>
|
|
|
|
|
|
The <intro> and <conclusion> are optional.
|
|
|
|
The subgroups are declared in SUBGROUP_LISTS at the top of this
|
|
file.
|
|
|
|
Arguments:
|
|
|
|
Context - Specifies the way the handler is being called,
|
|
either to initialize, process an item or clean up.
|
|
|
|
StringBuf - Specifies the current report. Append text to
|
|
this buffer via the pAppendStringToGrowBuf
|
|
routine.
|
|
|
|
SubGroup - Specifies the text of the subgrup
|
|
|
|
Message - Specifies the message text
|
|
|
|
Level - Specifies the severity level of Message (info, error, etc.)
|
|
|
|
HtmlFormat - Specifies TRUE if the text should be written
|
|
with HTML formatting tags, or FALSE if the
|
|
text should be formatted as plain text.
|
|
(See CreateReport comments for HTML tag
|
|
info.)
|
|
|
|
State - A pointer to state, defined by the handler. State
|
|
holds an arbitrary 32-bit value that the handler
|
|
maintains. Typically the handler allocates a
|
|
struct when Context is INIT, then uses the struct
|
|
for each PROCESS_ITEM, and finally cleans up the
|
|
allocation when Context is CLEANUP.
|
|
|
|
MsgIdTop - Specifies the ID of text that should appear above
|
|
the top of the list. This includes the section
|
|
title. This message ID should contain plain text.
|
|
Zero indicates no text.
|
|
|
|
MsgIdTopHtml - Specifies the ID of text that is the same as
|
|
MsgIdTop, except it must be formatted with
|
|
HTML tags. If MsgIdTop is zero, MsgIdTopHtml
|
|
must also be zero.
|
|
|
|
MsgIdBottom - Similar to MsgIdTop, except specifies ID of
|
|
text at the bottom of the list. Zero indicates
|
|
no text.
|
|
|
|
MsgIdBottomHtml - Specifies the same text as MsgIdBottom, except
|
|
formatted with HTML tags. If MsgIdBottom is
|
|
zero, MsgIdBottomHtml must also be zero.
|
|
|
|
FormatArgs - Specifies an optional pointer to an array of 4 pointers,
|
|
each associated with the previous MsgIds (first with MsgIdTop a.s.o.)
|
|
If not NULL, each one points to an array of actual strings to replace
|
|
the placeholders in the message (%1 -> first string in this array a.s.o.)
|
|
|
|
Flags - Specifies a list of flags used for formatting (like bold case items)
|
|
|
|
Return Value:
|
|
|
|
TRUE if the handler was successful, or FALSE if an
|
|
error occurs.
|
|
|
|
--*/
|
|
|
|
{
|
|
PITEMLIST Items;
|
|
PCTSTR Msg;
|
|
UINT Count;
|
|
UINT u;
|
|
PCTSTR EncodedText;
|
|
PGROWLIST currentList;
|
|
BOOL bMessageItems;
|
|
INT pass;
|
|
PCTSTR altMsg;
|
|
BOOL headerAdded = FALSE;
|
|
BOOL footerAdded = FALSE;
|
|
|
|
Level &= REPORTLEVEL_ALL;
|
|
MYASSERT (ONEBITSET (Level));
|
|
|
|
Items = *((PITEMLIST *) State);
|
|
|
|
switch (Context) {
|
|
case INIT:
|
|
//
|
|
// Allocate grow list to hold all hardware
|
|
//
|
|
|
|
MYASSERT (!Items);
|
|
Items = (PITEMLIST) MemAlloc (
|
|
g_hHeap,
|
|
HEAP_ZERO_MEMORY,
|
|
sizeof (ITEMLIST)
|
|
);
|
|
break;
|
|
|
|
case PROCESS_ITEM:
|
|
MYASSERT (Items);
|
|
|
|
//
|
|
// Add the subgroup to the grow list
|
|
//
|
|
|
|
|
|
if (HtmlFormat) {
|
|
Msg = pEscapeHtmlTitleText (SubGroup);
|
|
} else {
|
|
Msg = pEncodeMessage (SubGroup, FALSE);
|
|
}
|
|
|
|
if (FLAGSET_ENABLE_MESSAGE_ITEMS (Flags) && Message && *Message) {
|
|
GrowListAppendString (&Items->MessageItems, Msg);
|
|
GrowListAppendString (&Items->Messages, Message);
|
|
} else {
|
|
GrowListAppendString (&Items->List, Msg);
|
|
}
|
|
|
|
FreeText (Msg);
|
|
break;
|
|
|
|
case CLEANUP:
|
|
MYASSERT (Items);
|
|
|
|
//
|
|
// Add instructions, then add each item in the grow list
|
|
//
|
|
|
|
for (pass = 0; pass < 2; pass++) {
|
|
|
|
//
|
|
// are we processing message items this step, or just list entries?
|
|
//
|
|
bMessageItems = FLAGSET_MESSAGE_ITEMS_FIRST (Flags) && pass == 0 ||
|
|
!FLAGSET_MESSAGE_ITEMS_FIRST (Flags) && pass != 0;
|
|
|
|
if (bMessageItems) {
|
|
currentList = &Items->MessageItems;
|
|
} else {
|
|
currentList = &Items->List;
|
|
}
|
|
|
|
Count = GrowListGetSize (currentList);
|
|
|
|
if (Count) {
|
|
if (HtmlFormat) {
|
|
//pAppendStringToGrowBuf (StringBuf, TEXT("<UL>"));
|
|
|
|
if (bMessageItems) {
|
|
if (HtmlFormat && FLAGSET_INDENT_MESSAGE (Flags)) {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("<UL>"));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!headerAdded) {
|
|
|
|
headerAdded = TRUE;
|
|
|
|
if (MsgIdTop && MsgIdTopHtml) {
|
|
//
|
|
// check if FormatArgs and the corresponding pointer for MsgId are not NULL
|
|
//
|
|
if (FormatArgs && (HtmlFormat ? FormatArgs[1] : FormatArgs[0])) {
|
|
Msg = ParseMessageID (
|
|
HtmlFormat ? MsgIdTopHtml : MsgIdTop,
|
|
HtmlFormat ? FormatArgs[1] : FormatArgs[0]
|
|
);
|
|
} else {
|
|
Msg = GetStringResource (HtmlFormat ? MsgIdTopHtml : MsgIdTop);
|
|
}
|
|
|
|
if (Msg) {
|
|
altMsg = Msg;
|
|
|
|
if (g_ListFormat) {
|
|
pStartHeaderLine (StringBuf);
|
|
pDumpDwordToGrowBuf (StringBuf, Level);
|
|
|
|
//
|
|
// Determine heading from the root group, the subgroup
|
|
// or the message text (depending on macro expansion
|
|
// list flags)
|
|
//
|
|
|
|
if (FLAGSET_USESUBGROUP (Flags)) {
|
|
//
|
|
// Get text from the root group
|
|
//
|
|
|
|
MYASSERT (!FLAGSET_USEROOT (Flags));
|
|
altMsg = SubGroup;
|
|
} else if (FLAGSET_USEROOT (Flags)) {
|
|
//
|
|
// Get text from the subgroup
|
|
//
|
|
|
|
MYASSERT (!FLAGSET_USESUBGROUP (Flags));
|
|
altMsg = Props->Name;
|
|
} else {
|
|
//
|
|
// We assume that the plain text message has a heading
|
|
// that gives the text to put in the list view.
|
|
//
|
|
|
|
pCutAfterFirstLine ((PTSTR)altMsg);
|
|
}
|
|
|
|
altMsg = pEncodeMessage (altMsg, FALSE);
|
|
}
|
|
|
|
if (HtmlFormat) {
|
|
pAppendStringToGrowBuf (StringBuf, altMsg);
|
|
} else {
|
|
pWrapStringToGrowBuf (StringBuf, altMsg, 0, 0);
|
|
}
|
|
|
|
if (g_ListFormat) {
|
|
pWriteNewLine (StringBuf);
|
|
FreeText (altMsg);
|
|
}
|
|
|
|
FreeStringResource (Msg);
|
|
}
|
|
} else if (g_ListFormat) {
|
|
//
|
|
// No detailed heading; get list view text (just
|
|
// like above). NOTE: there is no message text.
|
|
//
|
|
|
|
pStartHeaderLine (StringBuf);
|
|
pDumpDwordToGrowBuf (StringBuf, Level);
|
|
|
|
if (FLAGSET_USESUBGROUP (Flags)) {
|
|
MYASSERT (!FLAGSET_USEROOT (Flags));
|
|
altMsg = SubGroup;
|
|
} else {
|
|
MYASSERT (FLAGSET_USEROOT (Flags));
|
|
altMsg = Props->Name;
|
|
}
|
|
|
|
pAppendStringToGrowBuf (StringBuf, altMsg);
|
|
pWriteNewLine (StringBuf);
|
|
}
|
|
}
|
|
|
|
for (u = 0 ; u < Count ; u++) {
|
|
|
|
if (g_ListFormat) {
|
|
pDumpDwordToGrowBuf (StringBuf, Level);
|
|
}
|
|
|
|
if (!bMessageItems) {
|
|
if (HtmlFormat) {
|
|
if (FLAGSET_BOLDITEMS(Flags)) {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("<B>"));
|
|
}
|
|
pAppendStringToGrowBuf (StringBuf, GrowListGetString (currentList, u));
|
|
if (FLAGSET_BOLDITEMS(Flags)) {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("</B>"));
|
|
}
|
|
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("<BR>"));
|
|
} else {
|
|
EncodedText = pEncodeMessage (GrowListGetString (currentList, u), FALSE);
|
|
if (EncodedText) {
|
|
pWrapStringToGrowBuf (StringBuf, EncodedText, 4, 2);
|
|
FreeText (EncodedText);
|
|
}
|
|
}
|
|
} else {
|
|
if (HtmlFormat) {
|
|
if (FLAGSET_BOLDITEMS(Flags)) {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("<B>"));
|
|
}
|
|
}
|
|
|
|
if (HtmlFormat) {
|
|
pAppendStringToGrowBuf (StringBuf, GrowListGetString (currentList, u));
|
|
} else {
|
|
EncodedText = pEncodeMessage (GrowListGetString (currentList, u), FALSE);
|
|
if (EncodedText) {
|
|
pWrapStringToGrowBuf (StringBuf, EncodedText, 0, 0);
|
|
FreeText (EncodedText);
|
|
}
|
|
}
|
|
|
|
if (HtmlFormat) {
|
|
if (FLAGSET_BOLDITEMS(Flags)) {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("</B>"));
|
|
}
|
|
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("<BR>"));
|
|
}
|
|
//
|
|
// now add the message itself
|
|
//
|
|
if (!g_ListFormat) {
|
|
EncodedText = pEncodeMessage (GrowListGetString (&Items->Messages, u), HtmlFormat);
|
|
if (EncodedText) {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
|
|
if (HtmlFormat) {
|
|
|
|
pAppendStringToGrowBuf (StringBuf, EncodedText);
|
|
if (Count == (u - 1) && FLAGSET_INDENT_MESSAGE (Flags)) {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("<BR>"));
|
|
} else {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("<P>"));
|
|
}
|
|
|
|
} else {
|
|
pWrapStringToGrowBuf (StringBuf, EncodedText, 4, 0);
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
|
|
}
|
|
FreeText (EncodedText);
|
|
}
|
|
}
|
|
}
|
|
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
|
|
}
|
|
|
|
if (!bMessageItems) {
|
|
//
|
|
// Terminate the list
|
|
//
|
|
|
|
if (HtmlFormat) {
|
|
if (FLAGSET_BOLDITEMS(Flags)) {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("</B>"));
|
|
}
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("</UL>"));
|
|
}
|
|
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
|
|
|
|
} else {
|
|
//
|
|
// Terminate the messages
|
|
//
|
|
|
|
if (HtmlFormat) {
|
|
//pAppendStringToGrowBuf (StringBuf, TEXT("</UL>"));
|
|
|
|
if (FLAGSET_INDENT_MESSAGE (Flags)) {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("</UL>"));
|
|
}
|
|
}
|
|
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
|
|
}
|
|
|
|
if (!g_ListFormat && !footerAdded) {
|
|
footerAdded = TRUE;
|
|
|
|
if (MsgIdBottom && MsgIdBottomHtml) {
|
|
//
|
|
// check if FormatArgs and the corresponding pointer for MsgId are not NULL
|
|
//
|
|
if (FormatArgs && (HtmlFormat ? FormatArgs[3] : FormatArgs[2])) {
|
|
Msg = ParseMessageID (
|
|
HtmlFormat ? MsgIdBottomHtml : MsgIdBottom,
|
|
HtmlFormat ? FormatArgs[3] : FormatArgs[2]
|
|
);
|
|
} else {
|
|
Msg = GetStringResource (HtmlFormat ? MsgIdBottomHtml : MsgIdBottom);
|
|
}
|
|
|
|
if (Msg) {
|
|
if (HtmlFormat) {
|
|
pAppendStringToGrowBuf (StringBuf, Msg);
|
|
} else {
|
|
pWrapStringToGrowBuf (StringBuf, Msg, 0, 0);
|
|
}
|
|
|
|
FreeStringResource (Msg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free the grow list
|
|
//
|
|
|
|
FreeGrowList (&Items->List);
|
|
FreeGrowList (&Items->MessageItems);
|
|
FreeGrowList (&Items->Messages);
|
|
MemFree (g_hHeap, 0, Items);
|
|
Items = NULL;
|
|
break;
|
|
}
|
|
|
|
*((PITEMLIST *) State) = Items;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
pCleanUpOtherDevices (
|
|
VOID
|
|
)
|
|
{
|
|
MEMDB_ENUM e;
|
|
HASHTABLE Table;
|
|
PCTSTR Str;
|
|
TCHAR ReportRoot[MEMDB_MAX];
|
|
TCHAR Pattern[MEMDB_MAX];
|
|
TCHAR OtherDevices[MEMDB_MAX];
|
|
UINT Bytes;
|
|
PCTSTR p;
|
|
|
|
//
|
|
// Prepare the report root Hardware\Incompatible Hardware\Other devices
|
|
//
|
|
|
|
Str = GetStringResource (MSG_INCOMPATIBLE_HARDWARE_ROOT);
|
|
MYASSERT (Str);
|
|
if (!Str) {
|
|
return;
|
|
}
|
|
StringCopy (ReportRoot, Str);
|
|
FreeStringResource (Str);
|
|
|
|
Str = GetStringResource (MSG_INCOMPATIBLE_HARDWARE_PNP_SUBGROUP);
|
|
MYASSERT (Str);
|
|
if (!Str) {
|
|
return;
|
|
}
|
|
StringCopy (AppendWack (ReportRoot), Str);
|
|
FreeStringResource (Str);
|
|
|
|
Str = GetStringResource (MSG_UNKNOWN_DEVICE_CLASS);
|
|
MYASSERT (Str);
|
|
if (!Str) {
|
|
return;
|
|
}
|
|
StringCopy (OtherDevices, Str);
|
|
FreeStringResource (Str);
|
|
|
|
//
|
|
// Enumerate the entries in this root
|
|
//
|
|
|
|
if (MemDbGetValueEx (&e, MEMDB_CATEGORY_REPORT, ReportRoot, OtherDevices)) {
|
|
|
|
Table = HtAlloc();
|
|
|
|
do {
|
|
//
|
|
// Add the device name to the table
|
|
//
|
|
|
|
HtAddString (Table, e.szName);
|
|
|
|
} while (MemDbEnumNextValue (&e));
|
|
|
|
//
|
|
// Now search all other classes of the report
|
|
//
|
|
|
|
MemDbBuildKey (Pattern, MEMDB_CATEGORY_REPORT, ReportRoot, TEXT("*"), NULL);
|
|
AppendWack (OtherDevices);
|
|
Bytes = (PBYTE) GetEndOfString (OtherDevices) - (PBYTE) OtherDevices;
|
|
|
|
if (MemDbEnumFirstValue (&e, Pattern, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) {
|
|
do {
|
|
|
|
//
|
|
// Skip "Other devices"
|
|
//
|
|
|
|
if (StringIMatchByteCount (e.szName, OtherDevices, Bytes)) {
|
|
continue;
|
|
}
|
|
|
|
p = _tcschr (e.szName, TEXT('\\'));
|
|
MYASSERT (p);
|
|
|
|
if (p) {
|
|
p = _tcsinc (p);
|
|
|
|
if (HtFindString (Table, p)) {
|
|
//
|
|
// This is a match, so remove the Other devices entry
|
|
//
|
|
|
|
StringCopy (Pattern, MEMDB_CATEGORY_REPORT);
|
|
StringCopy (AppendWack (Pattern), ReportRoot);
|
|
StringCopy (AppendWack (Pattern), OtherDevices);
|
|
StringCopy (AppendWack (Pattern), p);
|
|
|
|
//
|
|
// NOTE: This delete is safe because we know we cannot be
|
|
// enumerating this node.
|
|
//
|
|
|
|
MemDbDeleteValue (Pattern);
|
|
}
|
|
}
|
|
|
|
} while (MemDbEnumNextValue (&e));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
pAddPnpHardwareToReport (
|
|
IN CALLCONTEXT Context,
|
|
IN PMSGGROUP_PROPS Group,
|
|
IN OUT PGROWBUFFER StringBuf,
|
|
IN PCTSTR SubGroup,
|
|
IN PCTSTR Message,
|
|
IN DWORD Level,
|
|
IN BOOL HtmlFormat,
|
|
IN OUT PVOID *State,
|
|
IN DWORD Arg
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pAddPnpHardwareToReport formats the incompatible PNP hardware differently
|
|
than the generic lists. The format includes the hardware class, followed
|
|
by device names, which are indented an extra two spaces.
|
|
|
|
Arguments:
|
|
|
|
Context - Specifies the way the handler is being called, either to
|
|
initialize, process an item or clean up.
|
|
|
|
StringBuf - Specifies the current report. Append text to this buffer via
|
|
the pAppendStringToGrowBuf routine.
|
|
|
|
Group - Specifies the group properties of this item
|
|
|
|
SubGroup - Specifies the message subgroup, and does not include the root
|
|
message group.
|
|
|
|
Message - Specifies the message text
|
|
|
|
Level - Specifies the severity level of Message (info, error, etc.)
|
|
|
|
HtmlFormat - Specifies TRUE if the text should be written with HTML
|
|
formatting tags, or FALSE if the text should be formatted as
|
|
plain text. (See CreateReport comments for HTML tag info.)
|
|
|
|
State - Holds a pointer to the formatting state.
|
|
|
|
Arg - The DWORD argument from the macro expansion list
|
|
|
|
Return Value:
|
|
|
|
TRUE if the handler was successful, or FALSE if an error occurs.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPNPFORMATSTATE FormatState;
|
|
PCTSTR p;
|
|
PCTSTR Msg;
|
|
TCHAR Class[MEMDB_MAX];
|
|
UINT MsgIdTop;
|
|
UINT MsgIdTopHtml;
|
|
UINT MsgIdBottom;
|
|
UINT MsgIdBottomHtml;
|
|
PCTSTR EncodedText;
|
|
TCHAR fmtLine[1024];
|
|
PCTSTR msg;
|
|
|
|
Level &= REPORTLEVEL_ALL;
|
|
MYASSERT (ONEBITSET (Level));
|
|
|
|
switch (Arg) {
|
|
|
|
case 0:
|
|
MsgIdTop = MSG_HARDWARE_UNSUPPORTED_INSTRUCTIONS;
|
|
MsgIdTopHtml = MSG_HARDWARE_UNSUPPORTED_INSTRUCTIONS_HTML;
|
|
MsgIdBottom = MSG_HARDWARE_UNSUPPORTED_INSTRUCTIONS2;
|
|
MsgIdBottomHtml = MSG_HARDWARE_UNSUPPORTED_INSTRUCTIONS_HTML2;
|
|
break;
|
|
|
|
default:
|
|
case 1:
|
|
MsgIdTop = MSG_HARDWARE_PNP_INSTRUCTIONS;
|
|
MsgIdTopHtml = MSG_HARDWARE_PNP_INSTRUCTIONS_HTML;
|
|
MsgIdBottom = MSG_HARDWARE_PNP_INSTRUCTIONS2;
|
|
MsgIdBottomHtml = MSG_HARDWARE_PNP_INSTRUCTIONS2_HTML;
|
|
break;
|
|
|
|
case 2:
|
|
MsgIdTop = MSG_HARDWARE_REINSTALL_PNP_INSTRUCTIONS;
|
|
MsgIdTopHtml = MSG_HARDWARE_REINSTALL_PNP_INSTRUCTIONS_HTML;
|
|
MsgIdBottom = MSG_HARDWARE_REINSTALL_PNP_INSTRUCTIONS2;
|
|
MsgIdBottomHtml = MSG_HARDWARE_REINSTALL_PNP_INSTRUCTIONS_HTML2;
|
|
//
|
|
// make this a warning icon
|
|
//
|
|
Level = REPORTLEVEL_WARNING;
|
|
break;
|
|
}
|
|
|
|
switch (Context) {
|
|
|
|
case INIT:
|
|
FormatState = MemAlloc (g_hHeap, 0, sizeof (PNPFORMATSTATE));
|
|
ZeroMemory (FormatState, sizeof (PNPFORMATSTATE));
|
|
|
|
*State = FormatState;
|
|
|
|
//
|
|
// Special filtering is performed on Other devices, to remove
|
|
// duplicates. If we find that something in Other devices is
|
|
// listed in another device class, we remove the copy in
|
|
// Other devices.
|
|
//
|
|
|
|
pCleanUpOtherDevices();
|
|
break;
|
|
|
|
case PROCESS_ITEM:
|
|
FormatState = *((PPNPFORMATSTATE *) State);
|
|
|
|
p = _tcschr (SubGroup, TEXT('\\'));
|
|
MYASSERT (p);
|
|
|
|
if (!p) {
|
|
break;
|
|
}
|
|
|
|
StringCopyAB (Class, SubGroup, p);
|
|
p = _tcsinc (p);
|
|
|
|
if (!StringMatch (Class, FormatState->LastClass)) {
|
|
|
|
//
|
|
// End the previous class
|
|
//
|
|
|
|
if (*FormatState->LastClass) {
|
|
if (HtmlFormat) {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("<BR></UL>\r\n"));
|
|
} else {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
|
|
}
|
|
|
|
} else if (MsgIdTop) {
|
|
|
|
//
|
|
// The very first message gets a heading
|
|
//
|
|
|
|
Msg = GetStringResource (HtmlFormat ? MsgIdTopHtml : MsgIdTop);
|
|
if (Msg) {
|
|
if (g_ListFormat) {
|
|
pStartHeaderLine (StringBuf);
|
|
pDumpDwordToGrowBuf (StringBuf, Level);
|
|
pCutAfterFirstLine ((PTSTR)Msg);
|
|
msg = pEncodeMessage (Msg, FALSE);
|
|
}
|
|
|
|
if (HtmlFormat) {
|
|
pAppendStringToGrowBuf (StringBuf, Msg);
|
|
} else {
|
|
pWrapStringToGrowBuf (StringBuf, Msg, 0, 0);
|
|
}
|
|
|
|
if (g_ListFormat) {
|
|
pWriteNewLine (StringBuf);
|
|
FreeText (msg);
|
|
}
|
|
|
|
FreeStringResource (Msg);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Begin a new class
|
|
//
|
|
|
|
StringCopy (FormatState->LastClass, Class);
|
|
|
|
if (!g_ListFormat) {
|
|
if (HtmlFormat) {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("<UL><B>"));
|
|
pAppendStringToGrowBuf (StringBuf, Class);
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("</B><BR>"));
|
|
} else {
|
|
|
|
EncodedText = pEncodeMessage (Class, FALSE);
|
|
if (EncodedText) {
|
|
pWrapStringToGrowBuf (StringBuf, EncodedText, 4, 2);
|
|
FreeText (EncodedText);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
|
|
}
|
|
|
|
//
|
|
// Add the device name
|
|
//
|
|
|
|
if (!g_ListFormat) {
|
|
if (HtmlFormat) {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT(" "));
|
|
}
|
|
|
|
if (HtmlFormat) {
|
|
pAppendStringToGrowBuf (StringBuf, p);
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("<BR>"));
|
|
} else {
|
|
EncodedText = pEncodeMessage (p, HtmlFormat);
|
|
if (EncodedText) {
|
|
pWrapStringToGrowBuf (StringBuf, EncodedText, 6, 2);
|
|
FreeText (EncodedText);
|
|
}
|
|
}
|
|
} else {
|
|
pDumpDwordToGrowBuf (StringBuf, Level);
|
|
EncodedText = pEncodeMessage (p, FALSE);
|
|
_sntprintf (fmtLine, 1024, TEXT("%s (%s)"), EncodedText ? EncodedText : p, Class);
|
|
FreeText (EncodedText);
|
|
pAppendStringToGrowBuf (StringBuf, fmtLine);
|
|
}
|
|
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
|
|
break;
|
|
|
|
case CLEANUP:
|
|
|
|
FormatState = *((PPNPFORMATSTATE *) State);
|
|
|
|
if (FormatState) {
|
|
|
|
if (!g_ListFormat) {
|
|
if (*FormatState->LastClass) {
|
|
if (HtmlFormat) {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("</UL>\r\n"));
|
|
} else {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Append optional footer text
|
|
//
|
|
|
|
if (MsgIdBottom) {
|
|
Msg = GetStringResource (HtmlFormat ? MsgIdBottomHtml : MsgIdBottom);
|
|
if (Msg) {
|
|
if (HtmlFormat) {
|
|
pAppendStringToGrowBuf (StringBuf, Msg);
|
|
} else {
|
|
pWrapStringToGrowBuf (StringBuf, Msg, 0, 0);
|
|
}
|
|
|
|
FreeStringResource (Msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
MemFree (g_hHeap, 0, FormatState);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
PCTSTR
|
|
pFindEndOfTag (
|
|
IN PCTSTR Tag
|
|
)
|
|
{
|
|
BOOL quoteMode;
|
|
CHARTYPE ch;
|
|
|
|
quoteMode = FALSE;
|
|
|
|
do {
|
|
Tag = _tcsinc (Tag);
|
|
ch = _tcsnextc (Tag);
|
|
|
|
if (ch == TEXT('\"')) {
|
|
quoteMode = !quoteMode;
|
|
} else if (!quoteMode && ch == TEXT('>')) {
|
|
break;
|
|
}
|
|
} while (ch);
|
|
|
|
return Tag;
|
|
}
|
|
|
|
|
|
PCTSTR
|
|
pEncodeMessage (
|
|
IN PCTSTR Message,
|
|
IN BOOL HtmlFormat
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pEncodeMessage removes the unsupported HTML tags from Message, and returns a
|
|
text pool string. If plain text is required, all HTML tags are removed from
|
|
Message, and all HTML-escaped characters are converted into normal text.
|
|
|
|
If Message contains leading space, and the caller wants an HTML return
|
|
string, then the leading space is converted in to non-breaking space
|
|
characters.
|
|
|
|
Arguments:
|
|
|
|
Message - Specifies the text to convert
|
|
|
|
HtmlFormat - Specifies TRUE if the return value should be in HTML, or FALSE
|
|
if it should be in plain text.
|
|
|
|
Return Value:
|
|
|
|
A pointer to a text pool allocated string. The caller must
|
|
free this pointer with FreeText.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCTSTR p, r;
|
|
PCTSTR mnemonic;
|
|
PTSTR q;
|
|
BOOL processed;
|
|
PTSTR Buf;
|
|
CHARTYPE ch;
|
|
BOOL Copy;
|
|
PCTSTR semicolon;
|
|
PCTSTR closeBracket;
|
|
PCTSTR endOfMnemonic;
|
|
UINT leadingSpaces;
|
|
|
|
leadingSpaces = 0;
|
|
|
|
if (HtmlFormat) {
|
|
p = Message;
|
|
|
|
while (*p) {
|
|
if (_istspace (_tcsnextc (Message))) {
|
|
leadingSpaces++;
|
|
} else if (_tcsnextc (Message) == TEXT('<')) {
|
|
// ignore html tags
|
|
p = pFindEndOfTag (p);
|
|
} else {
|
|
// first printable character -- stop
|
|
break;
|
|
}
|
|
|
|
p = _tcsinc (p);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Allocate an output buffer. AllocText takes the number of logical
|
|
// characters as input; the terminating nul is a character.
|
|
//
|
|
|
|
Buf = AllocText (LcharCount (Message) + (leadingSpaces * 6) + 1);
|
|
if (!Buf) {
|
|
return NULL;
|
|
}
|
|
|
|
p = Message;
|
|
q = Buf;
|
|
|
|
while (*p) {
|
|
|
|
ch = _tcsnextc (p);
|
|
processed = FALSE;
|
|
|
|
//
|
|
// If caller wants plain text, remove HTML encodings and tags
|
|
//
|
|
|
|
if (!HtmlFormat) {
|
|
if (ch == TEXT('&')) {
|
|
//
|
|
// Convert ampersand-encoded characters
|
|
//
|
|
|
|
semicolon = _tcschr (p + 1, TEXT(';'));
|
|
mnemonic = p + 1;
|
|
|
|
if (semicolon) {
|
|
processed = TRUE;
|
|
|
|
if (StringMatchAB (TEXT("lt"), mnemonic, semicolon)) {
|
|
*q++ = TEXT('<');
|
|
} else if (StringMatchAB (TEXT("gt"), mnemonic, semicolon)) {
|
|
*q++ = TEXT('>');
|
|
} else if (StringMatchAB (TEXT("amp"), mnemonic, semicolon)) {
|
|
*q++ = TEXT('&');
|
|
} else if (StringMatchAB (TEXT("quot"), mnemonic, semicolon)) {
|
|
*q++ = TEXT('\"');
|
|
} else if (StringMatchAB (TEXT("apos"), mnemonic, semicolon)) {
|
|
*q++ = TEXT('\'');
|
|
} else if (StringMatchAB (TEXT("nbsp"), mnemonic, semicolon)) {
|
|
*q++ = TEXT(' ');
|
|
} else {
|
|
processed = FALSE;
|
|
}
|
|
|
|
if (processed) {
|
|
// move p to the last character of the mnemonic
|
|
p = semicolon;
|
|
}
|
|
}
|
|
|
|
} else if (ch == TEXT('<')) {
|
|
//
|
|
// Hop over HTML tag and its arguments, leaving p on the
|
|
// closing angle bracket or terminating nul
|
|
//
|
|
|
|
p = pFindEndOfTag (p);
|
|
processed = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If the caller wants an HTML return string, strip out all
|
|
// unsupported tags. Convert leading spaces into .
|
|
//
|
|
|
|
else {
|
|
|
|
if (ch == TEXT('<')) {
|
|
|
|
closeBracket = pFindEndOfTag (p);
|
|
mnemonic = p + 1;
|
|
|
|
endOfMnemonic = p;
|
|
while (!_istspace (_tcsnextc (endOfMnemonic))) {
|
|
endOfMnemonic = _tcsinc (endOfMnemonic);
|
|
if (endOfMnemonic == closeBracket) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// if a known good tag, copy it, otherwise skip it
|
|
//
|
|
|
|
if (StringIMatchAB (TEXT("A"), mnemonic, endOfMnemonic) ||
|
|
StringIMatchAB (TEXT("/A"), mnemonic, endOfMnemonic) ||
|
|
StringIMatchAB (TEXT("P"), mnemonic, endOfMnemonic) ||
|
|
StringIMatchAB (TEXT("/P"), mnemonic, endOfMnemonic) ||
|
|
StringIMatchAB (TEXT("BR"), mnemonic, endOfMnemonic) ||
|
|
StringIMatchAB (TEXT("/BR"), mnemonic, endOfMnemonic)
|
|
) {
|
|
StringCopyAB (q, p, _tcsinc (closeBracket));
|
|
q = GetEndOfString (q);
|
|
processed = TRUE;
|
|
}
|
|
|
|
p = closeBracket;
|
|
|
|
} else if (leadingSpaces && _istspace (ch)) {
|
|
StringCopy (q, TEXT(" "));
|
|
q = GetEndOfString (q);
|
|
processed = TRUE;
|
|
} else {
|
|
// first printable character -- turn off leading space conversion
|
|
leadingSpaces = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If not processed, copy the character
|
|
//
|
|
|
|
if (!processed) {
|
|
_copytchar (q, p);
|
|
q = _tcsinc (q);
|
|
}
|
|
|
|
if (*p) {
|
|
p = _tcsinc (p);
|
|
}
|
|
}
|
|
|
|
*q = 0;
|
|
|
|
return Buf;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pDefaultHandler (
|
|
IN CALLCONTEXT Context,
|
|
IN PMSGGROUP_PROPS Group,
|
|
IN OUT PGROWBUFFER StringBuf,
|
|
IN PCTSTR SubGroup,
|
|
IN PCTSTR Message,
|
|
IN DWORD Level,
|
|
IN BOOL HtmlFormat,
|
|
IN OUT PVOID *State,
|
|
IN DWORD Arg
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pDefaultHandler formats all messages that are not handled
|
|
in some other way. The formatting is simple -- the message group
|
|
is added to the report in bold, and the text is placed
|
|
below the message group.
|
|
|
|
All text for the default handler appears at the end of the
|
|
incompatibility report.
|
|
|
|
Arguments:
|
|
|
|
Context - Specifies the way the handler is being called, either to
|
|
initialize, process an item or clean up.
|
|
|
|
Group - Specifies the group properties of this item
|
|
|
|
StringBuf - Specifies the current report. Append text to this buffer via
|
|
the pAppendStringToGrowBuf routine.
|
|
|
|
SubGroup - Specifies the message subgroup, and does not include the root
|
|
message group.
|
|
|
|
Message - Specifies the message text
|
|
|
|
Level - Specifies the severity level of Message (info, error, etc.)
|
|
|
|
HtmlFormat - Specifies TRUE if the text should be written with HTML
|
|
formatting tags, or FALSE if the text should be formatted as
|
|
plain text. (See CreateReport comments for HTML tag info.)
|
|
|
|
State - A pointer to state, defined by the handler. State holds an arbitrary
|
|
32-bit value that the handler maintains. Typically the handler
|
|
allocates a struct when Context is INIT, then uses the struct for
|
|
each PROCESS_ITEM, and finally cleans up the allocation when Context
|
|
is CLEANUP.
|
|
|
|
Arg - The DWORD argument from the macro expansion list
|
|
|
|
Return Value:
|
|
|
|
TRUE if the handler was successful, or FALSE if an error occurs.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCTSTR EncodedMessage;
|
|
BOOL indent;
|
|
|
|
Level &= REPORTLEVEL_ALL;
|
|
MYASSERT (ONEBITSET (Level));
|
|
|
|
if (Context != PROCESS_ITEM) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Build message group string
|
|
//
|
|
|
|
if (HtmlFormat) {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("<UL>"));
|
|
}
|
|
|
|
indent = pAddMsgGroupString (StringBuf, Group, SubGroup, HtmlFormat, Level);
|
|
|
|
if (Message) {
|
|
//
|
|
// Add details
|
|
//
|
|
|
|
if (!g_ListFormat) {
|
|
if (HtmlFormat) {
|
|
if (indent) {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("<UL>"));
|
|
}
|
|
} else {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
|
|
}
|
|
|
|
EncodedMessage = pEncodeMessage (Message, HtmlFormat);
|
|
|
|
if (EncodedMessage) {
|
|
if (HtmlFormat) {
|
|
pAppendStringToGrowBuf (StringBuf, EncodedMessage);
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("<BR>\r\n"));
|
|
} else {
|
|
pWrapStringToGrowBuf (StringBuf, EncodedMessage, 4, 0);
|
|
}
|
|
|
|
FreeText (EncodedMessage);
|
|
}
|
|
|
|
if (HtmlFormat) {
|
|
if (indent) {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("</UL>"));
|
|
}
|
|
} else {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (HtmlFormat) {
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("</UL>"));
|
|
}
|
|
|
|
pAppendStringToGrowBuf (StringBuf, TEXT("\r\n"));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pProcessGenericList (
|
|
IN OUT PGROWBUFFER StringBuf,
|
|
IN BOOL HtmlFormat,
|
|
IN PMSGGROUP_PROPS Props,
|
|
IN PGENERIC_LIST List,
|
|
IN DWORD LevelMask
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pProcessGenericList calls the pGenericItemList handler for every message in the
|
|
message group/subgroup.
|
|
|
|
An example of a group:
|
|
|
|
Installation Notes
|
|
|
|
An example of a subgroup:
|
|
|
|
Name Changes
|
|
|
|
The combined name, as stored in memdb:
|
|
|
|
Installation Notes\Name Changes
|
|
|
|
All messages for a group are processed if the message group name (specified
|
|
by Props->Name) and subgroup name (specified by List->SubGroupStr) are identical.
|
|
If they are different, only the subgroup messages are processed, providing the
|
|
capatiblity to format a single message group in multiple ways.
|
|
|
|
Arguments:
|
|
|
|
StringBuf - Specifies the GROWBUFFER holding the current report.
|
|
Receives all additional text.
|
|
|
|
HtmlFormat - Specifies TRUE if the caller wants the text to
|
|
contain HTML characters, or FALSE if not.
|
|
|
|
Props - Specifies the poperties of the group to process.
|
|
|
|
List - Specifies the generic list attributes, including the subgroup name and
|
|
intro/conclusion text ids.
|
|
|
|
LevelMask - Specifies the severity mask of the messages to process
|
|
|
|
Return Value:
|
|
|
|
TRUE if at least one message was processed, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
REPORT_MESSAGE_ENUM e;
|
|
TCHAR Node[MEMDB_MAX];
|
|
PVOID State = NULL;
|
|
BOOL result = FALSE;
|
|
|
|
MYASSERT (List->SubGroupStr);
|
|
|
|
if (!StringMatch (List->SubGroupStr, Props->Name)) {
|
|
wsprintf (Node, TEXT("%s\\%s"), Props->Name, List->SubGroupStr);
|
|
} else {
|
|
StringCopy (Node, List->SubGroupStr);
|
|
}
|
|
|
|
if (EnumFirstMessage (&e, Node, LevelMask)) {
|
|
|
|
result = TRUE;
|
|
|
|
pGenericItemList (
|
|
INIT,
|
|
StringBuf,
|
|
List->SubGroupStr,
|
|
NULL,
|
|
e.e.UserFlags,
|
|
HtmlFormat,
|
|
&State,
|
|
List->IntroId,
|
|
List->IntroIdHtml,
|
|
List->ConclusionId,
|
|
List->ConclusionIdHtml,
|
|
List->FormatArgs,
|
|
List->Flags,
|
|
Props
|
|
);
|
|
|
|
do {
|
|
|
|
pGenericItemList (
|
|
PROCESS_ITEM,
|
|
StringBuf,
|
|
e.MsgGroup,
|
|
e.Message,
|
|
e.e.UserFlags,
|
|
HtmlFormat,
|
|
&State,
|
|
List->IntroId,
|
|
List->IntroIdHtml,
|
|
List->ConclusionId,
|
|
List->ConclusionIdHtml,
|
|
List->FormatArgs,
|
|
List->Flags,
|
|
Props
|
|
);
|
|
|
|
} while (EnumNextMessage (&e));
|
|
|
|
pGenericItemList (
|
|
CLEANUP,
|
|
StringBuf,
|
|
List->SubGroupStr,
|
|
NULL,
|
|
e.e.UserFlags,
|
|
HtmlFormat,
|
|
&State,
|
|
List->IntroId,
|
|
List->IntroIdHtml,
|
|
List->ConclusionId,
|
|
List->ConclusionIdHtml,
|
|
List->FormatArgs,
|
|
List->Flags,
|
|
Props
|
|
);
|
|
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pProcessMessageHandler (
|
|
IN OUT PGROWBUFFER StringBuf,
|
|
IN BOOL HtmlFormat,
|
|
IN PMSGGROUP_PROPS Props,
|
|
IN PHANDLER_LIST Handler,
|
|
IN DWORD LevelMask
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pProcessMessageHandler calls the handler for every message in the
|
|
message group/subgroup that the function wants to handle.
|
|
|
|
An example of a group:
|
|
|
|
Installation Notes
|
|
|
|
An example of a subgroup:
|
|
|
|
Name Changes
|
|
|
|
The combined name, as stored in memdb:
|
|
|
|
Installation Notes\Name Changes
|
|
|
|
All messages for a group are processed if the message group name (specified
|
|
by Props->Name) and subgroup name (specified by List->SubGroupStr) are identical.
|
|
If they are different, only the subgroup messages are processed, providing the
|
|
capatiblity to format a single message group in multiple ways.
|
|
|
|
Arguments:
|
|
|
|
StringBuf - Specifies the GROWBUFFER holding the current report.
|
|
Receives all additional text.
|
|
|
|
HtmlFormat - Specifies TRUE if the caller wants the text to
|
|
contain HTML characters, or FALSE if not.
|
|
|
|
Props - Specifies the poperties of the group to process.
|
|
|
|
Handler - Specifies the handler attributes, including the function name
|
|
and subgroup name.
|
|
|
|
LevelMask - Specifies the severity of the messages to include, or 0 to
|
|
include all messages
|
|
|
|
Return Value:
|
|
|
|
TRUE if at least one message was processed, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
REPORT_MESSAGE_ENUM e;
|
|
TCHAR Node[MEMDB_MAX];
|
|
BOOL result = FALSE;
|
|
|
|
MYASSERT (Handler->SubGroupStr);
|
|
MYASSERT (Handler->Fn);
|
|
|
|
if (!StringMatch (Handler->SubGroupStr, Props->Name)) {
|
|
wsprintf (Node, TEXT("%s\\%s"), Props->Name, Handler->SubGroupStr);
|
|
} else {
|
|
StringCopy (Node, Handler->SubGroupStr);
|
|
}
|
|
|
|
if (EnumFirstMessage (&e, Node, LevelMask)) {
|
|
|
|
result = TRUE;
|
|
|
|
Handler->Fn (
|
|
INIT,
|
|
Props,
|
|
StringBuf,
|
|
NULL,
|
|
NULL,
|
|
e.e.UserFlags,
|
|
HtmlFormat,
|
|
&Handler->State,
|
|
Handler->Arg
|
|
);
|
|
|
|
do {
|
|
|
|
Handler->Fn (
|
|
PROCESS_ITEM,
|
|
Props,
|
|
StringBuf,
|
|
e.MsgGroup,
|
|
e.Message,
|
|
e.e.UserFlags,
|
|
HtmlFormat,
|
|
&Handler->State,
|
|
Handler->Arg
|
|
);
|
|
|
|
} while (EnumNextMessage (&e));
|
|
|
|
Handler->Fn (
|
|
CLEANUP,
|
|
Props,
|
|
StringBuf,
|
|
NULL,
|
|
NULL,
|
|
e.e.UserFlags,
|
|
HtmlFormat,
|
|
&Handler->State,
|
|
Handler->Arg
|
|
);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pAddMsgGroupToReport (
|
|
IN OUT PGROWBUFFER StringBuf,
|
|
IN BOOL HtmlFormat,
|
|
IN PMSGGROUP_PROPS Props,
|
|
IN DWORD LevelMask
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pAddMsgGroupToReport adds messages for the specified message group
|
|
to the report. It first enumerates all messages in the group,
|
|
and each one that has a handler is processed first. After that,
|
|
any remaining messages are put in an "other" section.
|
|
|
|
Arguments:
|
|
|
|
StringBuf - Specifies the GROWBUFFER holding the current report.
|
|
Receives all additional text.
|
|
|
|
HtmlFormat - Specifies TRUE if the caller wants the text to
|
|
contain HTML characters, or FALSE if not.
|
|
|
|
Props - Specifies the poperties of the group to process.
|
|
|
|
LevelMask - Specifies a mask to restrict processing, or 0 to
|
|
process all messages
|
|
|
|
Return Value:
|
|
|
|
TRUE if at least one message was added, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
REPORT_MESSAGE_ENUM e;
|
|
UINT Pass;
|
|
BOOL AddOtherText;
|
|
PHANDLER_LIST Handler;
|
|
PGENERIC_LIST List;
|
|
BOOL result = FALSE;
|
|
|
|
//
|
|
// Check to see if there is a handler for all messages in the message
|
|
// group.
|
|
//
|
|
|
|
List = pSearchForGenericList (Props->Name);
|
|
if (List) {
|
|
return pProcessGenericList (
|
|
StringBuf,
|
|
HtmlFormat,
|
|
Props,
|
|
List,
|
|
LevelMask
|
|
);
|
|
}
|
|
|
|
Handler = pSearchForMsgGroupHandler (Props->Name);
|
|
if (Handler->Fn != pDefaultHandler) {
|
|
return pProcessMessageHandler (
|
|
StringBuf,
|
|
HtmlFormat,
|
|
Props,
|
|
Handler,
|
|
LevelMask
|
|
);
|
|
}
|
|
|
|
//
|
|
// Since there is no handler for all messages, call the handlers for
|
|
// subgroups, then call the default handler for the unhandled messages.
|
|
//
|
|
// Two passes, one for the handled messages, and another for the
|
|
// unhandled messages.
|
|
//
|
|
|
|
for (Pass = 1 ; Pass <= 2 ; Pass++) {
|
|
|
|
AddOtherText = (Pass == 2);
|
|
|
|
//
|
|
// Enumerate all messages in the group
|
|
//
|
|
|
|
Handler = NULL;
|
|
List = NULL;
|
|
|
|
if (EnumFirstMessage (&e, Props->Name, LevelMask)) {
|
|
|
|
result = TRUE;
|
|
|
|
do {
|
|
//
|
|
// Is this the same message group that was used last time through
|
|
// the loop? If so, continue enumerating, because this
|
|
// message group is done.
|
|
//
|
|
|
|
if ((Handler && pIsThisTheHandler (e.MsgGroup, Handler)) ||
|
|
(List && pIsThisTheGenericList (e.MsgGroup, List))
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Is this group a generic list? For Pass 1, we add the list; for
|
|
// pass 2 we just continue;
|
|
//
|
|
|
|
List = pSearchForGenericList (e.MsgGroup);
|
|
if (List) {
|
|
Handler = NULL;
|
|
|
|
if (Pass == 1) {
|
|
pProcessGenericList (
|
|
StringBuf,
|
|
HtmlFormat,
|
|
Props,
|
|
List,
|
|
LevelMask
|
|
);
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Does this group have a handler? Pass 1 requires one, and
|
|
// pass 2 requires no handler.
|
|
//
|
|
|
|
Handler = pSearchForMsgGroupHandler (e.MsgGroup);
|
|
|
|
if (Handler->Fn == pDefaultHandler) {
|
|
if (Pass == 1) {
|
|
continue;
|
|
}
|
|
} else if (Pass == 2) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Add the "other" intro?
|
|
//
|
|
|
|
if (!g_ListFormat) {
|
|
if (AddOtherText) {
|
|
AddOtherText = FALSE;
|
|
|
|
if (HtmlFormat && Props->OtherIdHtmlStr) {
|
|
pAppendStringToGrowBuf (StringBuf, Props->OtherIdHtmlStr);
|
|
} else if (!HtmlFormat && Props->OtherIdStr) {
|
|
pWrapStringToGrowBuf (StringBuf, Props->OtherIdStr, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If handler exists, process all messages
|
|
//
|
|
|
|
if (Handler->Fn != pDefaultHandler) {
|
|
pProcessMessageHandler (
|
|
StringBuf,
|
|
HtmlFormat,
|
|
Props,
|
|
Handler,
|
|
LevelMask
|
|
);
|
|
} else {
|
|
//
|
|
// Call the default handler
|
|
//
|
|
|
|
Handler->Fn (
|
|
PROCESS_ITEM,
|
|
Props,
|
|
StringBuf,
|
|
e.MsgGroup,
|
|
e.Message,
|
|
e.e.UserFlags,
|
|
HtmlFormat,
|
|
&Handler->State,
|
|
Handler->Arg
|
|
);
|
|
}
|
|
|
|
} while (EnumNextMessage (&e));
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
typedef enum {
|
|
FORMAT_HTML,
|
|
FORMAT_PLAIN_TEXT,
|
|
FORMAT_LIST
|
|
} REPORTFORMAT;
|
|
|
|
|
|
BOOL
|
|
pCreateReportTextWorker (
|
|
IN REPORTFORMAT Format,
|
|
IN UINT TotalCols, OPTIONAL
|
|
IN DWORD LevelMask
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pCreateReportTextWorker prepares a buffer for the incompatibility report
|
|
text. It enumerates messages that match the severity requested, then
|
|
performs special formatting, and dumps the remaining incompatibilities to
|
|
a global buffer. The buffer is then used to display, print or save.
|
|
|
|
A subset of HTML is supported if HtmlFormat is TRUE. Specifically, the
|
|
following tags are inserted into the report text:
|
|
|
|
<B> - Bold
|
|
<U> - Underline
|
|
<HR> - Line break
|
|
<UL> - Indented list
|
|
|
|
No other HTML tags are recognized.
|
|
|
|
The caller must free the return buffer by calling FreeReportText. Also,
|
|
CreateReportText uses a single global buffer and therefore cannot be called
|
|
more than once. Instead, the caller must use the text and/or duplicate it,
|
|
then call FreeReportText, before calling CreateReportText a second time.
|
|
|
|
Arguments:
|
|
|
|
Format - Specifies which type of report to generate
|
|
|
|
TotalCols - Specifies the number of cols for a plain text report
|
|
|
|
LevelMask - Specifies which severity levels to add (verbose, error,
|
|
blocking) or zero for all levels
|
|
|
|
Return Value:
|
|
|
|
TRUE if at least one message was added, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
REPORT_MESSAGE_ENUM MsgGroups;
|
|
PMSGGROUP_PROPS Props;
|
|
REPORT_MESSAGE_ENUM e;
|
|
PTSTR TempStr;
|
|
BOOL HtmlFormat;
|
|
UINT oldEnd;
|
|
BOOL result = FALSE;
|
|
|
|
if (!LevelMask) {
|
|
return FALSE;
|
|
}
|
|
|
|
HtmlFormat = (Format == FORMAT_HTML);
|
|
|
|
//
|
|
// Add report details
|
|
//
|
|
|
|
if (EnumFirstRootMsgGroup (&e, LevelMask)) {
|
|
do {
|
|
g_LastMsgGroupBuf[0] = 0;
|
|
|
|
//
|
|
// Obtain message group properties. If no properties exist, then ignore the message.
|
|
//
|
|
|
|
Props = pFindMsgGroupStruct (e.MsgGroup);
|
|
if (!Props) {
|
|
DEBUGMSG ((DBG_WHOOPS, "Group %s is not supported as a root", e.MsgGroup));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Add bookmark for base group
|
|
//
|
|
|
|
oldEnd = g_ReportString.End;
|
|
|
|
if (!g_ListFormat) {
|
|
if (HtmlFormat) {
|
|
|
|
TempStr = AllocText (6 + LcharCount (e.MsgGroup) + 1);
|
|
if (TempStr) {
|
|
wsprintf (TempStr, TEXT("<A NAME=\"%s\">"), e.MsgGroup);
|
|
pAppendStringToGrowBuf (&g_ReportString, TempStr);
|
|
FreeText (TempStr);
|
|
}
|
|
}
|
|
//
|
|
// Is there an intro string? If so, add it.
|
|
//
|
|
|
|
if (HtmlFormat && Props->IntroIdHtmlStr) {
|
|
pAppendStringToGrowBuf (&g_ReportString, Props->IntroIdHtmlStr);
|
|
} else if (!HtmlFormat && Props->IntroIdStr) {
|
|
pWrapStringToGrowBuf (&g_ReportString, Props->IntroIdStr, 0, 0);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add all messages in this group to the report
|
|
//
|
|
|
|
if (!pAddMsgGroupToReport (
|
|
&g_ReportString,
|
|
HtmlFormat,
|
|
Props,
|
|
LevelMask
|
|
)) {
|
|
//
|
|
// No messages -- back out heading text
|
|
//
|
|
|
|
if (oldEnd) {
|
|
g_ReportString.End = 0;
|
|
} else {
|
|
FreeGrowBuffer (&g_ReportString);
|
|
}
|
|
} else {
|
|
result = TRUE;
|
|
}
|
|
|
|
} while (EnumNextRootMsgGroup (&e));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
VOID
|
|
FreeReportText (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
FreeReportText frees the memory allocated by CreateReportText.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
FreeGrowBuffer (&g_ReportString);
|
|
}
|
|
|
|
|
|
VOID
|
|
pAddHeadingToReport (
|
|
IN OUT PGROWBUFFER Buffer,
|
|
IN BOOL HtmlFormat,
|
|
IN UINT PlainTextId,
|
|
IN UINT HtmlId
|
|
)
|
|
{
|
|
PCTSTR msg;
|
|
|
|
if (HtmlFormat) {
|
|
msg = GetStringResource (HtmlId);
|
|
pAppendStringToGrowBuf (Buffer, msg);
|
|
} else {
|
|
msg = GetStringResource (PlainTextId);
|
|
pWrapStringToGrowBuf (Buffer, msg, 0, 0);
|
|
}
|
|
|
|
FreeStringResource (msg);
|
|
}
|
|
|
|
|
|
VOID
|
|
pMoveReportTextToGrowBuf (
|
|
IN OUT PGROWBUFFER SourceBuffer,
|
|
IN OUT PGROWBUFFER DestBuffer
|
|
)
|
|
{
|
|
UINT end;
|
|
UINT trim = 0;
|
|
|
|
end = DestBuffer->End;
|
|
if (end) {
|
|
trim = sizeof (TCHAR);
|
|
end -= trim;
|
|
}
|
|
|
|
if (!GrowBuffer (DestBuffer, SourceBuffer->End - trim)) {
|
|
return;
|
|
}
|
|
|
|
CopyMemory (DestBuffer->Buf + end, SourceBuffer->Buf, SourceBuffer->End);
|
|
SourceBuffer->End = 0;
|
|
}
|
|
|
|
|
|
VOID
|
|
pAddTocEntry (
|
|
IN OUT PGROWBUFFER Buffer,
|
|
IN PCTSTR Bookmark,
|
|
IN UINT MessageId,
|
|
IN BOOL HtmlFormat
|
|
)
|
|
{
|
|
PCTSTR msg;
|
|
|
|
msg = GetStringResource (MessageId);
|
|
|
|
if (HtmlFormat) {
|
|
pAppendStringToGrowBuf (Buffer, TEXT("<A HREF=\"#"));
|
|
pAppendStringToGrowBuf (Buffer, Bookmark);
|
|
pAppendStringToGrowBuf (Buffer, TEXT("\">"));
|
|
pAppendStringToGrowBuf (Buffer, msg);
|
|
pAppendStringToGrowBuf (Buffer, TEXT("</A><BR>\r\n"));
|
|
} else {
|
|
pWrapStringToGrowBuf (Buffer, msg, 8, 2);
|
|
pAppendStringToGrowBuf (Buffer, TEXT("\r\n"));
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
AreThereAnyBlockingIssues(
|
|
VOID
|
|
)
|
|
{
|
|
REPORT_MESSAGE_ENUM e;
|
|
|
|
if(EnumFirstRootMsgGroup (&e, REPORTLEVEL_BLOCKING)) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
PCTSTR
|
|
CreateReportText (
|
|
IN BOOL HtmlFormat,
|
|
IN UINT TotalCols,
|
|
IN DWORD Level,
|
|
IN BOOL ListFormat
|
|
)
|
|
{
|
|
REPORTFORMAT format;
|
|
GROWBUFFER fullReport = GROWBUF_INIT;
|
|
PCTSTR argArray[1];
|
|
PCTSTR msg;
|
|
PCTSTR subMsg = NULL;
|
|
REPORT_MESSAGE_ENUM msgGroups;
|
|
PBYTE dest;
|
|
DWORD levelMask;
|
|
UINT end;
|
|
BOOL blocking = FALSE;
|
|
BOOL warning = FALSE;
|
|
BOOL info = FALSE;
|
|
|
|
//
|
|
// Intialize
|
|
//
|
|
|
|
if (HtmlFormat) {
|
|
format = FORMAT_HTML;
|
|
} else if (ListFormat) {
|
|
format = FORMAT_LIST;
|
|
} else {
|
|
format = FORMAT_PLAIN_TEXT;
|
|
}
|
|
|
|
g_ListFormat = ListFormat;
|
|
|
|
if (TotalCols) {
|
|
g_TotalCols = TotalCols;
|
|
} else {
|
|
g_TotalCols = g_ListFormat ? 0x7fffffff : 70;
|
|
}
|
|
|
|
//
|
|
// Create the report body
|
|
//
|
|
|
|
FreeReportText();
|
|
|
|
//
|
|
// test for no incompatibilities
|
|
//
|
|
|
|
MYASSERT (ONEBITSET (Level));
|
|
levelMask = LEVELTOMASK (Level);
|
|
|
|
if (ListFormat) {
|
|
levelMask |= REPORTLEVEL_IN_SHORT_LIST;
|
|
}
|
|
|
|
if (!EnumFirstRootMsgGroup (&msgGroups, levelMask)) {
|
|
if (!ListFormat) {
|
|
pAddHeadingToReport (
|
|
&g_ReportString,
|
|
HtmlFormat,
|
|
MSG_NO_INCOMPATIBILITIES,
|
|
MSG_NO_INCOMPATIBILITIES
|
|
);
|
|
}
|
|
|
|
return (PCTSTR) g_ReportString.Buf;
|
|
}
|
|
|
|
if (ListFormat) {
|
|
//
|
|
// In list format, create the report in one pass
|
|
//
|
|
|
|
pCreateReportTextWorker (format, TotalCols, levelMask);
|
|
|
|
return (PCTSTR) g_ReportString.Buf;
|
|
}
|
|
|
|
//
|
|
// In HTML or plain text, create the body of the report by making 3
|
|
// passes. The first pass is for blocking issues, the second is for
|
|
// warnings, and the third is for information.
|
|
//
|
|
// We put the report in a temporary buffer (fullReport), because after the
|
|
// body is prepared, we then can prepare the table of contents.
|
|
//
|
|
|
|
// blocking section
|
|
if (pCreateReportTextWorker (
|
|
format,
|
|
TotalCols,
|
|
REPORTLEVEL_BLOCKING & levelMask
|
|
)) {
|
|
|
|
blocking = TRUE;
|
|
|
|
if (HtmlFormat) {
|
|
pAppendStringToGrowBuf (&fullReport, TEXT("<A NAME=\"blocking\">"));
|
|
}
|
|
|
|
pAddHeadingToReport (
|
|
&fullReport,
|
|
HtmlFormat,
|
|
MSG_BLOCKING_INTRO,
|
|
MSG_BLOCKING_INTRO_HTML
|
|
);
|
|
|
|
pMoveReportTextToGrowBuf (&g_ReportString, &fullReport);
|
|
}
|
|
|
|
// warning section
|
|
|
|
if (pCreateReportTextWorker (
|
|
format,
|
|
TotalCols,
|
|
(REPORTLEVEL_ERROR|REPORTLEVEL_WARNING) & levelMask
|
|
)) {
|
|
|
|
warning = TRUE;
|
|
|
|
if (HtmlFormat) {
|
|
pAppendStringToGrowBuf (&fullReport, TEXT("<A NAME=\"warning\">"));
|
|
}
|
|
|
|
pAddHeadingToReport (
|
|
&fullReport,
|
|
HtmlFormat,
|
|
MSG_WARNING_INTRO,
|
|
MSG_WARNING_INTRO_HTML
|
|
);
|
|
|
|
pMoveReportTextToGrowBuf (&g_ReportString, &fullReport);
|
|
}
|
|
|
|
// info section
|
|
if (pCreateReportTextWorker (
|
|
format,
|
|
TotalCols,
|
|
(REPORTLEVEL_INFORMATION|REPORTLEVEL_VERBOSE) & levelMask
|
|
)) {
|
|
|
|
info = TRUE;
|
|
|
|
if (HtmlFormat) {
|
|
pAppendStringToGrowBuf (&fullReport, TEXT("<A NAME=\"info\">"));
|
|
}
|
|
|
|
pAddHeadingToReport (
|
|
&fullReport,
|
|
HtmlFormat,
|
|
MSG_INFO_INTRO,
|
|
MSG_INFO_INTRO_HTML
|
|
);
|
|
|
|
pMoveReportTextToGrowBuf (&g_ReportString, &fullReport);
|
|
}
|
|
|
|
//
|
|
// Now produce the complete report (with table of contents)
|
|
//
|
|
|
|
MYASSERT (!g_ReportString.End);
|
|
MYASSERT (fullReport.End);
|
|
|
|
//
|
|
// add the heading text
|
|
//
|
|
|
|
if (HtmlFormat) {
|
|
pAppendStringToGrowBuf (&g_ReportString, TEXT("<A NAME=\"top\">"));
|
|
}
|
|
|
|
if (blocking) {
|
|
//
|
|
// add instructions based on the presence of blocking issues
|
|
//
|
|
|
|
if (HtmlFormat) {
|
|
argArray[0] = GetStringResource (g_PersonalSKU ?
|
|
MSG_PER_SUPPORT_LINK_HTML :
|
|
MSG_PRO_SUPPORT_LINK_HTML
|
|
);
|
|
|
|
msg = ParseMessageID (MSG_REPORT_BLOCKING_INSTRUCTIONS_HTML, argArray);
|
|
|
|
pAppendStringToGrowBuf (&g_ReportString, msg);
|
|
|
|
} else{
|
|
argArray[0] = GetStringResource (g_PersonalSKU ?
|
|
MSG_PER_SUPPORT_LINK :
|
|
MSG_PRO_SUPPORT_LINK
|
|
);
|
|
|
|
msg = ParseMessageID (MSG_REPORT_BLOCKING_INSTRUCTIONS, argArray);
|
|
|
|
pWrapStringToGrowBuf (&g_ReportString, msg, 0, 0);
|
|
}
|
|
|
|
FreeStringResource (argArray[0]);
|
|
FreeStringResource (msg);
|
|
|
|
} else {
|
|
//
|
|
// add instructions for just warnings and information
|
|
//
|
|
|
|
if (HtmlFormat) {
|
|
msg = GetStringResource (MSG_REPORT_GENERAL_INSTRUCTIONS_HTML);
|
|
pAppendStringToGrowBuf (&g_ReportString, msg);
|
|
} else {
|
|
msg = GetStringResource (MSG_REPORT_GENERAL_INSTRUCTIONS);
|
|
pWrapStringToGrowBuf (&g_ReportString, msg, 0, 0);
|
|
}
|
|
|
|
FreeStringResource (msg);
|
|
|
|
if (HtmlFormat) {
|
|
msg = GetStringResource (MSG_CONTENTS_TITLE_HTML);
|
|
pAppendStringToGrowBuf (&g_ReportString, msg);
|
|
} else {
|
|
msg = GetStringResource (MSG_CONTENTS_TITLE);
|
|
pWrapStringToGrowBuf (&g_ReportString, msg, 0, 0);
|
|
}
|
|
|
|
FreeStringResource (msg);
|
|
|
|
}
|
|
|
|
//
|
|
// add table of contents
|
|
//
|
|
|
|
if (HtmlFormat) {
|
|
pAppendStringToGrowBuf (&g_ReportString, TEXT("<UL>"));
|
|
}
|
|
|
|
if (blocking) {
|
|
pAddTocEntry (&g_ReportString, TEXT("blocking"), MSG_BLOCKING_TOC, HtmlFormat);
|
|
}
|
|
|
|
if (warning) {
|
|
pAddTocEntry (&g_ReportString, TEXT("warning"), MSG_WARNING_TOC, HtmlFormat);
|
|
}
|
|
|
|
if (info) {
|
|
pAddTocEntry (&g_ReportString, TEXT("info"), MSG_INFO_TOC, HtmlFormat);
|
|
}
|
|
|
|
if (HtmlFormat) {
|
|
pAppendStringToGrowBuf (&g_ReportString, TEXT("</UL>"));
|
|
}
|
|
|
|
pAppendStringToGrowBuf (&g_ReportString, TEXT("\r\n"));
|
|
|
|
//
|
|
// add bottom of heading text
|
|
//
|
|
|
|
if (!blocking) {
|
|
|
|
if (HtmlFormat) {
|
|
argArray[0] = GetStringResource (g_PersonalSKU ?
|
|
MSG_PER_SUPPORT_LINK_HTML :
|
|
MSG_PRO_SUPPORT_LINK_HTML
|
|
);
|
|
|
|
msg = ParseMessageID (MSG_REPORT_GENERAL_INSTRUCTIONS_END_HTML, argArray);
|
|
|
|
pAppendStringToGrowBuf (&g_ReportString, msg);
|
|
|
|
} else{
|
|
argArray[0] = GetStringResource (g_PersonalSKU ?
|
|
MSG_PER_SUPPORT_LINK :
|
|
MSG_PRO_SUPPORT_LINK
|
|
);
|
|
|
|
msg = ParseMessageID (MSG_REPORT_GENERAL_INSTRUCTIONS_END, argArray);
|
|
|
|
pWrapStringToGrowBuf (&g_ReportString, msg, 0, 0);
|
|
}
|
|
|
|
FreeStringResource (argArray[0]);
|
|
FreeStringResource (msg);
|
|
|
|
if (g_ConfigOptions.EnableBackup) {
|
|
if (HtmlFormat) {
|
|
subMsg = GetStringResource (MSG_REPORT_BACKUP_INSTRUCTIONS_HTML);
|
|
pAppendStringToGrowBuf (&g_ReportString, subMsg);
|
|
} else {
|
|
subMsg = GetStringResource (MSG_REPORT_BACKUP_INSTRUCTIONS);
|
|
pWrapStringToGrowBuf (&g_ReportString, subMsg, 0, 0);
|
|
}
|
|
FreeStringResource (subMsg);
|
|
}
|
|
}
|
|
|
|
//
|
|
// add body text
|
|
//
|
|
|
|
pMoveReportTextToGrowBuf (&fullReport, &g_ReportString);
|
|
|
|
//
|
|
// Clean up temp buffer and return
|
|
//
|
|
|
|
FreeGrowBuffer (&fullReport);
|
|
|
|
return (PCTSTR) g_ReportString.Buf;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsIncompatibleHardwarePresent (
|
|
VOID
|
|
)
|
|
{
|
|
REPORT_MESSAGE_ENUM msgGroups;
|
|
PCTSTR msg;
|
|
BOOL result = FALSE;
|
|
|
|
if (EnumFirstRootMsgGroup (&msgGroups, 0)) {
|
|
msg = GetStringResource (MSG_INCOMPATIBLE_HARDWARE_ROOT);
|
|
|
|
do {
|
|
if (StringMatch (msg, msgGroups.MsgGroup)){
|
|
result = TRUE;
|
|
break;
|
|
}
|
|
} while (EnumNextRootMsgGroup (&msgGroups));
|
|
|
|
FreeStringResource (msg);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
PCTSTR
|
|
BuildMessageGroup (
|
|
IN UINT RootGroupId,
|
|
IN UINT SubGroupId, OPTIONAL
|
|
IN PCTSTR Item OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
BuildMessageGroup returns a string generated by loading the string resources
|
|
for the specified group ID, subgroup ID and Item string.
|
|
|
|
Arguments:
|
|
|
|
RootGroupId - Specifies the message resource ID of the root group. Must be
|
|
one of the defined roots. (See the top of this file.)
|
|
|
|
SubGroup - Specifies a message resource ID of a string to append to the
|
|
root.
|
|
|
|
Item - Specifies a string to append to the end of the string, used to
|
|
uniquely identify a message.
|
|
|
|
Return Value:
|
|
|
|
A pointer to the message group string. Caller must free the string via
|
|
FreeText.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCTSTR RootGroup;
|
|
PCTSTR SubGroup;
|
|
PCTSTR Group;
|
|
|
|
RootGroup = GetStringResource (RootGroupId);
|
|
MYASSERT (RootGroup);
|
|
if (!RootGroup) {
|
|
return NULL;
|
|
}
|
|
|
|
if (SubGroupId) {
|
|
SubGroup = GetStringResource (SubGroupId);
|
|
MYASSERT (SubGroup);
|
|
} else {
|
|
SubGroup = NULL;
|
|
}
|
|
|
|
if (SubGroup) {
|
|
Group = JoinTextEx (NULL, RootGroup, SubGroup, TEXT("\\"), 0, NULL);
|
|
MYASSERT (Group);
|
|
FreeStringResource (SubGroup);
|
|
} else {
|
|
Group = DuplicateText (RootGroup);
|
|
}
|
|
|
|
FreeStringResource (RootGroup);
|
|
|
|
if (Item) {
|
|
RootGroup = Group;
|
|
Group = JoinTextEx (NULL, RootGroup, Item, TEXT("\\"), 0, NULL);
|
|
MYASSERT (Group);
|
|
|
|
FreeText (RootGroup);
|
|
}
|
|
|
|
return Group;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsPreDefinedMessageGroup (
|
|
IN PCTSTR Group
|
|
)
|
|
{
|
|
return pFindMsgGroupStruct (Group) != NULL;
|
|
}
|
|
|
|
PCTSTR
|
|
GetPreDefinedMessageGroupText (
|
|
IN UINT GroupNumber
|
|
)
|
|
{
|
|
PMSGGROUP_PROPS Props;
|
|
|
|
//
|
|
// GroupNumber is an externally used value. Migration DLLs may hard-code
|
|
// this number. If necessary, here is where translation is done when
|
|
// the groups change.
|
|
//
|
|
|
|
// No translation is necessary today
|
|
|
|
Props = pFindMsgGroupStructById (GroupNumber);
|
|
|
|
return Props ? Props->Name : NULL;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsReportEmpty (
|
|
IN DWORD Level
|
|
)
|
|
{
|
|
REPORT_MESSAGE_ENUM e;
|
|
DWORD levelMask;
|
|
|
|
MYASSERT (ONEBITSET (Level));
|
|
levelMask = LEVELTOMASK (Level);
|
|
|
|
return !EnumFirstMessage (&e, NULL, levelMask);
|
|
}
|
|
|
|
|
|
BOOL
|
|
EnumFirstListEntry (
|
|
OUT PLISTREPORTENTRY_ENUM EnumPtr,
|
|
IN PCTSTR ListReportText
|
|
)
|
|
{
|
|
if (!ListReportText || !*ListReportText) {
|
|
return FALSE;
|
|
}
|
|
EnumPtr->Next = (PTSTR)ListReportText;
|
|
EnumPtr->ReplacedChar = 0;
|
|
return EnumNextListEntry (EnumPtr);
|
|
}
|
|
|
|
BOOL
|
|
EnumNextListEntry (
|
|
IN OUT PLISTREPORTENTRY_ENUM EnumPtr
|
|
)
|
|
{
|
|
INT n;
|
|
|
|
if (!EnumPtr->Next) {
|
|
return FALSE;
|
|
}
|
|
|
|
EnumPtr->Entry = EnumPtr->Next;
|
|
if (EnumPtr->ReplacedChar) {
|
|
*EnumPtr->Next = EnumPtr->ReplacedChar;
|
|
EnumPtr->ReplacedChar = 0;
|
|
EnumPtr->Entry += sizeof (TEXT("\r\n")) / sizeof (TCHAR) - 1;
|
|
}
|
|
|
|
EnumPtr->Next = _tcsstr (EnumPtr->Entry, TEXT("\r\n"));
|
|
if (EnumPtr->Next) {
|
|
EnumPtr->ReplacedChar = *EnumPtr->Next;
|
|
*EnumPtr->Next = 0;
|
|
}
|
|
|
|
EnumPtr->Entry = SkipSpace (EnumPtr->Entry);
|
|
|
|
EnumPtr->Header = StringMatchTcharCount (
|
|
EnumPtr->Entry,
|
|
TEXT("<H>"),
|
|
sizeof (TEXT("<H>")) / sizeof (TCHAR) - 1
|
|
);
|
|
if (EnumPtr->Header) {
|
|
EnumPtr->Entry += sizeof (TEXT("<H>")) / sizeof (TCHAR) - 1;
|
|
}
|
|
|
|
if (_stscanf (EnumPtr->Entry, TEXT("<%lu>%n"), &EnumPtr->Level, &n) == 1) {
|
|
EnumPtr->Entry += n;
|
|
} else {
|
|
EnumPtr->Level = REPORTLEVEL_NONE;
|
|
}
|
|
|
|
EnumPtr->Level &= REPORTLEVEL_ALL; // screen out REPORTLEVEL_IN_SHORT_LIST
|
|
|
|
EnumPtr->Entry = SkipSpace (EnumPtr->Entry);
|
|
|
|
return TRUE;
|
|
}
|
|
|