- Added a little bit of formatting to the help subcommand

- Removed unnecessary version.go file
- Moved utils to their own go package
- Moved test packages under the to the test_packages directory
- Added the bpm-utils test package which can be used to make your own bpm packages
This commit is contained in:
CapCreeperGR 2024-03-26 17:12:29 +02:00
parent 88d4b94225
commit b67f357996
17 changed files with 209 additions and 224 deletions

Binary file not shown.

Binary file not shown.

View File

@ -1,4 +1,4 @@
package main package bpm_utils
import "syscall" import "syscall"
@ -20,6 +20,25 @@ func getKernel() string {
return byteArrayToString(u.Sysname[:]) + " " + byteArrayToString(u.Release[:]) return byteArrayToString(u.Sysname[:]) + " " + byteArrayToString(u.Release[:])
} }
func stringSliceRemove(s []string, r string) []string {
for i, v := range s {
if v == r {
return append(s[:i], s[i+1:]...)
}
}
return s
}
func stringSliceRemoveEmpty(s []string) []string {
var r []string
for _, str := range s {
if str != "" {
r = append(r, str)
}
}
return r
}
func byteArrayToString(bs []int8) string { func byteArrayToString(bs []int8) string {
b := make([]byte, len(bs)) b := make([]byte, len(bs))
for i, v := range bs { for i, v := range bs {

View File

@ -1,4 +1,4 @@
package main package bpm_utils
import ( import (
"archive/tar" "archive/tar"
@ -14,16 +14,16 @@ import (
"strings" "strings"
) )
type packageInfo struct { type PackageInfo struct {
name string Name string
description string Description string
version string Version string
pkgType string Type string
depends []string Depends []string
provides []string Provides []string
} }
func readPackage(filename string) (*packageInfo, error) { func ReadPackage(filename string) (*PackageInfo, error) {
if _, err := os.Stat(filename); os.IsNotExist(err) { if _, err := os.Stat(filename); os.IsNotExist(err) {
return nil, err return nil, err
} }
@ -50,7 +50,7 @@ func readPackage(filename string) (*packageInfo, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
pkgInfo, err := readPackageInfo(string(bs)) pkgInfo, err := ReadPackageInfo(string(bs))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -60,8 +60,8 @@ func readPackage(filename string) (*packageInfo, error) {
return nil, errors.New("pkg.info not found in archive") return nil, errors.New("pkg.info not found in archive")
} }
func readPackageInfo(contents string) (*packageInfo, error) { func ReadPackageInfo(contents string) (*PackageInfo, error) {
pkgInfo := packageInfo{} pkgInfo := PackageInfo{}
lines := strings.Split(contents, "\n") lines := strings.Split(contents, "\n")
for num, line := range lines { for num, line := range lines {
if len(strings.TrimSpace(line)) == 0 { if len(strings.TrimSpace(line)) == 0 {
@ -75,28 +75,40 @@ func readPackageInfo(contents string) (*packageInfo, error) {
split[1] = strings.Trim(split[1], " ") split[1] = strings.Trim(split[1], " ")
switch split[0] { switch split[0] {
case "name": case "name":
pkgInfo.name = split[1] pkgInfo.Name = split[1]
case "description": case "description":
pkgInfo.description = split[1] pkgInfo.Description = split[1]
case "version": case "version":
pkgInfo.version = split[1] pkgInfo.Version = split[1]
case "type": case "type":
pkgInfo.pkgType = split[1] pkgInfo.Type = split[1]
case "depends":
pkgInfo.Depends = strings.Split(strings.Replace(split[1], " ", "", -1), ",")
pkgInfo.Depends = stringSliceRemoveEmpty(pkgInfo.Depends)
case "provides":
pkgInfo.Provides = strings.Split(strings.Replace(split[1], " ", "", -1), ",")
pkgInfo.Provides = stringSliceRemoveEmpty(pkgInfo.Depends)
} }
} }
return &pkgInfo, nil return &pkgInfo, nil
} }
func createInfoFile(pkgInfo packageInfo) string { func CreateInfoFile(pkgInfo PackageInfo) string {
ret := "" ret := ""
ret = ret + "name: " + pkgInfo.name + "\n" ret = ret + "name: " + pkgInfo.Name + "\n"
ret = ret + "description: " + pkgInfo.description + "\n" ret = ret + "description: " + pkgInfo.Description + "\n"
ret = ret + "version: " + pkgInfo.version + "\n" ret = ret + "version: " + pkgInfo.Version + "\n"
ret = ret + "type: " + pkgInfo.pkgType + "\n" ret = ret + "type: " + pkgInfo.Type + "\n"
if len(pkgInfo.Depends) > 0 {
ret = ret + "depends (" + strconv.Itoa(len(pkgInfo.Depends)) + "): " + strings.Join(pkgInfo.Depends, ",") + "\n"
}
if len(pkgInfo.Provides) > 0 {
ret = ret + "provides (" + strconv.Itoa(len(pkgInfo.Provides)) + "): " + strings.Join(pkgInfo.Provides, ",") + "\n"
}
return ret return ret
} }
func installPackage(filename, installDir string) error { func InstallPackage(filename, installDir string, force bool) error {
if _, err := os.Stat(filename); os.IsNotExist(err) { if _, err := os.Stat(filename); os.IsNotExist(err) {
return err return err
} }
@ -110,7 +122,15 @@ func installPackage(filename, installDir string) error {
} }
tr := tar.NewReader(archive) tr := tar.NewReader(archive)
var files []string var files []string
var pkgInfo *packageInfo pkgInfo, err := ReadPackage(filename)
if err != nil {
return err
}
if !force {
if unresolved := CheckDependencies(pkgInfo, installDir); len(unresolved) != 0 {
return errors.New("Could not resolve all dependencies. Missing " + strings.Join(unresolved, ", "))
}
}
for { for {
header, err := tr.Next() header, err := tr.Next()
if err == io.EOF { if err == io.EOF {
@ -119,16 +139,6 @@ func installPackage(filename, installDir string) error {
if err != nil { if err != nil {
return err return err
} }
if header.Name == "pkg.info" {
bs, _ := io.ReadAll(tr)
if err != nil {
return err
}
pkgInfo, err = readPackageInfo(string(bs))
if err != nil {
return err
}
}
if strings.HasPrefix(header.Name, "files/") && header.Name != "files/" { if strings.HasPrefix(header.Name, "files/") && header.Name != "files/" {
extractFilename := path.Join(installDir, strings.TrimPrefix(header.Name, "files/")) extractFilename := path.Join(installDir, strings.TrimPrefix(header.Name, "files/"))
switch header.Typeflag { switch header.Typeflag {
@ -164,12 +174,12 @@ func installPackage(filename, installDir string) error {
slices.Sort(files) slices.Sort(files)
slices.Reverse(files) slices.Reverse(files)
dataDir := path.Join(installDir, "var/lib/bpm/installed/") installedDir := path.Join(installDir, "var/lib/bpm/installed/")
err = os.MkdirAll(dataDir, 755) err = os.MkdirAll(installedDir, 755)
if err != nil { if err != nil {
return err return err
} }
pkgDir := path.Join(dataDir, pkgInfo.name) pkgDir := path.Join(installedDir, pkgInfo.Name)
err = os.RemoveAll(pkgDir) err = os.RemoveAll(pkgDir)
if err != nil { if err != nil {
return err return err
@ -189,35 +199,70 @@ func installPackage(filename, installDir string) error {
return err return err
} }
} }
f.Close() err = f.Close()
if err != nil {
return err
}
f, err = os.Create(path.Join(pkgDir, "info")) f, err = os.Create(path.Join(pkgDir, "info"))
if err != nil { if err != nil {
return err return err
} }
_, err = f.WriteString(createInfoFile(*pkgInfo)) _, err = f.WriteString(CreateInfoFile(*pkgInfo))
if err != nil {
return err
}
err = f.Close()
if err != nil { if err != nil {
return err return err
} }
f.Close()
archive.Close() err = archive.Close()
file.Close() if err != nil {
return err
}
err = file.Close()
if err != nil {
return err
}
return nil return nil
} }
func isPackageInstalled(pkg string) bool { func CheckDependencies(pkgInfo *PackageInfo, rootDir string) []string {
dataDir := path.Join(rootDir, "var/lib/bpm/installed/") unresolved := make([]string, len(pkgInfo.Depends))
pkgDir := path.Join(dataDir, pkg) copy(unresolved, pkgInfo.Depends)
installedDir := path.Join(rootDir, "var/lib/bpm/installed/")
if _, err := os.Stat(installedDir); err != nil {
return nil
}
items, err := os.ReadDir(installedDir)
if err != nil {
return nil
}
for _, item := range items {
if !item.IsDir() {
continue
}
if slices.Contains(unresolved, item.Name()) {
unresolved = stringSliceRemove(unresolved, item.Name())
}
}
return unresolved
}
func IsPackageInstalled(pkg, rootDir string) bool {
installedDir := path.Join(rootDir, "var/lib/bpm/installed/")
pkgDir := path.Join(installedDir, pkg)
if _, err := os.Stat(pkgDir); err != nil { if _, err := os.Stat(pkgDir); err != nil {
return false return false
} }
return true return true
} }
func getInstalledPackages() ([]string, error) { func GetInstalledPackages(rootDir string) ([]string, error) {
dataDir := path.Join(rootDir, "var/lib/bpm/installed/") installedDir := path.Join(rootDir, "var/lib/bpm/installed/")
items, err := os.ReadDir(dataDir) items, err := os.ReadDir(installedDir)
if os.IsNotExist(err) { if os.IsNotExist(err) {
return nil, nil return nil, nil
} }
@ -231,12 +276,12 @@ func getInstalledPackages() ([]string, error) {
return ret, nil return ret, nil
} }
func getPackageFiles(pkg string) []string { func GetPackageFiles(pkg, rootDir string) []string {
var ret []string var ret []string
dataDir := path.Join(rootDir, "var/lib/bpm/installed/") installedDir := path.Join(rootDir, "var/lib/bpm/installed/")
pkgDir := path.Join(dataDir, pkg) pkgDir := path.Join(installedDir, pkg)
files := path.Join(pkgDir, "files") files := path.Join(pkgDir, "files")
if _, err := os.Stat(dataDir); os.IsNotExist(err) { if _, err := os.Stat(installedDir); os.IsNotExist(err) {
return nil return nil
} }
if _, err := os.Stat(pkgDir); os.IsNotExist(err) { if _, err := os.Stat(pkgDir); os.IsNotExist(err) {
@ -253,11 +298,11 @@ func getPackageFiles(pkg string) []string {
return ret return ret
} }
func getPackageInfo(pkg string) *packageInfo { func GetPackageInfo(pkg, rootDir string) *PackageInfo {
dataDir := path.Join(rootDir, "var/lib/bpm/installed/") installedDir := path.Join(rootDir, "var/lib/bpm/installed/")
pkgDir := path.Join(dataDir, pkg) pkgDir := path.Join(installedDir, pkg)
files := path.Join(pkgDir, "info") files := path.Join(pkgDir, "info")
if _, err := os.Stat(dataDir); os.IsNotExist(err) { if _, err := os.Stat(installedDir); os.IsNotExist(err) {
return nil return nil
} }
if _, err := os.Stat(pkgDir); os.IsNotExist(err) { if _, err := os.Stat(pkgDir); os.IsNotExist(err) {
@ -271,17 +316,17 @@ func getPackageInfo(pkg string) *packageInfo {
if err != nil { if err != nil {
return nil return nil
} }
info, err := readPackageInfo(string(bs)) info, err := ReadPackageInfo(string(bs))
if err != nil { if err != nil {
return nil return nil
} }
return info return info
} }
func removePackage(pkg string) error { func RemovePackage(pkg, rootDir string) error {
dataDir := path.Join(rootDir, "var/lib/bpm/installed/") installedDir := path.Join(rootDir, "var/lib/bpm/installed/")
pkgDir := path.Join(dataDir, pkg) pkgDir := path.Join(installedDir, pkg)
files := getPackageFiles(pkg) files := GetPackageFiles(pkg, rootDir)
for _, file := range files { for _, file := range files {
file = path.Join(rootDir, file) file = path.Join(rootDir, file)
stat, err := os.Stat(file) stat, err := os.Stat(file)

2
go.mod
View File

@ -1,3 +1,3 @@
module bpm module capcreepergr.me/bpm
go 1.22 go 1.22

91
main.go
View File

@ -2,6 +2,7 @@ package main
import ( import (
"bufio" "bufio"
"capcreepergr.me/bpm/bpm_utils"
"fmt" "fmt"
"log" "log"
"os" "os"
@ -14,11 +15,14 @@ import (
/* A simple-to-use package manager */ /* A simple-to-use package manager */
/* ---------------------------------- */ /* ---------------------------------- */
var bpmVer = "0.0.3" var bpmVer = "0.0.5"
var rootDir string = "/" var rootDir = "/"
func main() { func main() {
//fmt.Printf("Running %s %s\n", getKernel(), getArch()) if os.Getuid() != 0 {
fmt.Println("BPM needs to be run with superuser permissions")
os.Exit(0)
}
resolveCommand() resolveCommand()
} }
@ -76,19 +80,19 @@ func resolveCommand() {
return return
} }
for n, pkg := range packages { for n, pkg := range packages {
info := getPackageInfo(pkg) info := bpm_utils.GetPackageInfo(pkg, rootDir)
if info == nil { if info == nil {
fmt.Printf("Package (%s) could not be found\n", pkg) fmt.Printf("Package (%s) could not be found\n", pkg)
continue continue
} }
fmt.Print("----------------\n" + createInfoFile(*info)) fmt.Print("----------------\n" + bpm_utils.CreateInfoFile(*info))
if n == len(packages)-1 { if n == len(packages)-1 {
fmt.Println("----------------") fmt.Println("----------------")
} }
} }
case list: case list:
resolveFlags() resolveFlags()
packages, err := getInstalledPackages() packages, err := bpm_utils.GetInstalledPackages(rootDir)
if err != nil { if err != nil {
log.Fatalf("Could not get installed packages\nError: %s", err.Error()) log.Fatalf("Could not get installed packages\nError: %s", err.Error())
return return
@ -98,12 +102,12 @@ func resolveCommand() {
return return
} }
for n, pkg := range packages { for n, pkg := range packages {
info := getPackageInfo(pkg) info := bpm_utils.GetPackageInfo(pkg, rootDir)
if info == nil { if info == nil {
fmt.Printf("Package (%s) could not be found\n", pkg) fmt.Printf("Package (%s) could not be found\n", pkg)
continue continue
} }
fmt.Print("----------------\n" + createInfoFile(*info)) fmt.Print("----------------\n" + bpm_utils.CreateInfoFile(*info))
if n == len(packages)-1 { if n == len(packages)-1 {
fmt.Println("----------------") fmt.Println("----------------")
} }
@ -116,33 +120,38 @@ func resolveCommand() {
return return
} }
for _, file := range files { for _, file := range files {
pkgInfo, err := readPackage(file) pkgInfo, err := bpm_utils.ReadPackage(file)
if err != nil { if err != nil {
log.Fatalf("Could not read package\nError: %s\n", err) log.Fatalf("Could not read package\nError: %s\n", err)
} }
fmt.Print("----------------\n" + createInfoFile(*pkgInfo)) fmt.Print("----------------\n" + bpm_utils.CreateInfoFile(*pkgInfo))
fmt.Println("----------------") fmt.Println("----------------")
if isPackageInstalled(pkgInfo.name) { if !slices.Contains(flags, "f") {
if unresolved := bpm_utils.CheckDependencies(pkgInfo, rootDir); len(unresolved) != 0 {
log.Fatalf("Cannot install package (%s) due to missing dependencies: %s\n", pkgInfo.Name, strings.Join(unresolved, ", "))
}
}
if bpm_utils.IsPackageInstalled(pkgInfo.Name, rootDir) {
if !slices.Contains(flags, "y") { if !slices.Contains(flags, "y") {
installedInfo := getPackageInfo(pkgInfo.name) installedInfo := bpm_utils.GetPackageInfo(pkgInfo.Name, rootDir)
if strings.Compare(pkgInfo.version, installedInfo.version) > 0 { if strings.Compare(pkgInfo.Version, installedInfo.Version) > 0 {
fmt.Println("This file contains a newer version of this package (" + installedInfo.version + " -> " + pkgInfo.version + ")") fmt.Println("This file contains a newer version of this package (" + installedInfo.Version + " -> " + pkgInfo.Version + ")")
fmt.Print("Do you wish to update this package? [y\\N] ") fmt.Print("Do you wish to update this package? [y\\N] ")
} else if strings.Compare(pkgInfo.version, installedInfo.version) < 0 { } else if strings.Compare(pkgInfo.Version, installedInfo.Version) < 0 {
fmt.Println("This file contains an older version of this package (" + installedInfo.version + " -> " + pkgInfo.version + ")") fmt.Println("This file contains an older version of this package (" + installedInfo.Version + " -> " + pkgInfo.Version + ")")
fmt.Print("Do you wish to downgrade this package? (Not recommended) [y\\N] ") fmt.Print("Do you wish to downgrade this package? (Not recommended) [y\\N] ")
} else if strings.Compare(pkgInfo.version, installedInfo.version) == 0 { } else if strings.Compare(pkgInfo.Version, installedInfo.Version) == 0 {
fmt.Println("This package is already installed on the system and is up to date") fmt.Println("This package is already installed on the system and is up to date")
fmt.Print("Do you wish to reinstall this package? [y\\N] ") fmt.Print("Do you wish to reinstall this package? [y\\N] ")
} }
reader := bufio.NewReader(os.Stdin) reader := bufio.NewReader(os.Stdin)
text, _ := reader.ReadString('\n') text, _ := reader.ReadString('\n')
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" { if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
fmt.Printf("Skipping package (%s)...\n", pkgInfo.name) fmt.Printf("Skipping package (%s)...\n", pkgInfo.Name)
continue continue
} }
} }
err := removePackage(pkgInfo.name) err := bpm_utils.RemovePackage(pkgInfo.Name, rootDir)
if err != nil { if err != nil {
log.Fatalf("Could not remove current version of the package\nError: %s\n", err) log.Fatalf("Could not remove current version of the package\nError: %s\n", err)
} }
@ -151,16 +160,16 @@ func resolveCommand() {
reader := bufio.NewReader(os.Stdin) reader := bufio.NewReader(os.Stdin)
text, _ := reader.ReadString('\n') text, _ := reader.ReadString('\n')
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" { if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
fmt.Printf("Skipping package (%s)...\n", pkgInfo.name) fmt.Printf("Skipping package (%s)...\n", pkgInfo.Name)
continue continue
} }
} }
err = installPackage(file, rootDir) err = bpm_utils.InstallPackage(file, rootDir, slices.Contains(flags, "f"))
if err != nil { if err != nil {
log.Fatalf("Could not install package\nError: %s\n", err) log.Fatalf("Could not install package\nError: %s\n", err)
} }
fmt.Printf("Package (%s) was successfully installed!\n", pkgInfo.name) fmt.Printf("Package (%s) was successfully installed!\n", pkgInfo.Name)
} }
case remove: case remove:
flags, i := resolveFlags() flags, i := resolveFlags()
@ -170,37 +179,41 @@ func resolveCommand() {
return return
} }
for _, pkg := range packages { for _, pkg := range packages {
pkgInfo := getPackageInfo(pkg) pkgInfo := bpm_utils.GetPackageInfo(pkg, rootDir)
if pkgInfo == nil { if pkgInfo == nil {
fmt.Printf("Package (%s) could not be found\n", pkg) fmt.Printf("Package (%s) could not be found\n", pkg)
continue continue
} }
fmt.Print("----------------\n" + createInfoFile(*pkgInfo)) fmt.Print("----------------\n" + bpm_utils.CreateInfoFile(*pkgInfo))
fmt.Println("----------------") fmt.Println("----------------")
if !slices.Contains(flags, "y") { if !slices.Contains(flags, "y") {
reader := bufio.NewReader(os.Stdin) reader := bufio.NewReader(os.Stdin)
fmt.Print("Do you wish to remove this package? [y\\N] ") fmt.Print("Do you wish to remove this package? [y\\N] ")
text, _ := reader.ReadString('\n') text, _ := reader.ReadString('\n')
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" { if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
fmt.Printf("Skipping package (%s)...\n", pkgInfo.name) fmt.Printf("Skipping package (%s)...\n", pkgInfo.Name)
continue continue
} }
} }
err := removePackage(pkg) err := bpm_utils.RemovePackage(pkg, rootDir)
if err != nil { if err != nil {
log.Fatalf("Could not remove package\nError: %s\n", err) log.Fatalf("Could not remove package\nError: %s\n", err)
} }
fmt.Printf("Package (%s) was successfully removed!\n", pkgInfo.name) fmt.Printf("Package (%s) was successfully removed!\n", pkgInfo.Name)
} }
default: default:
fmt.Println("------Help------") fmt.Println("\033[1m------Help------\033[0m")
fmt.Println("bpm version | shows information on the installed version of bpm") fmt.Println("\033[1m\\ Command Format /\033[0m")
fmt.Println("bpm info | shows information on an installed package") fmt.Println("-> command format: bpm <subcommand> [-flags]...")
fmt.Println("bpm list [-e] | lists all installed packages. -e lists only explicitly installed packages") fmt.Println("-> flags will be read if passed right after the subcommand otherwise they will be read as subcommand arguments")
fmt.Println("bpm install [-y] <files...> | installs the following files. -y skips the confirmation prompt") fmt.Println("\033[1m\\ Command List /\033[0m")
fmt.Println("bpm remove [-y] <packages...> | removes the following packages. -y skips the confirmation prompt") fmt.Println("-> bpm version | shows information on the installed version of bpm")
fmt.Println("bpm cleanup | removes all unneeded dependencies") fmt.Println("-> bpm info | shows information on an installed package")
fmt.Println("----------------") fmt.Println("-> bpm list | lists all installed packages")
fmt.Println("-> bpm install [-y, -f] <files...> | installs the following files. -y skips the confirmation prompt. -f skips dependency resolution")
fmt.Println("-> bpm remove [-y] <packages...> | removes the following packages. -y skips the confirmation prompt")
fmt.Println("-> bpm cleanup | removes all unneeded dependencies")
fmt.Println("\033[1m----------------\033[0m")
} }
} }
@ -213,14 +226,8 @@ func resolveFlags() ([]string, int) {
switch getCommandType() { switch getCommandType() {
default: default:
log.Fatalf("Invalid flag " + flag) log.Fatalf("Invalid flag " + flag)
case list:
v := [...]string{"e"}
if !slices.Contains(v[:], f) {
log.Fatalf("Invalid flag " + flag)
}
ret = append(ret, f)
case install: case install:
v := [...]string{"y"} v := [...]string{"y", "f"}
if !slices.Contains(v[:], f) { if !slices.Contains(v[:], f) {
log.Fatalf("Invalid flag " + flag) log.Fatalf("Invalid flag " + flag)
} }

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,28 @@
#!/bin/bash
if [ $# -eq 0 ]
then
echo "No output package name given!"
exit 1
fi
output=$1
echo "Creating package with the name $output..."
if [ -d files ]; then
echo "files/ directory found"
else
echo "files/ directory not found in $PWD"
exit 1
fi
if [ -f pkg.info ]; then
echo "pkg.info file found"
else
echo "pkg.info file not found in $PWD"
exit 1
fi
echo "Creating $output.bpm package..."
tar -czf $output.bpm files/ pkg.info

View File

@ -0,0 +1,4 @@
name: bpm-utils
description: Utilities to create BPM packages
version: 1.0.0
type: binary

BIN
test_packages/bpm/bpm.bpm Normal file

Binary file not shown.

Binary file not shown.

View File

@ -1,4 +1,4 @@
name: bpm name: bpm
description: The Bubble Package Manager description: The Bubble Package Manager
version: 0.0.3 version: 0.0.5
type: binary type: binary

Binary file not shown.

View File

@ -1,4 +1,4 @@
name: hello name: hello
description: A simple hello world program description: A simple hello world program
version: 1.0-1 version: 1.0
type: binary type: binary

View File

@ -1,118 +0,0 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
// Package cmpver implements a variant of debian version number
// comparison.
//
// A version is a string consisting of alternating non-numeric and
// numeric fields. When comparing two versions, each one is broken
// down into its respective fields, and the fields are compared
// pairwise. The comparison is lexicographic for non-numeric fields,
// numeric for numeric fields. The first non-equal field pair
// determines the ordering of the two versions.
//
// This comparison scheme is a simplified version of Debian's version
// number comparisons. Debian differs in a few details of
// lexicographical field comparison, where certain characters have
// special meaning and ordering. We don't need that, because Tailscale
// version numbers don't need it.
package main
import (
"fmt"
"strconv"
"strings"
)
// Less reports whether v1 is less than v2.
//
// Note that "12" is less than "12.0".
func Less(v1, v2 string) bool {
return Compare(v1, v2) < 0
}
// LessEq reports whether v1 is less than or equal to v2.
//
// Note that "12" is less than "12.0".
func LessEq(v1, v2 string) bool {
return Compare(v1, v2) <= 0
}
func isnum(r rune) bool {
return r >= '0' && r <= '9'
}
func notnum(r rune) bool {
return !isnum(r)
}
// Compare returns an integer comparing two strings as version numbers.
// The result will be -1, 0, or 1 representing the sign of v1 - v2:
//
// Compare(v1, v2) < 0 if v1 < v2
// == 0 if v1 == v2
// > 0 if v1 > v2
func Compare(v1, v2 string) int {
var (
f1, f2 string
n1, n2 uint64
err error
)
for v1 != "" || v2 != "" {
// Compare the non-numeric character run lexicographically.
f1, v1 = splitPrefixFunc(v1, notnum)
f2, v2 = splitPrefixFunc(v2, notnum)
if res := strings.Compare(f1, f2); res != 0 {
return res
}
// Compare the numeric character run numerically.
f1, v1 = splitPrefixFunc(v1, isnum)
f2, v2 = splitPrefixFunc(v2, isnum)
// ParseUint refuses to parse empty strings, which would only
// happen if we reached end-of-string. We follow the Debian
// convention that empty strings mean zero, because
// empirically that produces reasonable-feeling comparison
// behavior.
n1 = 0
if f1 != "" {
n1, err = strconv.ParseUint(f1, 10, 64)
if err != nil {
panic(fmt.Sprintf("all-number string %q didn't parse as string: %s", f1, err))
}
}
n2 = 0
if f2 != "" {
n2, err = strconv.ParseUint(f2, 10, 64)
if err != nil {
panic(fmt.Sprintf("all-number string %q didn't parse as string: %s", f2, err))
}
}
switch {
case n1 == n2:
case n1 < n2:
return -1
case n1 > n2:
return 1
}
}
// Only way to reach here is if v1 and v2 run out of fields
// simultaneously - i.e. exactly equal versions.
return 0
}
// splitPrefixFunc splits s at the first rune where f(rune) is false.
func splitPrefixFunc(s string, f func(rune) bool) (string, string) {
for i, r := range s {
if !f(r) {
return s[:i], s[i:]
}
}
return s, s[:0]
}