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 = &section
    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 = &section
    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 = &section
    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 = &section
    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 = &section
    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 = &section
    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  }