|
@@ -1,428 +0,0 @@
|
|
|
-// conf.go - Functions for pacman.conf parsing.
|
|
|
-//
|
|
|
-// Copyright (c) 2013 The go-alpm Authors
|
|
|
-//
|
|
|
-// MIT Licensed. See LICENSE for details.
|
|
|
-
|
|
|
-package alpm
|
|
|
-
|
|
|
-import (
|
|
|
- "bufio"
|
|
|
- "bytes"
|
|
|
- "fmt"
|
|
|
- "io"
|
|
|
- "os"
|
|
|
- "reflect"
|
|
|
- "strconv"
|
|
|
- "strings"
|
|
|
- "syscall"
|
|
|
-)
|
|
|
-
|
|
|
-type PacmanOption uint
|
|
|
-
|
|
|
-const (
|
|
|
- ConfUseSyslog PacmanOption = 1 << iota
|
|
|
- ConfColor
|
|
|
- ConfTotalDownload
|
|
|
- ConfCheckSpace
|
|
|
- ConfVerbosePkgLists
|
|
|
- ConfILoveCandy
|
|
|
-)
|
|
|
-
|
|
|
-var optionsMap = map[string]PacmanOption{
|
|
|
- "UseSyslog": ConfUseSyslog,
|
|
|
- "Color": ConfColor,
|
|
|
- "TotalDownload": ConfTotalDownload,
|
|
|
- "CheckSpace": ConfCheckSpace,
|
|
|
- "VerbosePkgLists": ConfVerbosePkgLists,
|
|
|
- "ILoveCandy": ConfILoveCandy,
|
|
|
-}
|
|
|
-
|
|
|
-// PacmanConfig is a type for holding pacman options parsed from pacman
|
|
|
-// configuration data passed to ParseConfig.
|
|
|
-type PacmanConfig struct {
|
|
|
- RootDir string
|
|
|
- DBPath string
|
|
|
- CacheDir []string
|
|
|
- HookDir []string
|
|
|
- GPGDir string
|
|
|
- LogFile string
|
|
|
- HoldPkg []string
|
|
|
- IgnorePkg []string
|
|
|
- IgnoreGroup []string
|
|
|
- Include []string
|
|
|
- Architecture string
|
|
|
- XferCommand string
|
|
|
- NoUpgrade []string
|
|
|
- NoExtract []string
|
|
|
- CleanMethod []string
|
|
|
- SigLevel SigLevel
|
|
|
- LocalFileSigLevel SigLevel
|
|
|
- RemoteFileSigLevel SigLevel
|
|
|
- UseDelta float64
|
|
|
- Options PacmanOption
|
|
|
- Repos []RepoConfig
|
|
|
-}
|
|
|
-
|
|
|
-// RepoConfig is a type that stores the signature level of a repository
|
|
|
-// specified in the pacman config file.
|
|
|
-type RepoConfig struct {
|
|
|
- Name string
|
|
|
- SigLevel SigLevel
|
|
|
- Usage Usage
|
|
|
- Servers []string
|
|
|
-}
|
|
|
-
|
|
|
-// Constants for pacman configuration parsing
|
|
|
-const (
|
|
|
- tokenSection = iota
|
|
|
- tokenKey
|
|
|
- tokenComment
|
|
|
-)
|
|
|
-
|
|
|
-type iniToken struct {
|
|
|
- Type uint
|
|
|
- Name string
|
|
|
- Values []string
|
|
|
-}
|
|
|
-
|
|
|
-type confReader struct {
|
|
|
- *bufio.Reader
|
|
|
- Lineno uint
|
|
|
-}
|
|
|
-
|
|
|
-// newConfReader reads from the io.Reader if it is buffered and returns a
|
|
|
-// confReader containing the number of bytes read and 0 for the first line. If
|
|
|
-// r is not a buffered reader, a new buffered reader is created using r as its
|
|
|
-// input and returned.
|
|
|
-func newConfReader(r io.Reader) confReader {
|
|
|
- if buf, ok := r.(*bufio.Reader); ok {
|
|
|
- return confReader{buf, 0}
|
|
|
- }
|
|
|
- buf := bufio.NewReader(r)
|
|
|
- return confReader{buf, 0}
|
|
|
-}
|
|
|
-
|
|
|
-func (rdr *confReader) ParseLine() (tok iniToken, err error) {
|
|
|
- line, overflow, err := rdr.ReadLine()
|
|
|
- switch {
|
|
|
- case err != nil:
|
|
|
- return
|
|
|
- case overflow:
|
|
|
- err = fmt.Errorf("line %d too long", rdr.Lineno)
|
|
|
- return
|
|
|
- }
|
|
|
- rdr.Lineno++
|
|
|
-
|
|
|
- line = bytes.TrimSpace(line)
|
|
|
- if len(line) == 0 {
|
|
|
- tok.Type = tokenComment
|
|
|
- return
|
|
|
- }
|
|
|
- switch line[0] {
|
|
|
- case '#':
|
|
|
- tok.Type = tokenComment
|
|
|
- return
|
|
|
- case '[':
|
|
|
- closing := bytes.IndexByte(line, ']')
|
|
|
- if closing < 0 {
|
|
|
- err = fmt.Errorf("missing ']' is section name at line %d", rdr.Lineno)
|
|
|
- return
|
|
|
- }
|
|
|
- tok.Name = string(line[1:closing])
|
|
|
- if closing+1 < len(line) {
|
|
|
- err = fmt.Errorf("trailing characters %q after section name %s",
|
|
|
- line[closing+1:], tok.Name)
|
|
|
- return
|
|
|
- }
|
|
|
- return
|
|
|
- default:
|
|
|
- tok.Type = tokenKey
|
|
|
- if idx := bytes.IndexByte(line, '='); idx >= 0 {
|
|
|
- optname := bytes.TrimSpace(line[:idx])
|
|
|
- values := bytes.Split(line[idx+1:], []byte{' '})
|
|
|
- tok.Name = string(optname)
|
|
|
- tok.Values = make([]string, 0, len(values))
|
|
|
- for _, word := range values {
|
|
|
- word = bytes.TrimSpace(word)
|
|
|
- if len(word) > 0 {
|
|
|
- tok.Values = append(tok.Values, string(word))
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- // boolean option
|
|
|
- tok.Name = string(line)
|
|
|
- tok.Values = nil
|
|
|
- }
|
|
|
- return
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-func ParseConfig(r io.Reader) (conf PacmanConfig, err error) {
|
|
|
- rdr := newConfReader(r)
|
|
|
- rdrStack := []confReader{rdr}
|
|
|
- conf.SetDefaults()
|
|
|
- confReflect := reflect.ValueOf(&conf).Elem()
|
|
|
- var currentSection string
|
|
|
- var curRepo *RepoConfig
|
|
|
-lineloop:
|
|
|
- for {
|
|
|
- line, err := rdr.ParseLine()
|
|
|
- // fmt.Printf("%+v\n", line)
|
|
|
- switch err {
|
|
|
- case io.EOF:
|
|
|
- // pop reader stack.
|
|
|
- l := len(rdrStack)
|
|
|
- if l == 1 {
|
|
|
- break lineloop
|
|
|
- }
|
|
|
- rdr = rdrStack[l-2]
|
|
|
- rdrStack = rdrStack[:l-1]
|
|
|
- default:
|
|
|
- break lineloop
|
|
|
- case nil:
|
|
|
- // Ok.
|
|
|
- }
|
|
|
-
|
|
|
- switch line.Type {
|
|
|
- case tokenComment:
|
|
|
- case tokenSection:
|
|
|
- currentSection = line.Name
|
|
|
- if currentSection != "options" {
|
|
|
- conf.Repos = append(conf.Repos, RepoConfig{})
|
|
|
- curRepo = &conf.Repos[len(conf.Repos)-1]
|
|
|
- curRepo.Name = line.Name
|
|
|
- }
|
|
|
- case tokenKey:
|
|
|
- switch line.Name {
|
|
|
- case "SigLevel":
|
|
|
- // TODO: implement SigLevel parsing.
|
|
|
- continue lineloop
|
|
|
- case "Usage":
|
|
|
- for _, usage := range line.Values {
|
|
|
- switch usage {
|
|
|
- case "Sync":
|
|
|
- curRepo.Usage |= UsageSync
|
|
|
- case "Search":
|
|
|
- curRepo.Usage |= UsageSearch
|
|
|
- case "Install":
|
|
|
- curRepo.Usage |= UsageInstall
|
|
|
- case "Upgrade":
|
|
|
- curRepo.Usage |= UsageUpgrade
|
|
|
- case "All":
|
|
|
- curRepo.Usage |= UsageAll
|
|
|
- default:
|
|
|
- err = fmt.Errorf("unknown option at line %d: %s", rdr.Lineno, line.Name)
|
|
|
- break lineloop
|
|
|
- }
|
|
|
- }
|
|
|
- case "Server":
|
|
|
- curRepo.Servers = append(curRepo.Servers, line.Values...)
|
|
|
- continue lineloop
|
|
|
- case "Include":
|
|
|
- conf.Include = append(conf.Include, line.Values[0])
|
|
|
- f, err := os.Open(line.Values[0])
|
|
|
- if err != nil {
|
|
|
- err = fmt.Errorf("error while processing Include directive at line %d: %s",
|
|
|
- rdr.Lineno, err)
|
|
|
- break lineloop
|
|
|
- }
|
|
|
- rdr = newConfReader(f)
|
|
|
- rdrStack = append(rdrStack, rdr)
|
|
|
- continue lineloop
|
|
|
- case "UseDelta":
|
|
|
- if len(line.Values) > 0 {
|
|
|
- deltaRatio, err := strconv.ParseFloat(line.Values[0], 64)
|
|
|
-
|
|
|
- if err != nil {
|
|
|
- break lineloop
|
|
|
- }
|
|
|
-
|
|
|
- conf.UseDelta = deltaRatio
|
|
|
- }
|
|
|
- continue lineloop
|
|
|
- }
|
|
|
-
|
|
|
- if currentSection != "options" {
|
|
|
- err = fmt.Errorf("option %s outside of [options] section, at line %d",
|
|
|
- line.Name, rdr.Lineno)
|
|
|
- break lineloop
|
|
|
- }
|
|
|
- // main options.
|
|
|
- if opt, ok := optionsMap[line.Name]; ok {
|
|
|
- // boolean option.
|
|
|
- conf.Options |= opt
|
|
|
- } else {
|
|
|
- // key-value option.
|
|
|
- fld := confReflect.FieldByName(line.Name)
|
|
|
- if !fld.IsValid() || !fld.CanAddr() {
|
|
|
- _ = fmt.Errorf("unknown option at line %d: %s", rdr.Lineno, line.Name)
|
|
|
- continue
|
|
|
- }
|
|
|
-
|
|
|
- switch fieldP := fld.Addr().Interface().(type) {
|
|
|
- case *string:
|
|
|
- // single valued option.
|
|
|
- *fieldP = strings.Join(line.Values, " ")
|
|
|
- case *[]string:
|
|
|
- //many valued option.
|
|
|
- *fieldP = append(*fieldP, line.Values...)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if len(conf.CleanMethod) == 0 {
|
|
|
- conf.CleanMethod = []string{"KeepInstalled"}
|
|
|
- }
|
|
|
-
|
|
|
- if len(conf.CacheDir) == 0 {
|
|
|
- conf.CacheDir = []string{"/var/cache/pacman/pkg/"} //should only be set if the config does not specify this
|
|
|
- }
|
|
|
-
|
|
|
- for n, _ := range conf.Repos {
|
|
|
- repo := &conf.Repos[n]
|
|
|
- if repo.Usage == 0 {
|
|
|
- repo.Usage = UsageAll
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return conf, err
|
|
|
-}
|
|
|
-
|
|
|
-func (conf *PacmanConfig) SetDefaults() {
|
|
|
- conf.RootDir = "/"
|
|
|
- conf.DBPath = "/var/lib/pacman"
|
|
|
- conf.DBPath = "/var/lib/pacman/"
|
|
|
- conf.HookDir = []string{"/etc/pacman.d/hooks/"} //should be added to whatever the config states
|
|
|
- conf.GPGDir = "/etc/pacman.d/gnupg/"
|
|
|
- conf.LogFile = "/var/log/pacman.log"
|
|
|
- conf.UseDelta = 0.7
|
|
|
-
|
|
|
- conf.SigLevel = SigPackage | SigPackageOptional | SigDatabase | SigDatabaseOptional
|
|
|
- conf.LocalFileSigLevel = SigUseDefault
|
|
|
- conf.RemoteFileSigLevel = SigUseDefault
|
|
|
-}
|
|
|
-
|
|
|
-func getArch() (string, error) {
|
|
|
- var uname syscall.Utsname
|
|
|
- err := syscall.Uname(&uname)
|
|
|
- if err != nil {
|
|
|
- return "", err
|
|
|
- }
|
|
|
- var arch [65]byte
|
|
|
- for i, c := range uname.Machine {
|
|
|
- if c == 0 {
|
|
|
- return string(arch[:i]), nil
|
|
|
- }
|
|
|
- arch[i] = byte(c)
|
|
|
- }
|
|
|
- return string(arch[:]), nil
|
|
|
-}
|
|
|
-
|
|
|
-func (conf *PacmanConfig) CreateHandle() (*Handle, error) {
|
|
|
- h, err := Init(conf.RootDir, conf.DBPath)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
- if conf.Architecture == "auto" {
|
|
|
- conf.Architecture, err = getArch()
|
|
|
- if err != nil {
|
|
|
- return nil, fmt.Errorf("architecture is 'auto' but couldn't uname()")
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- for _, repoconf := range conf.Repos {
|
|
|
- // TODO: set SigLevel
|
|
|
- db, err := h.RegisterSyncDb(repoconf.Name, 0)
|
|
|
- if err == nil {
|
|
|
- for i, addr := range repoconf.Servers {
|
|
|
- addr = strings.Replace(addr, "$repo", repoconf.Name, -1)
|
|
|
- addr = strings.Replace(addr, "$arch", conf.Architecture, -1)
|
|
|
- repoconf.Servers[i] = addr
|
|
|
- }
|
|
|
- db.SetServers(repoconf.Servers)
|
|
|
- db.SetUsage(repoconf.Usage)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- err = h.SetCacheDirs(conf.CacheDir...)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
-
|
|
|
- // add hook directories 1-by-1 to avoid overwriting the system directory
|
|
|
- for _, dir := range conf.HookDir {
|
|
|
- err = h.AddHookDir(dir)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- err = h.SetGPGDir(conf.GPGDir)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
-
|
|
|
- err = h.SetLogFile(conf.LogFile)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
-
|
|
|
- err = h.SetIgnorePkgs(conf.IgnorePkg...)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
-
|
|
|
- err = h.SetIgnoreGroups(conf.IgnoreGroup...)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
-
|
|
|
- err = h.SetArch(conf.Architecture)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
-
|
|
|
- h.SetNoUpgrades(conf.NoUpgrade...)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
-
|
|
|
- h.SetNoExtracts(conf.NoExtract...)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
-
|
|
|
- err = h.SetDefaultSigLevel(conf.SigLevel)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
-
|
|
|
- err = h.SetLocalFileSigLevel(conf.LocalFileSigLevel)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
-
|
|
|
- err = h.SetRemoteFileSigLevel(conf.RemoteFileSigLevel)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
-
|
|
|
- err = h.SetDeltaRatio(conf.UseDelta)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
-
|
|
|
- err = h.SetUseSyslog(conf.Options&ConfUseSyslog > 0)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
-
|
|
|
- err = h.SetCheckSpace(conf.Options&ConfCheckSpace > 0)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
-
|
|
|
- return h, nil
|
|
|
-}
|