Compare commits

...

1 Commits

Author SHA1 Message Date
Mikhail Chusavitin
8149360410 Fix SAA DMI parser to match real DMI.txt format
Replace the guessed pipe/key=value parser with the correct format
documented in SAA User Guide 4.8.1:

  [Section]
  Item Name   {SHN}  = "value"   // comment

Handles string values (strips surrounding quotes), non-string values
(UUID, hex), section headers for display names, version line, and
// comments. Verified against the SAA 1.5.0 User Guide sample.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 15:58:02 +03:00

View File

@@ -24,42 +24,56 @@ type saaChange struct {
Value string `json:"value"` Value string `json:"value"`
} }
var shnRE = regexp.MustCompile(`^[A-Za-z0-9_]{1,16}$`) var (
shnRE = regexp.MustCompile(`^[A-Za-z0-9_]{1,16}$`)
dmiSectionRE = regexp.MustCompile(`^\[(.+?)\]$`)
// Item Name {SHN} = value // comment
dmiItemRE = regexp.MustCompile(`^(.+?)\s+\{([A-Za-z0-9]{1,16})\}\s*=\s*(.*)$`)
dmiVersionRE = regexp.MustCompile(`(?i)^version\s*=`)
)
// parseDMIFile parses the DMI.txt produced by "saa GetDmiInfo". // parseDMIFile parses the DMI.txt produced by "saa GetDmiInfo".
// Supports two formats: // Real format (from SAA User Guide 4.8.1):
// - Name|Shn|Value (pipe-separated, primary)
// - Shn=Value (key=value fallback)
// //
// Lines starting with '#', empty lines, "version=..." and section headers are skipped. // [System]
// Version {SYVS} = "A Version" // string value
// Serial Number {SYSN} = $DEFAULT$ // string value
// UUID {SYUU} = 00112233-... // hex value
func parseDMIFile(content string) []dmiField { func parseDMIFile(content string) []dmiField {
var fields []dmiField var fields []dmiField
currentSection := ""
for _, line := range strings.Split(content, "\n") { for _, line := range strings.Split(content, "\n") {
line = strings.TrimSpace(line) line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") { if line == "" || strings.HasPrefix(line, "//") || strings.HasPrefix(line, "#") {
continue continue
} }
lower := strings.ToLower(line) if dmiVersionRE.MatchString(line) {
if strings.HasPrefix(lower, "version=") || strings.HasPrefix(lower, "[") {
continue continue
} }
parts := strings.SplitN(line, "|", 3) if m := dmiSectionRE.FindStringSubmatch(line); m != nil {
if len(parts) == 3 { currentSection = strings.TrimSpace(m[1])
name := strings.TrimSpace(parts[0])
shn := strings.TrimSpace(parts[1])
value := strings.TrimSpace(parts[2])
if shnRE.MatchString(shn) {
fields = append(fields, dmiField{Name: name, Shn: shn, Value: value})
continue continue
} }
m := dmiItemRE.FindStringSubmatch(line)
if m == nil {
continue
} }
if idx := strings.IndexByte(line, '='); idx > 0 { itemName := strings.TrimSpace(m[1])
shn := strings.TrimSpace(line[:idx]) shn := m[2]
value := strings.TrimSpace(line[idx+1:]) rawValue := strings.TrimSpace(m[3])
if shnRE.MatchString(shn) { // strip trailing comment (space + //)
fields = append(fields, dmiField{Name: shn, Shn: shn, Value: value}) if idx := strings.LastIndex(rawValue, " //"); idx >= 0 {
rawValue = strings.TrimSpace(rawValue[:idx])
} }
// strip surrounding double quotes from string values
if len(rawValue) >= 2 && rawValue[0] == '"' && rawValue[len(rawValue)-1] == '"' {
rawValue = rawValue[1 : len(rawValue)-1]
} }
displayName := itemName
if currentSection != "" {
displayName = currentSection + " / " + itemName
}
fields = append(fields, dmiField{Name: displayName, Shn: shn, Value: rawValue})
} }
return fields return fields
} }