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.
 

267 lines
5.6 KiB

package archlinux
import (
"bufio"
"encoding/base64"
"encoding/hex"
"fmt"
"io"
"net/url"
"reflect"
"strings"
"time"
)
// 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) {
values, encodings := parsePackageFields(parseFieldInfo, pkg)
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
}
v, ok := values[fields[0]]
if !ok {
return fmt.Errorf("archlinux: unknown PKGINFO field %q", fields[0])
}
if err = setPackageValue(pkg, v, fields[1], encodings[fields[0]]); err != nil {
return
}
}
return
}
func parseFieldInfo(field reflect.StructField) (name, encoding string) {
tag := field.Tag.Get("pkginfo")
switch tag {
case "-":
// Ignored tag
case "":
// No desc tag, default to upper case field name
name = strings.ToLower(field.Name)
default:
if i := strings.IndexByte(tag, ','); i != -1 {
name, encoding = tag[:i], tag[i+1:]
} else {
name = tag
}
}
return
}
// WritePackageInfo writes a PKGINFO to the supplied Writer
func (pkg *Package) WritePackageInfo(w io.Writer) (int, error) {
var (
value = reflect.Indirect(reflect.ValueOf(pkg))
typ = value.Type()
total, n int
err error
)
for i := 0; i < value.NumField(); i++ {
fieldValue := value.Field(i)
fieldType := typ.Field(i)
name, encoding := parseFieldInfo(fieldType)
if name == "" {
// No name means ignored field
continue
}
switch v := fieldValue.Interface().(type) {
case string:
if v == "" {
continue
}
switch encoding {
case "":
// No additional encoding
case evrEncoding:
// Epoch:Version-Release
if pkg.Epoch > 0 {
v = fmt.Sprintf("%d:%s-%s", pkg.Epoch, pkg.Version, pkg.Release)
} else {
v = fmt.Sprintf("%s-%s", pkg.Version, pkg.Release)
}
}
if n, err = fmt.Fprintf(w, "%s = %s\n", name, v); err != nil {
return total + n, err
}
total += n
case []string:
if len(v) == 0 {
continue
}
for _, s := range v {
if n, err = fmt.Fprintf(w, "%s = %s\n", name, s); err != nil {
return total + n, err
}
}
total += n
case []byte:
if len(v) == 0 {
continue
}
var o []byte
switch encoding {
case "":
o = v
case base64Encoding:
o = make([]byte, base64.StdEncoding.EncodedLen(len(v)))
base64.StdEncoding.Encode(o, v)
case hexEncoding:
o = make([]byte, hex.EncodedLen(len(v)))
hex.Encode(o, v)
}
if n, err = fmt.Fprintf(w, "%s = %s\n", name, o); err != nil {
return total + n, err
}
total += n
case int64:
if v == 0 {
continue
}
if n, err = fmt.Fprintf(w, "%s = %d\n", name, v); err != nil {
return total + n, err
}
total += n
case time.Time:
if v.Equal(timeZero) {
continue
}
if n, err = fmt.Fprintf(w, "%s = %d\n", name, v.Unix()); err != nil {
return total + n, err
}
total += n
case *url.URL:
if v == nil {
continue
}
if n, err = fmt.Fprintf(w, "%s = %s\n", name, v); err != nil {
return total + n, err
}
total += n
default:
return total, fmt.Errorf("archlinux: can't write %T to info", v)
}
}
return total, nil
/*
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 _, f := range fields {
switch value := f.Value.(type) {
case string:
if value == "" {
continue
}
if _, err = fmt.Fprintln(w, f.Key, "=", value); err != nil {
return err
}
case []string:
if len(value) > 0 {
for _, v := range value {
if v == "" {
continue
}
if _, err = fmt.Fprintln(w, f.Key, "=", v); err != nil {
return err
}
}
}
case int64:
if value > 0 {
if _, err = fmt.Fprintln(w, f.Key, "=", value); err != nil {
return err
}
}
case *url.URL:
if value == nil {
continue
}
if _, err = fmt.Fprintln(w, f.Key, "=", value.String()); err != nil {
return err
}
}
}
return nil
*/
}