// Copyright 2021 Clayton Craft // SPDX-License-Identifier: GPL-3.0-or-later package deviceinfo import ( "bufio" "fmt" "io" "log" "os" "reflect" "strings" ) type DeviceInfo struct { AppendDtb string Arch string BootimgAppendSEAndroidEnforce string BootimgBlobpack string BootimgDtbSecond string BootimgMtkMkimage string BootimgPxa string BootimgQcdt string Dtb string FlashKernelOnUpdate string FlashOffsetBase string FlashOffsetKernel string FlashOffsetRamdisk string FlashOffsetSecond string FlashOffsetTags string FlashPagesize string GenerateBootimg string GenerateLegacyUbootInitfs string InitfsCompression string KernelCmdline string LegacyUbootLoadAddress string MesaDriver string MkinitfsPostprocess string ModulesInitfs string } func ReadDeviceinfo(file string) (DeviceInfo, error) { var deviceinfo DeviceInfo fd, err := os.Open(file) if err != nil { return deviceinfo, err } defer fd.Close() if err := unmarshal(fd, &deviceinfo); err != nil { return deviceinfo, err } return deviceinfo, nil } // Unmarshals a deviceinfo into a DeviceInfo struct func unmarshal(r io.Reader, devinfo *DeviceInfo) error { s := bufio.NewScanner(r) for s.Scan() { line := s.Text() if strings.HasPrefix(line, "#") { continue } // line isn't setting anything, so just ignore it if !strings.Contains(line, "=") { continue } // sometimes line has a comment at the end after setting an option line = strings.SplitN(line, "#", 2)[0] line = strings.TrimSpace(line) // must support having '=' in the value (e.g. kernel cmdline) parts := strings.SplitN(line, "=", 2) if len(parts) != 2 { return fmt.Errorf("error parsing deviceinfo line, invalid format: %s", line) } name, val := parts[0], parts[1] val = strings.ReplaceAll(val, "\"", "") if name == "deviceinfo_format_version" && val != "0" { return fmt.Errorf("deviceinfo format version %q is not supported", val) } fieldName := nameToField(name) if fieldName == "" { return fmt.Errorf("error parsing deviceinfo line, invalid format: %s", line) } field := reflect.ValueOf(devinfo).Elem().FieldByName(fieldName) if !field.IsValid() { // an option that meets the deviceinfo "specification", but isn't // one we care about in this module continue } field.SetString(val) } if err := s.Err(); err != nil { log.Print("unable to parse deviceinfo: ", err) return err } return nil } // Convert string into the string format used for DeviceInfo fields. // Note: does not test that the resulting field name is a valid field in the // DeviceInfo struct! func nameToField(name string) string { var field string parts := strings.Split(name, "_") for _, p := range parts { if p == "deviceinfo" { continue } field = field + strings.Title(p) } return field }