|
|
@ -1,60 +1,303 @@ |
|
|
|
package archlinux |
|
|
|
|
|
|
|
import ( |
|
|
|
"bufio" |
|
|
|
"errors" |
|
|
|
"fmt" |
|
|
|
"io" |
|
|
|
"net/url" |
|
|
|
"strconv" |
|
|
|
"strings" |
|
|
|
"time" |
|
|
|
|
|
|
|
"maze.io/archlinux.v0/tarball" |
|
|
|
) |
|
|
|
|
|
|
|
// Errors
|
|
|
|
var ( |
|
|
|
ErrNoPackageName = errors.New("archlinux: name is missing") |
|
|
|
ErrNoPackageVersion = errors.New("archlinux: version is missing") |
|
|
|
ErrNoPackageRelease = errors.New("archlinux: release is missing") |
|
|
|
) |
|
|
|
|
|
|
|
// Namer is an interface for instances that have a name, such as *os.File
|
|
|
|
type Namer interface { |
|
|
|
// Name returns the instance name
|
|
|
|
Name() string |
|
|
|
} |
|
|
|
|
|
|
|
// Package describes an Arch Linux package
|
|
|
|
type Package struct { |
|
|
|
Name string |
|
|
|
Base string |
|
|
|
Version string |
|
|
|
Release string |
|
|
|
Epoch int |
|
|
|
Description string |
|
|
|
Arch []string |
|
|
|
URL *url.URL `json:"-"` |
|
|
|
License []string |
|
|
|
Groups []string |
|
|
|
Depends []string |
|
|
|
MakeDepends []string |
|
|
|
CheckDepends []string |
|
|
|
// 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 |
|
|
|
|
|
|
|
// 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
|
|
|
|
OptionalDepends []string |
|
|
|
Provides []string |
|
|
|
Conflicts []string |
|
|
|
Replaces []string |
|
|
|
Backup []string |
|
|
|
Options []string |
|
|
|
MD5 []byte |
|
|
|
SHA1 []byte |
|
|
|
SHA256 []byte |
|
|
|
SHA384 []byte |
|
|
|
SHA512 []byte |
|
|
|
PGPSignature []byte |
|
|
|
|
|
|
|
// 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 Timestamp |
|
|
|
|
|
|
|
// Packager name and email
|
|
|
|
Packager string |
|
|
|
|
|
|
|
// Maintainers for this package
|
|
|
|
Maintainers []string |
|
|
|
|
|
|
|
// Contributors to this package
|
|
|
|
Contributors []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" |
|
|
|
s[0] = unknown |
|
|
|
} |
|
|
|
if pkg.Version != "" { |
|
|
|
s[1] = pkg.Version |
|
|
|
} else { |
|
|
|
s[1] = "unknown" |
|
|
|
s[1] = unknown |
|
|
|
} |
|
|
|
if pkg.Release != "" { |
|
|
|
s[2] = pkg.Release |
|
|
|
} else { |
|
|
|
s[2] = "unknown" |
|
|
|
s[2] = unknown |
|
|
|
} |
|
|
|
return strings.Join(s, "-") |
|
|
|
} |
|
|
|
|
|
|
|
// 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 |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// ReadPackageInfo reads a package description from a .PKGINFO file
|
|
|
|
func ReadPackageInfo(r io.Reader) (*Package, error) { |
|
|
|
pkg := new(Package) |
|
|
|
if n, ok := r.(Namer); ok { |
|
|
|
pkg.Filename = n.Name() |
|
|
|
} |
|
|
|
if err := pkg.readPackageInfo(r); err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
if pkg.Name == "" { |
|
|
|
return nil, ErrNoPackageName |
|
|
|
} |
|
|
|
if pkg.Version == "" { |
|
|
|
return nil, ErrNoPackageVersion |
|
|
|
} |
|
|
|
if pkg.Release == "" { |
|
|
|
return nil, ErrNoPackageRelease |
|
|
|
} |
|
|
|
return pkg, nil |
|
|
|
} |
|
|
|
|
|
|
|
func (pkg *Package) readBuildInfo(r io.Reader) (err error) { |
|
|
|
s := bufio.NewScanner(r) |
|
|
|
for s.Scan() { |
|
|
|
if err = s.Err(); err != nil { |
|
|
|
if err == io.EOF { |
|
|
|
err = nil |
|
|
|
} |
|
|
|
break |
|
|
|
} |
|
|
|
|
|
|
|
fields := strings.SplitN(s.Text(), " = ", 2) |
|
|
|
if len(fields) != 2 { |
|
|
|
continue |
|
|
|
} |
|
|
|
|
|
|
|
switch fields[0] { |
|
|
|
case "buildenv": |
|
|
|
pkg.BuildEnv = append(pkg.BuildEnv, fields[1]) |
|
|
|
case "options": |
|
|
|
pkg.Options = append(pkg.Options, fields[1]) |
|
|
|
} |
|
|
|
} |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
func (pkg *Package) readPackageInfo(r io.Reader) (err error) { |
|
|
|
s := bufio.NewScanner(r) |
|
|
|
for s.Scan() { |
|
|
|
if err = s.Err(); err != nil { |
|
|
|
if err == io.EOF { |
|
|
|
err = nil |
|
|
|
} |
|
|
|
break |
|
|
|
} |
|
|
|
|
|
|
|
fields := strings.SplitN(s.Text(), " = ", 2) |
|
|
|
if len(fields) != 2 { |
|
|
|
continue |
|
|
|
} |
|
|
|
|
|
|
|
switch fields[0] { |
|
|
|
case "pkgname": |
|
|
|
pkg.Name = fields[1] |
|
|
|
case "pkgbase": |
|
|
|
pkg.Base = fields[1] |
|
|
|
case "pkgver": |
|
|
|
if i := strings.IndexByte(fields[1], '-'); i != -1 { |
|
|
|
pkg.Version, pkg.Release = fields[1][:i], fields[1][i+1:] |
|
|
|
} else { |
|
|
|
return fmt.Errorf("archlinux: invalid version %q", fields[1]) |
|
|
|
} |
|
|
|
case "pkgdesc": |
|
|
|
pkg.Description = fields[1] |
|
|
|
case "url": |
|
|
|
if pkg.URL, err = ParseURL(fields[1]); err != nil { |
|
|
|
return fmt.Errorf("archlinux: invalid url %q: %v", fields[1], err) |
|
|
|
} |
|
|
|
case "builddate": |
|
|
|
var t int64 |
|
|
|
if t, err = strconv.ParseInt(fields[1], 10, 64); err != nil { |
|
|
|
return fmt.Errorf("archlinux: invalid build date %q: %v", fields[1], err) |
|
|
|
} |
|
|
|
pkg.BuildDate = Timestamp(time.Unix(t, 0)) |
|
|
|
case "packager": |
|
|
|
pkg.Packager = fields[1] |
|
|
|
case "size": |
|
|
|
if pkg.Size, err = strconv.ParseInt(fields[1], 10, 64); err != nil { |
|
|
|
return fmt.Errorf("archlinux: invalid size %q: %v", fields[1], err) |
|
|
|
} |
|
|
|
case "arch": |
|
|
|
pkg.Arch = append(pkg.Arch, fields[1]) |
|
|
|
case "group": |
|
|
|
pkg.Groups = append(pkg.Groups, fields[1]) |
|
|
|
case "license": |
|
|
|
pkg.License = append(pkg.License, fields[1]) |
|
|
|
case "backup": |
|
|
|
pkg.Backup = append(pkg.Backup, fields[1]) |
|
|
|
case "replaces": |
|
|
|
pkg.Replaces = append(pkg.Replaces, fields[1]) |
|
|
|
case "depend": |
|
|
|
pkg.Depends = append(pkg.Depends, fields[1]) |
|
|
|
case "conflict": |
|
|
|
pkg.Conflicts = append(pkg.Conflicts, fields[1]) |
|
|
|
case "provides": |
|
|
|
pkg.Provides = append(pkg.Provides, fields[1]) |
|
|
|
case "optdepend": |
|
|
|
pkg.OptionalDepends = append(pkg.OptionalDepends, fields[1]) |
|
|
|
case "makedepend": |
|
|
|
pkg.MakeDepends = append(pkg.MakeDepends, fields[1]) |
|
|
|
case "checkdepend": |
|
|
|
pkg.CheckDepends = append(pkg.CheckDepends, fields[1]) |
|
|
|
default: |
|
|
|
return fmt.Errorf("archlinux: unknown PKGINFO field %q", fields[0]) |
|
|
|
} |
|
|
|
} |
|
|
|
return |
|
|
|
} |