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"`
}
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".
// Supports two formats:
// - Name|Shn|Value (pipe-separated, primary)
// - Shn=Value (key=value fallback)
// Real format (from SAA User Guide 4.8.1):
//
// 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 {
var fields []dmiField
currentSection := ""
for _, line := range strings.Split(content, "\n") {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
if line == "" || strings.HasPrefix(line, "//") || strings.HasPrefix(line, "#") {
continue
}
lower := strings.ToLower(line)
if strings.HasPrefix(lower, "version=") || strings.HasPrefix(lower, "[") {
if dmiVersionRE.MatchString(line) {
continue
}
parts := strings.SplitN(line, "|", 3)
if len(parts) == 3 {
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
}
if m := dmiSectionRE.FindStringSubmatch(line); m != nil {
currentSection = strings.TrimSpace(m[1])
continue
}
if idx := strings.IndexByte(line, '='); idx > 0 {
shn := strings.TrimSpace(line[:idx])
value := strings.TrimSpace(line[idx+1:])
if shnRE.MatchString(shn) {
fields = append(fields, dmiField{Name: shn, Shn: shn, Value: value})
}
m := dmiItemRE.FindStringSubmatch(line)
if m == nil {
continue
}
itemName := strings.TrimSpace(m[1])
shn := m[2]
rawValue := strings.TrimSpace(m[3])
// strip trailing comment (space + //)
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
}