lf/misc.go

307 lines
6.4 KiB
Go
Raw Normal View History

2016-08-13 12:49:04 +00:00
package main
import (
2022-02-13 13:58:53 +00:00
"bufio"
2016-08-13 12:49:04 +00:00
"fmt"
2022-02-13 13:58:53 +00:00
"io"
"path/filepath"
"regexp"
2016-08-13 12:49:04 +00:00
"strconv"
"strings"
2016-08-13 12:49:04 +00:00
"unicode"
"github.com/mattn/go-runewidth"
2016-08-13 12:49:04 +00:00
)
func isRoot(name string) bool { return filepath.Dir(name) == name }
2016-08-13 12:49:04 +00:00
2020-06-11 01:11:40 +00:00
func replaceTilde(s string) string {
if strings.HasPrefix(s, "~") {
s = strings.Replace(s, "~", gUser.HomeDir, 1)
}
return s
}
func runeSliceWidth(rs []rune) int {
w := 0
for _, r := range rs {
2016-12-22 19:58:55 +00:00
w += runewidth.RuneWidth(r)
}
return w
}
2017-11-19 18:55:13 +00:00
func runeSliceWidthRange(rs []rune, beg, end int) []rune {
curr := 0
b := 0
for i, r := range rs {
2016-12-22 19:58:55 +00:00
w := runewidth.RuneWidth(r)
switch {
case curr == beg:
b = i
case curr < beg && curr+w > beg:
b = i + 1
case curr == end:
return rs[b:i]
case curr > end:
return rs[b : i-1]
}
curr += w
}
return nil
}
// This function is used to escape whitespaces and special characters with
// backlashes in a given string.
2016-12-02 21:28:25 +00:00
func escape(s string) string {
buf := make([]rune, 0, len(s))
for _, r := range s {
if unicode.IsSpace(r) || r == '\\' || r == ';' || r == '#' {
2016-12-02 21:28:25 +00:00
buf = append(buf, '\\')
}
buf = append(buf, r)
}
return string(buf)
}
// This function is used to remove backlashes that are used to escape
// whitespaces and special characters in a given string.
2016-12-02 21:28:25 +00:00
func unescape(s string) string {
esc := false
buf := make([]rune, 0, len(s))
for _, r := range s {
if esc {
if !unicode.IsSpace(r) && r != '\\' && r != ';' && r != '#' {
buf = append(buf, '\\')
2016-12-02 21:28:25 +00:00
}
buf = append(buf, r)
esc = false
continue
}
if r == '\\' {
esc = true
2016-12-02 21:28:25 +00:00
continue
}
esc = false
2016-12-02 21:28:25 +00:00
buf = append(buf, r)
}
if esc {
buf = append(buf, '\\')
}
2016-12-02 21:28:25 +00:00
return string(buf)
}
// This function splits the given string by whitespaces. It is aware of escaped
// whitespaces so that they are not split unintentionally.
2016-12-02 21:28:25 +00:00
func tokenize(s string) []string {
esc := false
var buf []rune
var toks []string
for _, r := range s {
if r == '\\' {
esc = true
buf = append(buf, r)
continue
}
if esc {
esc = false
buf = append(buf, r)
continue
}
if !unicode.IsSpace(r) {
buf = append(buf, r)
} else {
toks = append(toks, string(buf))
buf = nil
}
}
toks = append(toks, string(buf))
return toks
}
// This function splits the first word of a string delimited by whitespace from
// the rest. This is used to tokenize a string one by one without touching the
// rest. Whitespace on the left side of both the word and the rest are trimmed.
func splitWord(s string) (word, rest string) {
s = strings.TrimLeftFunc(s, unicode.IsSpace)
ind := len(s)
for i, c := range s {
if unicode.IsSpace(c) {
ind = i
break
}
}
word = s[0:ind]
rest = strings.TrimLeftFunc(s[ind:], unicode.IsSpace)
return
}
// This function reads whitespace separated string pairs at each line. Single
// or double quotes can be used to escape whitespaces. Hash characters can be
2022-02-13 21:29:15 +00:00
// used to add a comment until the end of line. Leading and trailing space is
// trimmed. Empty lines are skipped.
2022-02-13 13:58:53 +00:00
func readPairs(r io.Reader) ([][]string, error) {
var pairs [][]string
s := bufio.NewScanner(r)
for s.Scan() {
line := s.Text()
squote, dquote := false, false
for i := 0; i < len(line); i++ {
if line[i] == '\'' && !dquote {
squote = !squote
} else if line[i] == '"' && !squote {
dquote = !dquote
}
if !squote && !dquote && line[i] == '#' {
line = line[:i]
break
}
}
line = strings.TrimSpace(line)
2022-02-13 13:58:53 +00:00
if line == "" {
continue
}
squote, dquote = false, false
2022-02-13 13:58:53 +00:00
pair := strings.FieldsFunc(line, func(r rune) bool {
if r == '\'' && !dquote {
squote = !squote
} else if r == '"' && !squote {
dquote = !dquote
}
return !squote && !dquote && unicode.IsSpace(r)
})
if len(pair) != 2 {
return nil, fmt.Errorf("expected pair but found: %s", s.Text())
2022-02-13 13:58:53 +00:00
}
for i := 0; i < len(pair); i++ {
squote, dquote = false, false
2022-02-13 13:58:53 +00:00
buf := make([]rune, 0, len(pair[i]))
for _, r := range pair[i] {
if r == '\'' && !dquote {
squote = !squote
continue
}
if r == '"' && !squote {
dquote = !dquote
continue
}
buf = append(buf, r)
}
pair[i] = string(buf)
}
pairs = append(pairs, pair)
}
return pairs, nil
}
2019-03-03 19:29:34 +00:00
// This function converts a size in bytes to a human readable form using metric
// suffixes (e.g. 1K = 1000). For values less than 10 the first significant
// digit is shown, otherwise it is hidden. Numbers are always rounded down.
// This should be fine for most human beings.
2016-08-13 12:49:04 +00:00
func humanize(size int64) string {
if size < 1000 {
return fmt.Sprintf("%dB", size)
2016-08-13 12:49:04 +00:00
}
suffix := []string{
"K", // kilo
"M", // mega
"G", // giga
"T", // tera
"P", // peta
"E", // exa
"Z", // zeta
"Y", // yotta
}
curr := float64(size) / 1000
for _, s := range suffix {
if curr < 10 {
2016-08-14 19:56:55 +00:00
return fmt.Sprintf("%.1f%s", curr-0.0499, s)
2016-08-13 12:49:04 +00:00
} else if curr < 1000 {
return fmt.Sprintf("%d%s", int(curr), s)
}
curr /= 1000
}
return ""
}
// This function compares two strings for natural sorting which takes into
// account values of numbers in strings. For example, '2' is less than '10',
// and similarly 'foo2bar' is less than 'foo10bar', but 'bar2bar' is greater
// than 'foo10bar'.
func naturalLess(s1, s2 string) bool {
lo1, lo2, hi1, hi2 := 0, 0, 0, 0
for {
if hi1 >= len(s1) {
return hi2 != len(s2)
}
if hi2 >= len(s2) {
return false
}
isDigit1 := isDigit(s1[hi1])
isDigit2 := isDigit(s2[hi2])
2016-08-13 12:49:04 +00:00
for lo1 = hi1; hi1 < len(s1) && isDigit(s1[hi1]) == isDigit1; hi1++ {
}
for lo2 = hi2; hi2 < len(s2) && isDigit(s2[hi2]) == isDigit2; hi2++ {
}
if s1[lo1:hi1] == s2[lo2:hi2] {
continue
2016-08-13 12:49:04 +00:00
}
if isDigit1 && isDigit2 {
num1, err1 := strconv.Atoi(s1[lo1:hi1])
num2, err2 := strconv.Atoi(s2[lo2:hi2])
2016-08-13 12:49:04 +00:00
if err1 == nil && err2 == nil {
return num1 < num2
}
}
return s1[lo1:hi1] < s2[lo2:hi2]
}
2016-08-13 12:49:04 +00:00
}
2018-05-20 17:30:41 +00:00
var reAltKey = regexp.MustCompile(`<a-(.)>`)
2018-05-14 16:38:35 +00:00
var reWord = regexp.MustCompile(`(\pL|\pN)+`)
var reWordBeg = regexp.MustCompile(`([^\pL\pN]|^)(\pL|\pN)`)
var reWordEnd = regexp.MustCompile(`(\pL|\pN)([^\pL\pN]|$)`)
2016-08-13 12:49:04 +00:00
func min(a, b int) int {
if a < b {
return a
}
return b
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
// We don't need no generic code
2018-05-20 17:30:41 +00:00
// We don't need no type control
2016-08-13 12:49:04 +00:00
// No dark templates in compiler
// Haskell leave them kids alone
// Hey Bjarne leave them kids alone
// All in all it's just another brick in the code
// All in all you're just another brick in the code
//
// -- Pink Trolled --