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  )