Arch Linux helpers and parsers
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

273 lines
5.9 KiB

package archlinux
import (
"encoding/json"
"errors"
"io"
"net/url"
"strings"
"time"
"maze.io/archlinux.v0/tarball"
"maze.io/archlinux.v0/version"
)
// Errors
var (
ErrPackageArchiveNameInvalid = errors.New("archlinux: invalid package archive name")
ErrPackageNameInvalid = errors.New("archlinux: invalid package name")
ErrPackageNoChecksums = errors.New("archlinux: package has no checksums")
ErrNoPackageName = errors.New("archlinux: name is missing")
ErrNoPackageVersion = errors.New("archlinux: version is missing")
ErrNoPackageRelease = errors.New("archlinux: release is missing")
)
// Package describes an Arch Linux package
type Package struct {
// Filename is the name of the underlying archive or PKGBUILD
Filename string
// Name of the package
Name string
// Base of the package, if this package is a member of a split package
Base string
// Version number
Version string
// Release number
Release string
// Epoch
Epoch int
// Description of the package
Description string
// Arch slice of supported architectures
Arch []string
// URL of the project
URL *url.URL
// License one or more licenses
License []string
// Groups are package groups
Groups []string
// Depends are the packages this package depends on
Depends []string
// MakeDepends are the packages this package depends on for building
MakeDepends []string
// CheckDepends are the packages this package depends on for running the
// checks during build
CheckDepends []string
// OptionalDepends are the packages this package recommends having
// installed. Note that these values may contain comments, indicated by
// "spec: comment" (the separator is ": ")
OptionalDepends []string
// Provides are virtual package names provided by this package
Provides []string
// Conflicts are packages that conflict with this package
Conflicts []string
// Replaces are packages that will be replaced if this package is installed
Replaces []string
// Backup are the files that will be backed up to .pacsave if they exist on
// the file system if this package is installed
Backup []string
// MD5 is the MD-5 hex digest of the package file
MD5 []byte
// SHA1 is the SHA-1 hex digest of the package file
SHA1 []byte
// SHA256 is the SHA-256 hex digest of the package file
SHA256 []byte
// SHA384 is the SHA-384 hex digest of the package file
SHA384 []byte
// SHA512 is the SHA-512 hex digest of the package file
SHA512 []byte
// PGPSignature is the deteched PGP signature base64 encoded digest of the
// package file
PGPSignature []byte
// Size of the installed package
Size int64
// CompressedSize of the archive
CompressedSize int64
// Options are the makepkg build options used for this package
Options []string
// BuildEnv are the options during the build
BuildEnv []string
// BuildDate is the date the package was built
BuildDate time.Time
// Packager name and email
Packager string
// Maintainers for this package
Maintainers []string
// Contributors to this package
Contributors []string
// Files is a list of files provided by the package
Files []string
}
const unknown = "unknown"
func (pkg Package) String() string {
var s = make([]string, 3)
if pkg.Name != "" {
s[0] = pkg.Name
} else {
s[0] = unknown
}
if pkg.Version != "" {
s[1] = pkg.Version
} else {
s[1] = unknown
}
if pkg.Release != "" {
s[2] = pkg.Release
} else {
s[2] = unknown
}
return strings.Join(s, "-")
}
func (pkg *Package) MarshalJSON() ([]byte, error) {
type Alias Package
return json.Marshal(&struct {
BuildDate int64
URL string
*Alias
}{
BuildDate: pkg.BuildDate.Unix(),
URL: pkg.URL.String(),
Alias: (*Alias)(pkg),
})
}
func (pkg *Package) UnmarshalJSON(data []byte) error {
type Alias Package
var (
aux = &struct {
BuildDate int64
URL string
*Alias
}{
Alias: (*Alias)(pkg),
}
err error
)
if err = json.Unmarshal(data, &aux); err != nil {
return err
}
if pkg.URL, err = url.Parse(aux.URL); err != nil {
return err
}
pkg.BuildDate = time.Unix(aux.BuildDate, 0)
return nil
}
// ReadPackage reads a package description from a package tarball.
func ReadPackage(r io.Reader) (*Package, error) {
t, err := tarball.NewReader(r)
if err != nil {
return nil, err
}
pkg := new(Package)
if n, ok := r.(namer); ok {
pkg.Filename = n.Name()
}
// Scan the contents of the tarball
for {
h, err := t.Next()
if err == io.EOF {
return pkg, nil
} else if err != nil {
return nil, err
}
switch strings.TrimLeft(h.Name, "/") {
case ".BUILDINFO":
if err = pkg.readBuildInfo(t); err != nil {
return nil, err
}
case ".PKGINFO":
if err = pkg.readPackageInfo(t); err != nil {
return nil, err
}
default:
pkg.Files = append(pkg.Files, strings.TrimLeft(h.Name, "/"))
}
}
}
// ParsePackageArchiveName parsea an Arch Linux package archive name
func ParsePackageArchiveName(arc string) (name string, v version.Version, arch, ext string, err error) {
i := strings.Index(arc, ".pkg.tar.")
if i == -1 {
err = ErrPackageArchiveNameInvalid
return
}
name, ext = arc[:i], arc[i:]
if i = strings.LastIndexByte(name, '-'); i == -1 {
// No arch
err = ErrPackageArchiveNameInvalid
return
}
name, arch = name[:i], name[i+1:]
if i = strings.LastIndexByte(name, '-'); i == -1 {
// No release
err = ErrPackageArchiveNameInvalid
return
}
if i = strings.LastIndexByte(name[:i], '-'); i == -1 {
// No version
err = ErrPackageArchiveNameInvalid
return
}
name, v = name[:i], version.Parse(name[i+1:])
return
}
// ParsePackageName parses an Arch Linux package name to its name and version
func ParsePackageName(s string) (name string, v version.Version, err error) {
i := strings.LastIndexByte(s, '-')
if i == -1 {
// No release
err = ErrPackageNameInvalid
return
}
if i = strings.LastIndexByte(s[:i], '-'); i == -1 {
// No version
err = ErrPackageArchiveNameInvalid
return
}
name, v = s[:i], version.Parse(s[i+1:])
return
}