github.com/undoio/delve@v1.9.0/pkg/config/config.go (about) 1 package config 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 "os" 8 "os/user" 9 "path" 10 "runtime" 11 12 "gopkg.in/yaml.v2" 13 ) 14 15 const ( 16 configDir string = "dlv" 17 configDirHidden string = ".dlv" 18 configFile string = "config.yml" 19 ) 20 21 // SubstitutePathRule describes a rule for substitution of path to source code file. 22 type SubstitutePathRule struct { 23 // Directory path will be substituted if it matches `From`. 24 From string 25 // Path to which substitution is performed. 26 To string 27 } 28 29 // SubstitutePathRules is a slice of source code path substitution rules. 30 type SubstitutePathRules []SubstitutePathRule 31 32 // Config defines all configuration options available to be set through the config file. 33 type Config struct { 34 // Commands aliases. 35 Aliases map[string][]string `yaml:"aliases"` 36 // Source code path substitution rules. 37 SubstitutePath SubstitutePathRules `yaml:"substitute-path"` 38 39 // MaxStringLen is the maximum string length that the commands print, 40 // locals, args and vars should read (in verbose mode). 41 MaxStringLen *int `yaml:"max-string-len,omitempty"` 42 // MaxArrayValues is the maximum number of array items that the commands 43 // print, locals, args and vars should read (in verbose mode). 44 MaxArrayValues *int `yaml:"max-array-values,omitempty"` 45 // MaxVariableRecurse is output evaluation depth of nested struct members, array and 46 // slice items and dereference pointers 47 MaxVariableRecurse *int `yaml:"max-variable-recurse,omitempty"` 48 // DisassembleFlavor allow user to specify output syntax flavor of assembly, one of 49 // this list "intel"(default), "gnu", "go" 50 DisassembleFlavor *string `yaml:"disassemble-flavor,omitempty"` 51 52 // If ShowLocationExpr is true whatis will print the DWARF location 53 // expression for its argument. 54 ShowLocationExpr bool `yaml:"show-location-expr"` 55 56 // Source list line-number color, as a terminal escape sequence. 57 // For historic reasons, this can also be an integer color code. 58 SourceListLineColor interface{} `yaml:"source-list-line-color"` 59 60 // Source list arrow color, as a terminal escape sequence. 61 SourceListArrowColor string `yaml:"source-list-arrow-color"` 62 63 // Source list keyword color, as a terminal escape sequence. 64 SourceListKeywordColor string `yaml:"source-list-keyword-color"` 65 66 // Source list string color, as a terminal escape sequence. 67 SourceListStringColor string `yaml:"source-list-string-color"` 68 69 // Source list number color, as a terminal escape sequence. 70 SourceListNumberColor string `yaml:"source-list-number-color"` 71 72 // Source list comment color, as a terminal escape sequence. 73 SourceListCommentColor string `yaml:"source-list-comment-color"` 74 75 // number of lines to list above and below cursor when printfile() is 76 // called (i.e. when execution stops, listCommand is used, etc) 77 SourceListLineCount *int `yaml:"source-list-line-count,omitempty"` 78 79 // DebugFileDirectories is the list of directories Delve will use 80 // in order to resolve external debug info files. 81 DebugInfoDirectories []string `yaml:"debug-info-directories"` 82 } 83 84 func (c *Config) GetSourceListLineCount() int { 85 n := 5 // default value 86 lcp := c.SourceListLineCount 87 if lcp != nil && *lcp >= 0 { 88 n = *lcp 89 } 90 return n 91 } 92 93 // LoadConfig attempts to populate a Config object from the config.yml file. 94 func LoadConfig() (*Config, error) { 95 err := createConfigPath() 96 if err != nil { 97 return &Config{}, fmt.Errorf("could not create config directory: %v", err) 98 } 99 fullConfigFile, err := GetConfigFilePath(configFile) 100 if err != nil { 101 return &Config{}, fmt.Errorf("unable to get config file path: %v", err) 102 } 103 104 hasOldConfig, _ := hasOldConfig() 105 106 if hasOldConfig { 107 userHomeDir := getUserHomeDir() 108 oldLocation := path.Join(userHomeDir, configDirHidden) 109 if err := moveOldConfig(); err != nil { 110 return &Config{}, fmt.Errorf("unable to move old config: %v", err) 111 } 112 113 if err := os.RemoveAll(oldLocation); err != nil { 114 return &Config{}, fmt.Errorf("unable to remove old config location: %v", err) 115 } 116 fmt.Fprintf(os.Stderr, "Successfully moved config from: %s to: %s\n", oldLocation, fullConfigFile) 117 } 118 119 f, err := os.Open(fullConfigFile) 120 if err != nil { 121 f, err = createDefaultConfig(fullConfigFile) 122 if err != nil { 123 return &Config{}, fmt.Errorf("error creating default config file: %v", err) 124 } 125 } 126 defer f.Close() 127 128 data, err := ioutil.ReadAll(f) 129 if err != nil { 130 return &Config{}, fmt.Errorf("unable to read config data: %v", err) 131 } 132 133 var c Config 134 err = yaml.Unmarshal(data, &c) 135 if err != nil { 136 return &Config{}, fmt.Errorf("unable to decode config file: %v", err) 137 } 138 139 if len(c.DebugInfoDirectories) == 0 { 140 c.DebugInfoDirectories = []string{"/usr/lib/debug/.build-id"} 141 } 142 143 return &c, nil 144 } 145 146 // SaveConfig will marshal and save the config struct 147 // to disk. 148 func SaveConfig(conf *Config) error { 149 fullConfigFile, err := GetConfigFilePath(configFile) 150 if err != nil { 151 return err 152 } 153 154 out, err := yaml.Marshal(*conf) 155 if err != nil { 156 return err 157 } 158 159 f, err := os.Create(fullConfigFile) 160 if err != nil { 161 return err 162 } 163 defer f.Close() 164 165 _, err = f.Write(out) 166 return err 167 } 168 169 // moveOldConfig attempts to move config to new location 170 // $HOME/.dlv to $XDG_CONFIG_HOME/dlv 171 func moveOldConfig() error { 172 if os.Getenv("XDG_CONFIG_HOME") == "" && runtime.GOOS != "linux" { 173 return nil 174 } 175 176 userHomeDir := getUserHomeDir() 177 178 p := path.Join(userHomeDir, configDirHidden, configFile) 179 _, err := os.Stat(p) 180 if err != nil { 181 return fmt.Errorf("unable to read config file located at: %s", p) 182 } 183 184 newFile, err := GetConfigFilePath(configFile) 185 if err != nil { 186 return fmt.Errorf("unable to read config file located at: %s", err) 187 } 188 189 if err := os.Rename(p, newFile); err != nil { 190 return fmt.Errorf("unable to move %s to %s", p, newFile) 191 } 192 return nil 193 } 194 195 func createDefaultConfig(path string) (*os.File, error) { 196 f, err := os.Create(path) 197 if err != nil { 198 return nil, fmt.Errorf("unable to create config file: %v", err) 199 } 200 err = writeDefaultConfig(f) 201 if err != nil { 202 return nil, fmt.Errorf("unable to write default configuration: %v", err) 203 } 204 f.Seek(0, io.SeekStart) 205 return f, nil 206 } 207 208 func writeDefaultConfig(f *os.File) error { 209 _, err := f.WriteString( 210 `# Configuration file for the delve debugger. 211 212 # This is the default configuration file. Available options are provided, but disabled. 213 # Delete the leading hash mark to enable an item. 214 215 # Uncomment the following line and set your preferred ANSI color for source 216 # line numbers in the (list) command. The default is 34 (dark blue). See 217 # https://en.wikipedia.org/wiki/ANSI_escape_code#3/4_bit 218 # source-list-line-color: "\x1b[34m" 219 220 # Uncomment the following lines to change the colors used by syntax highlighting. 221 # source-list-keyword-color: "\x1b[0m" 222 # source-list-string-color: "\x1b[92m" 223 # source-list-number-color: "\x1b[0m" 224 # source-list-comment-color: "\x1b[95m" 225 # source-list-arrow-color: "\x1b[93m" 226 227 # Uncomment to change the number of lines printed above and below cursor when 228 # listing source code. 229 # source-list-line-count: 5 230 231 # Provided aliases will be added to the default aliases for a given command. 232 aliases: 233 # command: ["alias1", "alias2"] 234 235 # Define sources path substitution rules. Can be used to rewrite a source path stored 236 # in program's debug information, if the sources were moved to a different place 237 # between compilation and debugging. 238 # Note that substitution rules will not be used for paths passed to "break" and "trace" 239 # commands. 240 substitute-path: 241 # - {from: path, to: path} 242 243 # Maximum number of elements loaded from an array. 244 # max-array-values: 64 245 246 # Maximum loaded string length. 247 # max-string-len: 64 248 249 # Output evaluation. 250 # max-variable-recurse: 1 251 252 # Uncomment the following line to make the whatis command also print the DWARF location expression of its argument. 253 # show-location-expr: true 254 255 # Allow user to specify output syntax flavor of assembly, one of this list "intel"(default), "gnu", "go". 256 # disassemble-flavor: intel 257 258 # List of directories to use when searching for separate debug info files. 259 debug-info-directories: ["/usr/lib/debug/.build-id"] 260 `) 261 return err 262 } 263 264 // createConfigPath creates the directory structure at which all config files are saved. 265 func createConfigPath() error { 266 path, err := GetConfigFilePath("") 267 if err != nil { 268 return err 269 } 270 return os.MkdirAll(path, 0700) 271 } 272 273 // GetConfigFilePath gets the full path to the given config file name. 274 func GetConfigFilePath(file string) (string, error) { 275 if configPath := os.Getenv("XDG_CONFIG_HOME"); configPath != "" { 276 return path.Join(configPath, configDir, file), nil 277 } 278 279 userHomeDir := getUserHomeDir() 280 281 if runtime.GOOS == "linux" { 282 return path.Join(userHomeDir, ".config", configDir, file), nil 283 } 284 return path.Join(userHomeDir, configDirHidden, file), nil 285 } 286 287 // Checks if the user has a config at the old location: $HOME/.dlv 288 func hasOldConfig() (bool, error) { 289 // If you don't have XDG_CONFIG_HOME set and aren't on Linux you have nothing to move 290 if os.Getenv("XDG_CONFIG_HOME") == "" && runtime.GOOS != "linux" { 291 return false, nil 292 } 293 294 userHomeDir := getUserHomeDir() 295 296 o := path.Join(userHomeDir, configDirHidden, configFile) 297 _, err := os.Stat(o) 298 if err != nil { 299 if os.IsNotExist(err) { 300 return false, nil 301 } 302 return false, err 303 } 304 return true, nil 305 } 306 307 func getUserHomeDir() string { 308 userHomeDir := "." 309 usr, err := user.Current() 310 if err == nil { 311 userHomeDir = usr.HomeDir 312 } 313 return userHomeDir 314 }