2021-07-26 05:31:22 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2021-07-29 03:47:05 +00:00
|
|
|
"crypto/tls"
|
2021-07-26 05:31:22 +00:00
|
|
|
"fmt"
|
2021-08-17 05:20:12 +00:00
|
|
|
"io/ioutil"
|
2021-07-29 03:47:05 +00:00
|
|
|
"net"
|
|
|
|
"os"
|
2021-08-02 21:37:57 +00:00
|
|
|
"os/user"
|
|
|
|
"path/filepath"
|
2021-07-30 23:03:25 +00:00
|
|
|
"strconv"
|
2021-07-29 03:47:05 +00:00
|
|
|
"strings"
|
2021-08-02 21:55:59 +00:00
|
|
|
"syscall"
|
2021-07-26 05:31:22 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
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 {
|
2021-08-12 21:20:02 +00:00
|
|
|
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 21:20:02 +00:00
|
|
|
}
|
|
|
|
|
2021-08-12 22:53:43 +00:00
|
|
|
func Merge(left, right []expression, ind int) []expression {
|
2021-08-12 21:20:02 +00:00
|
|
|
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++
|
2021-08-13 03:21:44 +00:00
|
|
|
continue
|
2021-08-12 22:53:43 +00:00
|
|
|
} else if j > len(right)-1 && i <= len(left)-1 {
|
|
|
|
slice[k] = left[i]
|
|
|
|
i++
|
2021-08-13 03:21:44 +00:00
|
|
|
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++
|
2021-08-13 03:21:44 +00:00
|
|
|
continue
|
2021-08-12 22:53:43 +00:00
|
|
|
} else if j > len(right)-1 && i <= len(left)-1 {
|
|
|
|
slice[k] = left[i]
|
|
|
|
i++
|
2021-08-13 03:21:44 +00:00
|
|
|
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++
|
|
|
|
}
|
2021-08-12 21:20:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return slice
|
|
|
|
}
|
|
|
|
|
2021-07-26 05:31:22 +00:00
|
|
|
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
|
|
|
|
}
|
2021-07-29 03:47:05 +00:00
|
|
|
|
|
|
|
func escapeString(s string) string {
|
2021-07-31 20:25:20 +00:00
|
|
|
var out strings.Builder
|
|
|
|
for _, c := range []rune(s) {
|
2021-07-29 03:47:05 +00:00
|
|
|
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)
|
|
|
|
}
|
2021-07-31 20:25:20 +00:00
|
|
|
}
|
|
|
|
return out.String()
|
2021-07-29 03:47:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func unescapeString(s string) string {
|
2021-07-31 20:25:20 +00:00
|
|
|
var out strings.Builder
|
2021-07-30 23:03:25 +00:00
|
|
|
|
|
|
|
escapeNumBase := 10
|
|
|
|
|
|
|
|
var altNum bool
|
|
|
|
var otherNum strings.Builder
|
|
|
|
|
2021-07-31 20:25:20 +00:00
|
|
|
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')
|
2021-07-30 23:03:25 +00:00
|
|
|
case '0':
|
|
|
|
escapeNumBase = 8
|
|
|
|
altNum = true
|
|
|
|
continue
|
2021-07-31 20:25:20 +00:00
|
|
|
case '1', '3', '4', '2', '5', '6', '7', '8', '9':
|
2021-07-30 23:03:25 +00:00
|
|
|
altNum = true
|
|
|
|
otherNum.WriteRune(c)
|
|
|
|
continue
|
2021-07-31 20:25:20 +00:00
|
|
|
default:
|
|
|
|
out.WriteRune(c)
|
|
|
|
}
|
|
|
|
slash = false
|
2021-07-30 23:03:25 +00:00
|
|
|
} else if slash {
|
|
|
|
switch c {
|
2021-07-31 20:25:20 +00:00
|
|
|
case '0', '1', '3', '4', '2', '5', '6', '7', '8', '9':
|
2021-07-30 23:03:25 +00:00
|
|
|
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()
|
|
|
|
}
|
2021-07-30 23:07:12 +00:00
|
|
|
if c == '\\' {
|
|
|
|
slash = true
|
|
|
|
} else {
|
|
|
|
out.WriteRune(c)
|
|
|
|
}
|
2021-07-30 23:03:25 +00:00
|
|
|
}
|
2021-07-31 20:25:20 +00:00
|
|
|
} else if c == '\\' {
|
|
|
|
slash = true
|
|
|
|
} else {
|
|
|
|
out.WriteRune(c)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return out.String()
|
2021-07-29 03:47:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
2021-08-02 21:37:57 +00:00
|
|
|
if linerTerm != nil || initialTerm != nil {
|
2021-07-31 20:25:20 +00:00
|
|
|
line.Close()
|
|
|
|
}
|
2021-07-29 03:47:05 +00:00
|
|
|
os.Exit(code)
|
|
|
|
}
|
|
|
|
|
|
|
|
func StringSliceToExpressionSlice(s []string) expression {
|
|
|
|
e := make([]expression, len(s))
|
|
|
|
for i := range s {
|
|
|
|
e[i] = s[i]
|
|
|
|
}
|
|
|
|
return e
|
|
|
|
}
|
2021-08-02 21:37:57 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2021-08-02 21:37:57 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-08-16 05:45:05 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|