mirror of
https://github.com/Jguer/yay.git
synced 2024-11-07 17:47:21 +01:00
311 lines
7.3 KiB
Go
311 lines
7.3 KiB
Go
|
package srcinfo
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"io/ioutil"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// parser is used to track our current state as we parse the srcinfo.
|
||
|
type parser struct {
|
||
|
// srcinfo is a Pointer to the Srcinfo we are currently building.
|
||
|
srcinfo *Srcinfo
|
||
|
|
||
|
// seenPkgnames is a set of pkgnames we have seen
|
||
|
seenPkgnames map[string]struct{}
|
||
|
}
|
||
|
|
||
|
func (psr *parser) currentPackage() (*Package, error) {
|
||
|
if psr.srcinfo.Pkgbase == "" {
|
||
|
return nil, fmt.Errorf("Not in pkgbase or pkgname")
|
||
|
} else if len(psr.srcinfo.Packages) == 0 {
|
||
|
return &psr.srcinfo.Package, nil
|
||
|
} else {
|
||
|
return &psr.srcinfo.Packages[len(psr.srcinfo.Packages) - 1], nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (psr *parser) setHeaderOrField(key, value string) error {
|
||
|
pkgbase := &psr.srcinfo.PackageBase
|
||
|
|
||
|
switch key {
|
||
|
case "pkgbase":
|
||
|
if psr.srcinfo.Pkgbase != "" {
|
||
|
return fmt.Errorf("key \"%s\" can not occur after pkgbase or pkgname", key)
|
||
|
}
|
||
|
|
||
|
pkgbase.Pkgbase = value
|
||
|
return nil
|
||
|
case "pkgname":
|
||
|
if psr.srcinfo.Pkgbase == "" {
|
||
|
return fmt.Errorf("key \"%s\" can not occur before pkgbase", key)
|
||
|
}
|
||
|
if _, ok := psr.seenPkgnames[value]; ok {
|
||
|
return fmt.Errorf("pkgname \"%s\" can not occur more than once", value)
|
||
|
}
|
||
|
psr.seenPkgnames[value] = struct{}{}
|
||
|
|
||
|
psr.srcinfo.Packages = append(psr.srcinfo.Packages, Package{Pkgname: value})
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
if psr.srcinfo.Pkgbase == "" {
|
||
|
return fmt.Errorf("key \"%s\" can not occur before pkgbase or pkgname", key)
|
||
|
}
|
||
|
|
||
|
return psr.setField(key, value)
|
||
|
}
|
||
|
|
||
|
func (psr *parser) setField(archKey, value string) error {
|
||
|
pkg, err := psr.currentPackage()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
pkgbase := &psr.srcinfo.PackageBase
|
||
|
key, arch := splitArchFromKey(archKey)
|
||
|
err = checkArch(psr.srcinfo.Arch, archKey, arch)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if value == "" {
|
||
|
value = EmptyOverride
|
||
|
}
|
||
|
|
||
|
// pkgbase only + not arch dependent
|
||
|
found := true
|
||
|
switch archKey {
|
||
|
case "pkgver":
|
||
|
pkgbase.Pkgver = value
|
||
|
case "pkgrel":
|
||
|
pkgbase.Pkgrel = value
|
||
|
case "epoch":
|
||
|
pkgbase.Epoch = value
|
||
|
case "validpgpkeys":
|
||
|
pkgbase.ValidPGPKeys = append(pkgbase.ValidPGPKeys, value)
|
||
|
case "noextract":
|
||
|
pkgbase.NoExtract = append(pkgbase.NoExtract, value)
|
||
|
default:
|
||
|
found = false
|
||
|
}
|
||
|
|
||
|
if found {
|
||
|
if len(psr.srcinfo.Packages) > 0 {
|
||
|
return fmt.Errorf("key \"%s\" can not occur after pkgname", archKey)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// pkgbase only + arch dependent
|
||
|
found = true
|
||
|
switch key {
|
||
|
case "source":
|
||
|
pkgbase.Source = append(pkgbase.Source, ArchString{arch, value})
|
||
|
case "md5sums":
|
||
|
pkgbase.MD5Sums = append(pkgbase.MD5Sums, ArchString{arch, value})
|
||
|
case "sha1sums":
|
||
|
pkgbase.SHA1Sums = append(pkgbase.SHA1Sums, ArchString{arch, value})
|
||
|
case "sha224sums":
|
||
|
pkgbase.SHA224Sums = append(pkgbase.SHA224Sums, ArchString{arch, value})
|
||
|
case "sha256sums":
|
||
|
pkgbase.SHA256Sums = append(pkgbase.SHA256Sums, ArchString{arch, value})
|
||
|
case "sha384sums":
|
||
|
pkgbase.SHA384Sums = append(pkgbase.SHA384Sums, ArchString{arch, value})
|
||
|
case "sha512sums":
|
||
|
pkgbase.SHA512Sums = append(pkgbase.SHA512Sums, ArchString{arch, value})
|
||
|
case "makedepends":
|
||
|
pkgbase.MakeDepends = append(pkgbase.MakeDepends, ArchString{arch, value})
|
||
|
case "checkdepends":
|
||
|
pkgbase.CheckDepends = append(pkgbase.CheckDepends, ArchString{arch, value})
|
||
|
default:
|
||
|
found = false
|
||
|
}
|
||
|
|
||
|
if found {
|
||
|
if len(psr.srcinfo.Packages) > 0 {
|
||
|
return fmt.Errorf("key \"%s\" can not occur after pkgname", archKey)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// pkgbase or pkgname + not arch dependent
|
||
|
found = true
|
||
|
switch archKey {
|
||
|
case "pkgdesc":
|
||
|
pkg.Pkgdesc = value
|
||
|
case "url":
|
||
|
pkg.URL = value
|
||
|
case "license":
|
||
|
pkg.License = append(pkg.License, value)
|
||
|
case "install":
|
||
|
pkg.Install = value
|
||
|
case "changelog":
|
||
|
pkg.Changelog = value
|
||
|
case "groups":
|
||
|
pkg.Groups = append(pkg.Groups, value)
|
||
|
case "arch":
|
||
|
pkg.Arch = append(pkg.Arch, value)
|
||
|
case "backup":
|
||
|
pkg.Backup = append(pkg.Backup, value)
|
||
|
case "options":
|
||
|
pkg.Options = append(pkg.Options, value)
|
||
|
default:
|
||
|
found = false
|
||
|
}
|
||
|
|
||
|
if found {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// pkgbase or pkgname + arch dependent
|
||
|
switch key {
|
||
|
case "depends":
|
||
|
pkg.Depends = append(pkg.Depends, ArchString{arch, value})
|
||
|
case "optdepends":
|
||
|
pkg.OptDepends = append(pkg.OptDepends, ArchString{arch, value})
|
||
|
case "conflicts":
|
||
|
pkg.Conflicts = append(pkg.Conflicts, ArchString{arch, value})
|
||
|
case "provides":
|
||
|
pkg.Provides = append(pkg.Provides, ArchString{arch, value})
|
||
|
case "replaces":
|
||
|
pkg.Replaces = append(pkg.Replaces, ArchString{arch, value})
|
||
|
default:
|
||
|
return fmt.Errorf("Unknown key: \"%s\"", archKey)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func parse(data string) (*Srcinfo, error) {
|
||
|
psr := &parser{
|
||
|
&Srcinfo{},
|
||
|
make(map[string]struct{}),
|
||
|
}
|
||
|
|
||
|
lines := strings.Split(data, "\n")
|
||
|
|
||
|
for n, line := range lines {
|
||
|
line = strings.TrimSpace(line)
|
||
|
|
||
|
if line == "" || strings.HasPrefix(line, "#") {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
key, value, err := splitPair(line)
|
||
|
if err != nil {
|
||
|
return nil, Error(n+1, line, err.Error())
|
||
|
}
|
||
|
|
||
|
err = psr.setHeaderOrField(key, value)
|
||
|
if err != nil {
|
||
|
return nil, Error(n+1, line, err.Error())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if psr.srcinfo.Pkgbase == "" {
|
||
|
return nil, fmt.Errorf("No pkgbase field")
|
||
|
}
|
||
|
|
||
|
if len(psr.srcinfo.Packages) == 0 {
|
||
|
return nil, fmt.Errorf("No pkgname field")
|
||
|
}
|
||
|
|
||
|
if psr.srcinfo.Pkgver == "" {
|
||
|
return nil, fmt.Errorf("No pkgver field")
|
||
|
}
|
||
|
|
||
|
if psr.srcinfo.Pkgrel == "" {
|
||
|
return nil, fmt.Errorf("No pkgrel field")
|
||
|
}
|
||
|
|
||
|
if len(psr.srcinfo.Arch) == 0 {
|
||
|
return nil, fmt.Errorf("No arch field")
|
||
|
}
|
||
|
|
||
|
return psr.srcinfo, nil
|
||
|
}
|
||
|
|
||
|
// splitPair splits a key value string in the form of "key = value",
|
||
|
// whitespace being ignored. The key and the value is returned.
|
||
|
func splitPair(line string) (string, string, error) {
|
||
|
split := strings.SplitN(line, "=", 2)
|
||
|
|
||
|
if len(split) != 2 {
|
||
|
return "", "", fmt.Errorf("Line does not contain =")
|
||
|
}
|
||
|
|
||
|
key := strings.TrimSpace(split[0])
|
||
|
value := strings.TrimSpace(split[1])
|
||
|
|
||
|
if key == "" {
|
||
|
return "", "", fmt.Errorf("Key is empty")
|
||
|
}
|
||
|
|
||
|
return key, value, nil
|
||
|
}
|
||
|
|
||
|
// splitArchFromKey splits up architecture dependent field names, separating
|
||
|
// the field name from the architecture they depend on.
|
||
|
func splitArchFromKey(key string) (string, string) {
|
||
|
split := strings.SplitN(key, "_", 2)
|
||
|
if len(split) == 2 {
|
||
|
return split[0], split[1]
|
||
|
}
|
||
|
|
||
|
return split[0], ""
|
||
|
}
|
||
|
|
||
|
// checkArg checks that the arch from an arch dependent string is actually
|
||
|
// defined inside of the srcinfo and speicifly disallows the arch "any" as it
|
||
|
// is not a real arch
|
||
|
func checkArch(arches []string, key string, arch string) error {
|
||
|
if arch == "" {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
if arch == "any" {
|
||
|
return fmt.Errorf("Invalid key \"%s\" arch \"%s\" is not allowed", key, arch)
|
||
|
}
|
||
|
|
||
|
for _, a := range arches {
|
||
|
if a == arch {
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return fmt.Errorf("Invalid key \"%s\" unsupported arch \"%s\"", key, arch)
|
||
|
}
|
||
|
|
||
|
// ParseFile parses a srcinfo file as specified by path.
|
||
|
func ParseFile(path string) (*Srcinfo, error) {
|
||
|
file, err := ioutil.ReadFile(path)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("Unable to read file: %s: %s", path, err.Error())
|
||
|
}
|
||
|
|
||
|
return Parse(string(file))
|
||
|
}
|
||
|
|
||
|
// Parse parses a srcinfo in string form. Parsing will fail if:
|
||
|
// A srcinfo does not contain all required fields
|
||
|
// The same pkgname is specified more then once
|
||
|
// arch is missing
|
||
|
// pkgver is mising
|
||
|
// pkgrel is missing
|
||
|
// An architecture specific field is defined for an architecture that does not exist
|
||
|
// An unknown key is specified
|
||
|
// An empty value is specified
|
||
|
//
|
||
|
// Required fields are:
|
||
|
// pkgbase
|
||
|
// pkname
|
||
|
// arch
|
||
|
// pkgrel
|
||
|
// pkgver
|
||
|
func Parse(data string) (*Srcinfo, error) {
|
||
|
return parse(data)
|
||
|
}
|