slope/helpers.go

394 lines
8.0 KiB
Go
Raw Normal View History

package main
import (
"crypto/tls"
"fmt"
2021-08-17 05:20:12 +00:00
"io/ioutil"
"net"
"os"
"os/user"
"path/filepath"
"strconv"
"strings"
2021-08-02 21:55:59 +00:00
"syscall"
)
func AnythingToBool(e expression) expression {
switch i := e.(type) {
case bool:
return i
default:
return true
}
}
2021-08-12 22:53:43 +00:00
func MergeSort(slice []expression, sublistIndex int) []expression {
if len(slice) < 2 {
return slice
}
mid := (len(slice)) / 2
2021-08-12 22:53:43 +00:00
return Merge(MergeSort(slice[:mid], sublistIndex), MergeSort(slice[mid:], sublistIndex), sublistIndex)
}
2021-08-12 22:53:43 +00:00
func Merge(left, right []expression, ind int) []expression {
size, i, j := len(left)+len(right), 0, 0
slice := make([]expression, size, size)
2021-08-12 22:53:43 +00:00
if ind >= 0 {
for k := 0; k < size; k++ {
if i > len(left)-1 && j <= len(right)-1 {
slice[k] = right[j]
j++
continue
2021-08-12 22:53:43 +00:00
} else if j > len(right)-1 && i <= len(left)-1 {
slice[k] = left[i]
i++
continue
}
i1, i1IsNumber := left[i].([]expression)[ind].(number)
i2, i2IsNumber := right[j].([]expression)[ind].(number)
if (i1IsNumber && i2IsNumber && i1 < i2) || (i1IsNumber && !i2IsNumber) {
slice[k] = left[i]
i++
} else if !i1IsNumber && i2IsNumber {
slice[k] = right[j]
j++
} else if (!i1IsNumber && !i2IsNumber) && String(left[i].([]expression)[ind], false) < String(right[j].([]expression)[ind], false) {
2021-08-12 22:53:43 +00:00
slice[k] = left[i]
i++
} else {
slice[k] = right[j]
j++
}
}
} else {
for k := 0; k < size; k++ {
if i > len(left)-1 && j <= len(right)-1 {
slice[k] = right[j]
j++
continue
2021-08-12 22:53:43 +00:00
} else if j > len(right)-1 && i <= len(left)-1 {
slice[k] = left[i]
i++
continue
}
i1, i1IsNumber := left[i].(number)
i2, i2IsNumber := right[j].(number)
if (i1IsNumber && i2IsNumber && float64(i1) < float64(i2)) || (!i1IsNumber && i2IsNumber) {
slice[k] = left[i]
i++
} else if i1IsNumber && !i2IsNumber {
slice[k] = right[j]
j++
} else if (!i1IsNumber && !i2IsNumber) && String(left[i], false) < String(right[j], false) {
2021-08-12 22:53:43 +00:00
slice[k] = left[i]
i++
} else {
slice[k] = right[j]
j++
}
}
}
return slice
}
func MergeLists(a ...expression) (expression, error) {
length := -1
for i := range a[0].([]expression) {
if _, ok := a[0].([]expression)[i].([]expression); !ok {
return 0, fmt.Errorf("a value other than a list was proivided")
}
if length == -1 {
length = len(a[0].([]expression)[i].([]expression))
} else if length != len(a[0].([]expression)[i].([]expression)) {
return 0, fmt.Errorf("lists are of unequal length")
}
}
mergedLists := make([]expression, 0, len(a[0].([]expression)[0].([]expression)))
for i, _ := range a[0].([]expression)[0].([]expression) {
lineList := make([]expression, 0, len(a[0].([]expression)))
for l, _ := range a[0].([]expression) {
lineList = append(lineList, a[0].([]expression)[l].([]expression)[i])
}
mergedLists = append(mergedLists, lineList)
}
return mergedLists, nil
}
func escapeString(s string) string {
var out strings.Builder
for _, c := range []rune(s) {
switch c {
case '\t':
out.WriteString("\\t")
case '\n':
out.WriteString("\\n")
case '\r':
out.WriteString("\\r")
case '\v':
out.WriteString("\\v")
case '\a':
out.WriteString("\\a")
case '\b':
out.WriteString("\\b")
case '\f':
out.WriteString("\\f")
case '\\':
out.WriteString("\\\\")
default:
out.WriteRune(c)
}
}
return out.String()
}
func unescapeString(s string) string {
var out strings.Builder
escapeNumBase := 10
var altNum bool
var otherNum strings.Builder
var slash bool
for _, c := range []rune(s) {
if slash && !altNum {
switch c {
case 't':
out.WriteRune('\t')
case 'n':
out.WriteRune('\n')
case 'r':
out.WriteRune('\r')
case 'v':
out.WriteRune('\v')
case 'a':
out.WriteRune('\a')
case 'b':
out.WriteRune('\b')
case 'f':
out.WriteRune('\f')
case '0':
escapeNumBase = 8
altNum = true
continue
case '1', '3', '4', '2', '5', '6', '7', '8', '9':
altNum = true
otherNum.WriteRune(c)
continue
default:
out.WriteRune(c)
}
slash = false
} else if slash {
switch c {
case '0', '1', '3', '4', '2', '5', '6', '7', '8', '9':
otherNum.WriteRune(c)
case 'x':
if otherNum.String() == "" {
escapeNumBase = 16
continue
}
fallthrough
default:
altNum = false
slash = false
if otherNum.Len() > 0 {
i, err := strconv.ParseInt(otherNum.String(), escapeNumBase, 0)
if err == nil {
out.WriteRune(rune(i))
}
otherNum.Reset()
}
if c == '\\' {
slash = true
} else {
out.WriteRune(c)
}
}
} else if c == '\\' {
slash = true
} else {
out.WriteRune(c)
}
}
return out.String()
}
func SafeExit(code int) {
for i := range openFiles {
if !openFiles[i].Open {
continue
}
switch o := openFiles[i].Obj.(type) {
case *os.File:
o.Close()
openFiles[i].Open = false
case *net.Conn:
(*o).Close()
openFiles[i].Open = false
case *tls.Conn:
o.Close()
openFiles[i].Open = false
}
}
if linerTerm != nil || initialTerm != nil {
line.Close()
}
os.Exit(code)
}
func StringSliceToExpressionSlice(s []string) expression {
e := make([]expression, len(s))
for i := range s {
e[i] = s[i]
}
return e
}
func loadFiles(files []expression) {
for _, v := range files {
err := RunFile(ExpandedAbsFilepath(v.(string)), true)
if err != nil {
panic(err.Error())
}
}
}
2021-08-17 05:20:12 +00:00
type Module struct {
text string
notes []string
dependencies []string
description string
source string
entry string
}
func ParseModFile(p string) (Module, error) {
b, err := ioutil.ReadFile(p)
if err != nil {
return Module{}, fmt.Errorf("Could not load modfile: %s", p)
}
s := string(b)
lines := strings.Split(s, "\n")
var mod Module
mod.text = s
for _, line := range lines {
fields := strings.SplitN(line, " ", 2)
if len(fields) != 2 {
continue
}
fields[1] = strings.TrimSpace(fields[1])
switch strings.ToLower(fields[0]) {
case "entry":
mod.entry = fields[1]
case "source":
mod.source = fields[1]
default:
continue
}
}
if mod.entry == "" && mod.source == "" {
return mod, fmt.Errorf("Modfile missing entry and source fields")
} else if mod.entry == "" {
return mod, fmt.Errorf("Modfile missing entry field")
} else if mod.source == "" {
return mod, fmt.Errorf("Modfile missing source field")
}
return mod, nil
}
func ExpandedAbsFilepath(p string) string {
if strings.HasPrefix(p, "~") {
if p == "~" || strings.HasPrefix(p, "~/") {
homedir, _ := os.UserHomeDir()
if len(p) <= 2 {
p = homedir
} else if len(p) > 2 {
p = filepath.Join(homedir, p[2:])
}
} else {
i := strings.IndexRune(p, '/')
var u string
var remainder string
if i < 0 {
u = p[1:]
remainder = ""
} else {
u = p[1:i]
remainder = p[i:]
}
usr, err := user.Lookup(u)
if err != nil {
p = filepath.Join("/home", u, remainder)
} else {
p = filepath.Join(usr.HomeDir, remainder)
}
}
} else if !strings.HasPrefix(p, "/") {
wd, _ := os.Getwd()
p = filepath.Join(wd, p)
}
path, _ := filepath.Abs(p)
return path
}
2021-08-02 21:55:59 +00:00
func handleSignals(c <-chan os.Signal) {
for {
switch <-c {
case syscall.SIGINT:
SafeExit(1)
}
}
}
func formatValue(format string, value expression) (string, error) {
v := String(value, false)
left := false
count := 0
if len(format) > 0 {
i, err := strconv.Atoi(format)
if err != nil {
return v, err
}
if i < 0 {
left = true
i = i*-1
}
count = i
}
if count <= len([]rune(v)) {
return v, nil
}
if left {
return v + strings.Repeat(" ", count - len(v)), nil
}
return strings.Repeat(" ", count - len(v)) + v, nil
}
2021-08-17 05:20:12 +00:00
func getModBaseDir() string {
p := os.Getenv("SLOPE_MOD_PATH")
if p == "" {
x := os.Getenv("XDG_DATA_HOME")
if x == "" {
return ExpandedAbsFilepath("~/.local/share/slope/modules/")
}
return filepath.Join(x, "slope", "modules")
}
return p
}
func createModBaseDir(p string) {
p = ExpandedAbsFilepath(p)
_, err := os.Stat(p)
if os.IsNotExist(err) {
os.MkdirAll(p, 0755)
}
}