github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/kconfig/config.go (about) 1 // Copyright 2020 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package kconfig 5 6 import ( 7 "bufio" 8 "bytes" 9 "fmt" 10 "os" 11 "regexp" 12 ) 13 14 // ConfigFile represents a parsed .config file. 15 // It should not be modified directly, only by means of calling methods. 16 // The only exception is Config.Value which may be modified directly. 17 // Note: config names don't include CONFIG_ prefix, here and in other public interfaces, 18 // users of this package should never mention CONFIG_. 19 // Use Yes/Mod/No consts to check for/set config to particular values. 20 type ConfigFile struct { 21 Configs []*Config 22 Map map[string]*Config // duplicates Configs for convenience 23 comments []string 24 } 25 26 type Config struct { 27 Name string 28 Value string 29 comments []string 30 } 31 32 const ( 33 Yes = "y" 34 Mod = "m" 35 No = "---===[[[is not set]]]===---" // to make it more obvious when some code writes it directly 36 prefix = "CONFIG_" 37 ) 38 39 // Value returns config value, or No if it's not present at all. 40 func (cf *ConfigFile) Value(name string) string { 41 cfg := cf.Map[name] 42 if cfg == nil { 43 return No 44 } 45 return cfg.Value 46 } 47 48 // Set changes config value, or adds it if it's not yet present. 49 func (cf *ConfigFile) Set(name, val string) { 50 cfg := cf.Map[name] 51 if cfg == nil { 52 cfg = &Config{ 53 Name: name, 54 Value: val, 55 } 56 cf.Map[name] = cfg 57 cf.Configs = append(cf.Configs, cfg) 58 } 59 cfg.Value = val 60 cfg.comments = append(cfg.comments, cf.comments...) 61 cf.comments = nil 62 } 63 64 // Unset sets config value to No, if it's present in the config. 65 func (cf *ConfigFile) Unset(name string) { 66 cfg := cf.Map[name] 67 if cfg == nil { 68 return 69 } 70 cfg.Value = No 71 } 72 73 func (cf *ConfigFile) ModToYes() { 74 for _, cfg := range cf.Configs { 75 if cfg.Value == Mod { 76 cfg.Value = Yes 77 } 78 } 79 } 80 81 func (cf *ConfigFile) ModToNo() { 82 for _, cfg := range cf.Configs { 83 if cfg.Value == Mod { 84 cfg.Value = No 85 } 86 } 87 } 88 89 func (cf *ConfigFile) Serialize() []byte { 90 buf := new(bytes.Buffer) 91 for _, cfg := range cf.Configs { 92 for _, comment := range cfg.comments { 93 fmt.Fprintf(buf, "%v\n", comment) 94 } 95 if cfg.Value == No { 96 fmt.Fprintf(buf, "# %v%v is not set\n", prefix, cfg.Name) 97 } else { 98 fmt.Fprintf(buf, "%v%v=%v\n", prefix, cfg.Name, cfg.Value) 99 } 100 } 101 for _, comment := range cf.comments { 102 fmt.Fprintf(buf, "%v\n", comment) 103 } 104 return buf.Bytes() 105 } 106 107 func ParseConfig(file string) (*ConfigFile, error) { 108 data, err := os.ReadFile(file) 109 if err != nil { 110 return nil, fmt.Errorf("failed to open .config file %v: %w", file, err) 111 } 112 return ParseConfigData(data, file) 113 } 114 115 func ParseConfigData(data []byte, file string) (*ConfigFile, error) { 116 cf := &ConfigFile{ 117 Map: make(map[string]*Config), 118 } 119 s := bufio.NewScanner(bytes.NewReader(data)) 120 for s.Scan() { 121 cf.parseLine(s.Text()) 122 } 123 return cf, nil 124 } 125 126 func (cf *ConfigFile) Clone() *ConfigFile { 127 cf1 := &ConfigFile{ 128 Map: make(map[string]*Config), 129 comments: cf.comments, 130 } 131 for _, cfg := range cf.Configs { 132 cfg1 := new(Config) 133 *cfg1 = *cfg 134 cf1.Configs = append(cf1.Configs, cfg1) 135 cf1.Map[cfg1.Name] = cfg1 136 } 137 return cf1 138 } 139 140 func (cf *ConfigFile) parseLine(text string) { 141 if match := reConfigY.FindStringSubmatch(text); match != nil { 142 cf.Set(match[1], match[2]) 143 } else if match := reConfigN.FindStringSubmatch(text); match != nil { 144 cf.Set(match[1], No) 145 } else { 146 cf.comments = append(cf.comments, text) 147 } 148 } 149 150 var ( 151 reConfigY = regexp.MustCompile(`^` + prefix + `([A-Za-z0-9_]+)=(y|m|(?:-?[0-9]+)|(?:0x[0-9a-fA-F]+)|(?:".*?"))$`) 152 reConfigN = regexp.MustCompile(`^# ` + prefix + `([A-Za-z0-9_]+) is not set$`) 153 )