@ -2,21 +2,33 @@ package archlinux
import (
"bufio"
"bytes"
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"hash"
"io"
"os"
"strconv"
"strings"
"time"
"maze.io/archlinux.v0/tarball"
"maze.io/archlinux.v0/version"
)
// Errors
var (
ErrNoPackageName = errors . New ( "archlinux: name is missing" )
ErrNoPackageVersion = errors . New ( "archlinux: version is missing" )
ErrNoPackageRelease = errors . New ( "archlinux: release is missing" )
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" )
)
// Namer is an interface for instances that have a name, such as *os.File
@ -204,6 +216,27 @@ func ReadPackageInfo(r io.Reader) (*Package, error) {
return pkg , nil
}
// ReadPackageDesc reads a package description from a desc file
func ReadPackageDesc ( r io . Reader ) ( * Package , error ) {
pkg := new ( Package )
if n , ok := r . ( Namer ) ; ok {
pkg . Filename = n . Name ( )
}
if err := pkg . readPackageDesc ( 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 ( ) {
@ -301,3 +334,289 @@ func (pkg *Package) readPackageInfo(r io.Reader) (err error) {
}
return
}
// WritePackageInfo writes a PKGINFO to the supplied Writer
func ( pkg * Package ) WritePackageInfo ( w io . Writer ) error {
var (
fields = [ ] struct {
Key string
Value interface { }
} {
{ "pkgname" , pkg . Name } ,
{ "pkgbase" , pkg . Base } ,
{ "pkgver" , pkg . Version + "-" + pkg . Release } ,
{ "pkgdesc" , pkg . Description } ,
{ "url" , pkg . URL } ,
{ "builddate" , pkg . BuildDate } ,
{ "packager" , pkg . Packager } ,
{ "size" , pkg . Size } ,
{ "arch" , pkg . Arch } ,
{ "license" , pkg . License } ,
{ "provides" , pkg . Provides } ,
{ "conflicts" , pkg . Conflicts } ,
{ "replaces" , pkg . Replaces } ,
{ "backup" , pkg . Backup } ,
{ "depend" , pkg . Depends } ,
{ "optdepend" , pkg . OptionalDepends } ,
{ "makedepend" , pkg . MakeDepends } ,
{ "checkdepend" , pkg . CheckDepends } ,
}
err error
)
if _ , err = fmt . Fprintln ( w , "# Generated by maze.io/archlinux.v0\n#" , time . Now ( ) . UTC ( ) . Format ( time . UnixDate ) ) ; err != nil {
return err
}
for _ , field := range fields {
switch value := field . Value . ( type ) {
case string :
if value == "" {
continue
}
if _ , err = fmt . Fprintln ( w , field . Key , "=" , value ) ; err != nil {
return err
}
case [ ] string :
if len ( value ) > 0 {
for _ , v := range value {
if v == "" {
continue
}
if _ , err = fmt . Fprintln ( w , field . Key , "=" , v ) ; err != nil {
return err
}
}
}
case int64 :
if value > 0 {
if _ , err = fmt . Fprintln ( w , field . Key , "=" , value ) ; err != nil {
return err
}
}
case * URL :
if value == nil {
continue
}
if _ , err = fmt . Fprintln ( w , field . Key , "=" , value . String ( ) ) ; err != nil {
return err
}
}
}
return nil
}
func ( pkg * Package ) readPackageDesc ( r io . Reader ) ( err error ) {
var (
s = bufio . NewScanner ( r )
line string
lines [ ] string
section string
)
for s . Scan ( ) {
if err = s . Err ( ) ; err != nil {
if err == io . EOF {
err = nil
}
break
}
line = s . Text ( )
if len ( line ) == 0 {
switch section {
case "FILENAME" :
pkg . Filename = lines [ 0 ]
case "NAME" :
pkg . Name = lines [ 0 ]
case "VERSION" :
i := strings . LastIndexByte ( lines [ 0 ] , '-' )
if i == - 1 {
return fmt . Errorf ( "archlinux: invalid version %q" , lines [ 0 ] )
}
pkg . Version , pkg . Release = lines [ 0 ] [ : i ] , lines [ 0 ] [ i + 1 : ]
case "DESC" :
pkg . Description = strings . TrimSpace ( strings . Join ( lines , "\n" ) )
case "ARCH" :
pkg . Arch = make ( [ ] string , len ( lines ) )
copy ( pkg . Arch , lines )
case "LICENSE" :
pkg . License = make ( [ ] string , len ( lines ) )
copy ( pkg . License , lines )
case "GROUPS" :
pkg . Groups = make ( [ ] string , len ( lines ) )
copy ( pkg . Groups , lines )
case "CSIZE" :
if pkg . CompressedSize , err = strconv . ParseInt ( lines [ 0 ] , 10 , 64 ) ; err != nil {
return
}
case "ISIZE" :
if pkg . Size , err = strconv . ParseInt ( lines [ 0 ] , 10 , 64 ) ; err != nil {
return
}
case "MD5SUM" :
if pkg . MD5 , err = hex . DecodeString ( lines [ 0 ] ) ; err != nil {
return
}
case "SHA1SUM" :
if pkg . SHA1 , err = hex . DecodeString ( lines [ 0 ] ) ; err != nil {
return
}
case "SHA256SUM" :
if pkg . SHA256 , err = hex . DecodeString ( lines [ 0 ] ) ; err != nil {
return
}
case "SHA384SUM" :
if pkg . SHA384 , err = hex . DecodeString ( lines [ 0 ] ) ; err != nil {
return
}
case "SHA512SUM" :
if pkg . SHA512 , err = hex . DecodeString ( lines [ 0 ] ) ; err != nil {
return
}
case "PGPSIG" :
if pkg . PGPSignature , err = base64 . StdEncoding . DecodeString ( lines [ 0 ] ) ; err != nil {
return
}
case "URL" :
if pkg . URL , err = ParseURL ( lines [ 0 ] ) ; err != nil {
return
}
case "DEPENDS" :
pkg . Depends = make ( [ ] string , len ( lines ) )
copy ( pkg . Depends , lines )
case "OPTDEPENDS" :
pkg . OptionalDepends = make ( [ ] string , len ( lines ) )
copy ( pkg . OptionalDepends , lines )
case "MAKEDEPENDS" :
pkg . MakeDepends = make ( [ ] string , len ( lines ) )
copy ( pkg . MakeDepends , lines )
case "CHECKDEPENDS" :
pkg . CheckDepends = make ( [ ] string , len ( lines ) )
copy ( pkg . CheckDepends , lines )
case "PROVIDES" :
pkg . Provides = make ( [ ] string , len ( lines ) )
copy ( pkg . Provides , lines )
case "CONFLICTS" :
pkg . Conflicts = make ( [ ] string , len ( lines ) )
copy ( pkg . Conflicts , lines )
case "REPLACES" :
pkg . Replaces = make ( [ ] string , len ( lines ) )
copy ( pkg . Replaces , lines )
case "BUILDDATE" :
var i int64
if i , err = strconv . ParseInt ( lines [ 0 ] , 10 , 64 ) ; err != nil {
return
}
pkg . BuildDate = Timestamp ( time . Unix ( i , 0 ) )
case "PACKAGER" :
pkg . Packager = lines [ 0 ]
default :
return fmt . Errorf ( "archlinux: unsupported desc section %q" , section )
}
} else if line [ 0 ] == '%' && line [ len ( line ) - 1 ] == '%' {
section = line [ 1 : len ( line ) - 1 ]
lines = lines [ : 0 ]
} else {
lines = append ( lines , line )
}
}
return
}
type hasher struct {
hash . Hash
Name string
Sum [ ] byte
//*sync.WaitGroup
//In chan []byte
}
func newHasher ( name string , sum [ ] byte , h hash . Hash ) hasher {
n := hasher {
Hash : h ,
Name : name ,
Sum : sum ,
//WaitGroup: new(sync.WaitGroup),
//In: make(chan []byte, 16),
}
/ *
n . Add ( 1 )
go n . Run ( )
* /
return n
}
/ *
func ( h * hasher ) Run ( ) {
defer h . Done ( )
for {
select {
case b := <- h . In :
if b == nil {
return
}
if _ , err := h . Hash . Write ( b ) ; err != nil {
panic ( err )
}
}
}
}
* /
// Verify verifies the package file checksums
func ( pkg * Package ) Verify ( filename string ) error {
f , err := os . Open ( filename )
if err != nil {
return err
}
defer f . Close ( )
var hashers [ ] hasher
addHasher := func ( name string , sum [ ] byte , fn func ( ) hash . Hash ) {
if len ( sum ) == 0 {
return
}
hashers = append ( hashers , newHasher ( name , sum , fn ( ) ) )
}
addHasher ( "MD5" , pkg . MD5 , md5 . New )
addHasher ( "SHA1" , pkg . SHA1 , sha1 . New )
addHasher ( "SHA256" , pkg . SHA256 , sha256 . New )
addHasher ( "SHA384" , pkg . SHA384 , sha512 . New384 )
addHasher ( "SHA512" , pkg . SHA512 , sha512 . New )
if len ( hashers ) == 0 {
return ErrPackageNoChecksums
}
var block [ 4096 ] byte
for {
n , err := f . Read ( block [ : ] )
if err != nil {
if err == io . EOF {
break
}
}
for _ , h := range hashers {
//h.In <- block[:n]
if _ , err = h . Write ( block [ : n ] ) ; err != nil {
return err
}
}
}
for _ , h := range hashers {
// h.Wait()
if s := h . Hash . Sum ( nil ) ; ! bytes . Equal ( s , h . Sum ) {
return fmt . Errorf ( "archlinux: %s checksum mismatch, got %x, expected %x" , h . Name , s , h . Sum )
}
}
return nil
}
func ParsePackageName ( s string ) ( name string , v version . Version , err error ) {
i := strings . LastIndexByte ( s , '-' )
if i == - 1 {
err = ErrPackageNameInvalid
return
}
name , v = s [ : i ] , version . Parse ( s [ i + 1 : ] )
return
}