github.com/driusan/dgit@v0.0.0-20221118233547-f39f0c15edbb/git/config.go (about) 1 package git 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 "log" 8 "os" 9 "path/filepath" 10 "strings" 11 ) 12 13 // This file provides a stupid way of parsing git config files. 14 // It's not very efficient, but for now it gets the job done. 15 // (There's a lot more low hanging fruit before optimizing this..) 16 type GitConfigValues map[string]string 17 18 type GitConfigSection struct { 19 name, subsection string 20 values GitConfigValues 21 } 22 type GitConfig struct { 23 sections []GitConfigSection 24 fname string 25 } 26 27 func (g *GitConfig) SetConfig(name, value string) { 28 pieces := strings.Split(name, ".") 29 var key string 30 var sec *GitConfigSection 31 value = strings.TrimSpace(value) 32 argChecker: 33 switch len(pieces) { 34 case 2: 35 key = strings.TrimSpace(pieces[1]) 36 for _, section := range g.sections { 37 log.Printf("Comparing %s to %s\n", section.name, pieces[0]) 38 if section.name == pieces[0] { 39 sec = §ion 40 break argChecker 41 } 42 } 43 log.Printf("Couldn't find %s, creating\n", pieces[0]) 44 section := GitConfigSection{pieces[0], "", make(map[string]string, 0)} 45 sec = §ion 46 g.sections = append(g.sections, section) 47 case 3: 48 key = strings.TrimSpace(pieces[2]) 49 for _, section := range g.sections { 50 log.Printf("Comparing %s to %s and %s to %s\n", section.name, pieces[0], section.subsection, pieces[1]) 51 if section.name == pieces[0] && section.subsection == pieces[1] { 52 sec = §ion 53 break argChecker 54 } 55 } 56 log.Printf("Couldn't find %s %s, creating\n", pieces[0], pieces[1]) 57 section := GitConfigSection{pieces[0], pieces[1], make(map[string]string, 0)} 58 sec = §ion 59 g.sections = append(g.sections, section) 60 } 61 62 if sec != nil { 63 sec.values[key] = value 64 } else { 65 // TODO Always auto-create the sections 66 log.Printf("Couldn't find section %v\n", name) 67 } 68 } 69 70 func (g *GitConfig) Unset(name string) int { 71 pieces := strings.Split(name, ".") 72 var key string 73 var sec *GitConfigSection 74 75 argChecker: 76 switch len(pieces) { 77 case 2: 78 key = strings.TrimSpace(pieces[1]) 79 for _, section := range g.sections { 80 log.Printf("Comparing %s to %s\n", section.name, pieces[0]) 81 if section.name == pieces[0] { 82 sec = §ion 83 break argChecker 84 } 85 } 86 case 3: 87 key = strings.TrimSpace(pieces[2]) 88 for _, section := range g.sections { 89 if section.name == pieces[0] && section.subsection == pieces[1] { 90 sec = §ion 91 break argChecker 92 } 93 } 94 } 95 96 if sec != nil { 97 if _, ok := sec.values[key]; !ok { 98 return 5 99 } 100 delete(sec.values, key) 101 return 0 102 } else { 103 return 5 104 } 105 } 106 107 func (g *GitConfig) GetConfig(name string) (string, int) { 108 109 pieces := strings.Split(name, ".") 110 111 switch len(pieces) { 112 case 2: 113 for _, section := range g.sections { 114 if section.name == pieces[0] { 115 val, ok := section.values[pieces[1]] 116 if !ok { 117 return "", 1 118 } 119 return val, 0 120 } 121 } 122 case 3: 123 for _, section := range g.sections { 124 if section.name == pieces[0] && section.subsection == pieces[1] { 125 val, ok := section.values[pieces[2]] 126 if !ok { 127 return "", 1 128 } 129 return val, 0 130 } 131 } 132 133 } 134 135 return "", 1 136 } 137 138 func (g *GitConfig) GetConfigList() []string { 139 list := []string{} 140 141 for _, section := range g.sections { 142 for key, value := range section.values { 143 if section.subsection != "" { 144 list = append(list, section.name+"."+section.subsection+"."+key+"="+value) 145 } else { 146 list = append(list, section.name+"."+key+"="+value) 147 } 148 } 149 } 150 151 return list 152 } 153 154 // Gets all config sections that match name and subsection. The empty 155 // string matches all names/subsections. 156 func (g *GitConfig) GetConfigSections(name, subsection string) []GitConfigSection { 157 matches := make([]GitConfigSection, 0, len(g.sections)) 158 for _, sect := range g.sections { 159 if name != "" && sect.name != name { 160 continue 161 } 162 if subsection != "" && subsection != sect.subsection { 163 continue 164 } 165 matches = append(matches, sect) 166 } 167 return matches 168 } 169 170 func (g GitConfig) WriteFile(w io.Writer) { 171 for _, section := range g.sections { 172 if section.subsection == "" { 173 fmt.Fprintf(w, "[%s]\n", section.name) 174 } else { 175 fmt.Fprintf(w, "[%s \"%s\"]\n", section.name, section.subsection) 176 } 177 178 for key, value := range section.values { 179 fmt.Fprintf(w, "\t%s = %s\n", key, value) 180 } 181 182 } 183 } 184 func (s *GitConfigSection) ParseValues(valueslines string) { 185 lines := strings.Split(valueslines, "\n") 186 s.values = make(map[string]string) 187 188 for _, line := range lines { 189 trimmed := strings.TrimSpace(line) 190 if trimmed == "" { 191 continue 192 } 193 split := strings.Split(trimmed, "=") 194 if len(split) < 1 { 195 panic("couldn't parse value") 196 } 197 varname := strings.TrimSpace(split[0]) 198 199 log.Printf("Parsed config variable %v\n", varname) 200 s.values[varname] = strings.TrimSpace(strings.Join(split[1:], "=")) 201 202 } 203 } 204 205 func (s *GitConfigSection) ParseSectionHeader(headerline string) { 206 s.name = headerline 207 parsingSubsection := false 208 subsectionStart := 0 209 for idx, b := range headerline { 210 211 if b == '"' && parsingSubsection == true { 212 213 parsingSubsection = true 214 s.subsection = strings.TrimSpace(headerline[subsectionStart:idx]) 215 216 } 217 if b == '"' && parsingSubsection == false { 218 parsingSubsection = true 219 subsectionStart = idx + 1 220 221 s.name = strings.TrimSpace(headerline[0:idx]) 222 223 } 224 } 225 if subsectionStart == 0 { 226 s.name = headerline 227 } 228 } 229 230 func ParseConfig(configFile io.Reader) GitConfig { 231 rawdata, _ := ioutil.ReadAll(configFile) 232 section := &GitConfigSection{} 233 parsingSectionName := false 234 parsingValues := false 235 var sect *GitConfigSection = nil 236 var sections []GitConfigSection 237 lastBracket := 0 238 lastClosingBracket := 0 239 240 for idx, b := range rawdata { 241 if b == '[' && parsingSectionName == false { 242 243 parsingSectionName = true 244 lastBracket = idx 245 if parsingValues == true { 246 section.ParseValues(string(rawdata[lastClosingBracket+1 : idx])) 247 parsingValues = false 248 249 if sect != nil { 250 for k, v := range section.values { 251 sect.values[k] = v 252 } 253 sect = nil 254 } else { 255 sections = append(sections, *section) 256 } 257 } 258 section = &GitConfigSection{} 259 } 260 if b == ']' && parsingSectionName == true { 261 section.ParseSectionHeader(string(rawdata[lastBracket+1 : idx])) 262 263 for _, s := range sections { 264 if s.name == section.name && s.subsection == section.subsection { 265 sect = &s 266 break 267 } 268 } 269 270 parsingValues = true 271 parsingSectionName = false 272 lastClosingBracket = idx 273 } 274 if idx == len(rawdata)-1 && parsingValues == true { 275 section.ParseValues(string(rawdata[lastClosingBracket+1 : idx])) 276 277 if sect != nil { 278 for k, v := range section.values { 279 sect.values[k] = v 280 } 281 sect = nil 282 } else { 283 sections = append(sections, *section) 284 } 285 } 286 } 287 return GitConfig{sections: sections} 288 } 289 290 func findGlobalConfigFile() (string, error) { 291 home := os.Getenv("HOME") 292 if home == "" { 293 home = os.Getenv("home") // On some OSes, it is home 294 } 295 296 if home == "" { 297 return "", fmt.Errorf("Global git configuration could not be found since HOME and home environment variables were not defined.") 298 } 299 300 return home + "/.gitconfig", nil 301 } 302 303 func LoadGlobalConfig() (GitConfig, error) { 304 fname, err := findGlobalConfigFile() 305 if err != nil { 306 return GitConfig{}, err 307 } 308 309 file, err := os.OpenFile(fname, os.O_RDWR|os.O_CREATE, 0644) 310 if err != nil { 311 return GitConfig{}, err 312 } 313 config := ParseConfig(file) 314 err = file.Close() 315 if err != nil { 316 return GitConfig{}, err 317 } 318 319 config.fname = fname 320 321 return config, nil 322 } 323 324 func LoadLocalConfig(c *Client) (GitConfig, error) { 325 fname := filepath.Join(c.GitDir.String(), "config") 326 327 file, err := os.OpenFile(fname, os.O_RDWR|os.O_CREATE, 0644) 328 if err != nil { 329 return GitConfig{}, err 330 } 331 config := ParseConfig(file) 332 if err := file.Close(); err != nil { 333 return GitConfig{}, err 334 } 335 336 config.fname = fname 337 338 return config, nil 339 } 340 341 func (g GitConfig) WriteConfig() error { 342 err := os.Remove(g.fname) 343 if err != nil { 344 return err 345 } 346 configFile, err := os.Create(g.fname) 347 if err != nil { 348 return err 349 } 350 g.WriteFile(configFile) 351 return configFile.Close() 352 }