Files
ipsw/internal/utils/semver.go

251 lines
4.3 KiB
Go

package utils
import (
"fmt"
"sort"
"strconv"
"strings"
)
type parsed struct {
major string
minor string
patch string
short string
micro string
}
func DiffVersion(v, w string) (string, error) {
pv, ok1 := parse(v)
pw, ok2 := parse(w)
if !ok1 && !ok2 {
return "", fmt.Errorf("invalid versions: %q, %q", v, w)
}
if !ok1 {
return "", fmt.Errorf("invalid version: %q", v)
}
if !ok2 {
return "", fmt.Errorf("invalid version: %q", w)
}
pvm, err := strconv.Atoi(pv.major)
if err != nil {
return "", err
}
pwm, err := strconv.Atoi(pw.major)
if err != nil {
return "", err
}
pmdelta := max(pvm-pwm, 0)
pvn, err := strconv.Atoi(pv.minor)
if err != nil {
return "", err
}
pwn, err := strconv.Atoi(pw.minor)
if err != nil {
return "", err
}
pndelta := max(pvn-pwn, 0)
pvp, err := strconv.Atoi(pv.patch)
if err != nil {
return "", err
}
pwp, err := strconv.Atoi(pw.patch)
if err != nil {
return "", err
}
ppdelta := max(pvp-pwp, 0)
pvs, err := strconv.Atoi(pv.short)
if err != nil {
return "", err
}
pws, err := strconv.Atoi(pw.short)
if err != nil {
return "", err
}
psdelta := max(pvs-pws, 0)
pvc, err := strconv.Atoi(pv.micro)
if err != nil {
return "", err
}
pwc, err := strconv.Atoi(pw.micro)
if err != nil {
return "", err
}
pcdelta := max(pvc-pwc, 0)
return fmt.Sprintf("%d.%d.%d.%d.%d", pmdelta, pndelta, ppdelta, psdelta, pcdelta), nil
}
// Compare returns an integer comparing two versions according to
// semantic version precedence.
// The result will be 0 if v == w, -1 if v < w, or +1 if v > w.
//
// An invalid semantic version string is considered less than a valid one.
// All invalid semantic version strings compare equal to each other.
func Compare(v, w string) int {
pv, ok1 := parse(v)
pw, ok2 := parse(w)
if !ok1 && !ok2 {
return 0
}
if !ok1 {
return -1
}
if !ok2 {
return +1
}
if c := compareInt(pv.major, pw.major); c != 0 {
return c
}
if c := compareInt(pv.minor, pw.minor); c != 0 {
return c
}
if c := compareInt(pv.patch, pw.patch); c != 0 {
return c
}
if c := compareInt(pv.short, pw.short); c != 0 {
return c
}
if c := compareInt(pv.micro, pw.micro); c != 0 {
return c
}
return 0
}
// ByVersion implements sort.Interface for sorting semantic version strings.
type ByVersion []string
func (vs ByVersion) Len() int { return len(vs) }
func (vs ByVersion) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] }
func (vs ByVersion) Less(i, j int) bool {
cmp := Compare(vs[i], vs[j])
if cmp != 0 {
return cmp < 0
}
return vs[i] < vs[j]
}
// SortVersions sorts a list of semantic version strings using ByVersion.
func SortVersions(list []string) {
sort.Sort(ByVersion(list))
}
type MachoVersion struct {
Name string
Version string
}
type ByMachoVersion []MachoVersion
func (vs ByMachoVersion) Len() int { return len(vs) }
func (vs ByMachoVersion) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] }
func (vs ByMachoVersion) Less(i, j int) bool {
cmp := Compare(vs[i].Version, vs[j].Version)
if cmp != 0 {
return cmp > 0
}
return strings.Compare(vs[i].Name, vs[j].Name) < 0
}
func SortMachoVersions(list []MachoVersion) {
sort.Sort(ByMachoVersion(list))
}
func parse(v string) (p parsed, ok bool) {
p.major, v, ok = parseInt(v)
if !ok {
return
}
if v == "" {
p.minor = "0"
p.patch = "0"
p.short = ".0.0"
return
}
if v[0] != '.' {
ok = false
return
}
p.minor, v, ok = parseInt(v[1:])
if !ok {
return
}
if v == "" {
p.patch = "0"
p.short = ".0"
return
}
if v[0] != '.' {
ok = false
return
}
p.patch, v, ok = parseInt(v[1:])
if !ok {
return
}
if v == "" {
p.patch = "0"
p.short = ".0"
return
}
if v[0] != '.' {
ok = false
return
}
p.short, v, ok = parseInt(v[1:])
if !ok {
return
}
if v == "" {
p.short = ".0"
return
}
if v[0] != '.' {
ok = false
return
}
p.micro, v, ok = parseInt(v[1:])
if !ok {
return
}
if v != "" {
ok = false
return
}
ok = true
return
}
func parseInt(v string) (t, rest string, ok bool) {
if v == "" {
return
}
if v[0] < '0' || '9' < v[0] {
return
}
i := 1
for i < len(v) && '0' <= v[i] && v[i] <= '9' {
i++
}
if v[0] == '0' && i != 1 {
return
}
return v[:i], v[i:], true
}
func compareInt(x, y string) int {
if x == y {
return 0
}
if len(x) < len(y) {
return -1
}
if len(x) > len(y) {
return +1
}
if x < y {
return -1
} else {
return +1
}
}