Added existing files
This commit is contained in:
parent
1ce95fbbab
commit
4e04fa6472
116
file-util.go
Normal file
116
file-util.go
Normal file
@ -0,0 +1,116 @@
|
||||
// file_util.go
|
||||
package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func FileSize(filePath string) (size int64) {
|
||||
info, err := os.Stat(filePath)
|
||||
if err == nil {
|
||||
size = info.Size()
|
||||
} else {
|
||||
size = -1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func IsRegularFile(filePath string) bool {
|
||||
if filePath != "" {
|
||||
info, err := os.Stat(filePath)
|
||||
// return (err == nil || os.IsExist(err)) && info.Mode().IsRegular()
|
||||
return (err == nil || errors.Is(err, os.ErrExist)) && info.Mode().IsRegular()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsDirectory(filePath string) bool {
|
||||
if filePath != "" {
|
||||
info, err := os.Stat(filePath)
|
||||
return (err == nil || errors.Is(err, os.ErrExist)) && info.Mode().IsDir()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsSymLink(filePath string) bool {
|
||||
if filePath != "" {
|
||||
info, err := os.Stat(filePath)
|
||||
return (err == nil || errors.Is(err, os.ErrExist)) && (info.Mode()&os.ModeSymlink != 0)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsSymLinkByDirEntry(e os.DirEntry) bool {
|
||||
info, _ := e.Info()
|
||||
return info.Mode()&os.ModeSymlink != 0
|
||||
}
|
||||
|
||||
func StreamIsTerminal(fh *os.File) bool {
|
||||
var isTerminal bool = false
|
||||
if fh != nil {
|
||||
fi, _ := fh.Stat()
|
||||
isTerminal = (fi.Mode() & os.ModeCharDevice) != 0
|
||||
}
|
||||
return isTerminal
|
||||
}
|
||||
|
||||
/*
|
||||
* Ispirata a https://stackoverflow.com/a/50741908
|
||||
*/
|
||||
func MoveFile(sourcePath, destPath string) (int64, error) {
|
||||
var size int64 = 0
|
||||
|
||||
err := os.Rename(sourcePath, destPath)
|
||||
if err != nil {
|
||||
inputFile, err := os.Open(sourcePath)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("Couldn't open source file: %s", err)
|
||||
}
|
||||
outputFile, err := os.Create(destPath)
|
||||
if err != nil {
|
||||
inputFile.Close()
|
||||
return 0, fmt.Errorf("Couldn't open dest file: %s", err)
|
||||
}
|
||||
defer outputFile.Close()
|
||||
size, err = io.Copy(outputFile, inputFile)
|
||||
inputFile.Close()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("Writing to output file failed: %s", err)
|
||||
}
|
||||
// The copy was successful, so now delete the original file
|
||||
err = os.Remove(sourcePath)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("Failed removing original file: %s", err)
|
||||
}
|
||||
}
|
||||
return size, nil
|
||||
}
|
||||
|
||||
func ParseMemSize(s string) (size int64, err error) {
|
||||
const MULTIPLES = "BKMGTP"
|
||||
// s = strings.TrimSpace(s)
|
||||
if len(s) != 0 {
|
||||
s = strings.ToUpper(s)
|
||||
s = strings.ReplaceAll(s, ".", "")
|
||||
s = strings.ReplaceAll(s, ",", "")
|
||||
s = strings.ReplaceAll(s, " ", "")
|
||||
pos := strings.IndexAny(s, MULTIPLES)
|
||||
if pos >= 0 {
|
||||
size, err = strconv.ParseInt(s[0:pos], 10, 64)
|
||||
mul := strings.IndexByte(MULTIPLES, s[pos])
|
||||
for ; mul > 0; mul-- {
|
||||
size = size * 1_000
|
||||
}
|
||||
} else {
|
||||
size, err = strconv.ParseInt(s, 10, 64)
|
||||
}
|
||||
} else {
|
||||
err = errors.New("Empty string")
|
||||
}
|
||||
return
|
||||
}
|
6
go.mod
Normal file
6
go.mod
Normal file
@ -0,0 +1,6 @@
|
||||
module portale-stac.it/packages/utils
|
||||
|
||||
go 1.21
|
||||
|
||||
require golang.org/x/text v0.3.7
|
||||
|
2
go.sum
Normal file
2
go.sum
Normal file
@ -0,0 +1,2 @@
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
36
gzip-util.go
Normal file
36
gzip-util.go
Normal file
@ -0,0 +1,36 @@
|
||||
// gzip-util.go
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const GZIP_SUFFIX = ".gz"
|
||||
|
||||
func CreateGzipReader(in io.Reader) (reader *bufio.Reader, err error) {
|
||||
inflate, err := gzip.NewReader(in)
|
||||
if err != nil {
|
||||
fmt.Errorf("Can't inflate gzipped input stream: %v", err)
|
||||
} else {
|
||||
reader = bufio.NewReader(inflate)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func CreateGzipFile(in io.Reader, fileName string) (reader *bufio.Reader, err error) {
|
||||
if len(fileName) > 0 && strings.HasSuffix(fileName, GZIP_SUFFIX) {
|
||||
inflate, err := gzip.NewReader(in)
|
||||
if err != nil {
|
||||
fmt.Errorf("Can't inflate gzipped input stream %#v: %v", fileName, err)
|
||||
} else {
|
||||
reader = bufio.NewReader(inflate)
|
||||
}
|
||||
} else {
|
||||
reader = bufio.NewReader(in)
|
||||
}
|
||||
return
|
||||
}
|
154
misc-util.go
Normal file
154
misc-util.go
Normal file
@ -0,0 +1,154 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/message"
|
||||
)
|
||||
|
||||
func NewEnglishPrinter() *message.Printer {
|
||||
return message.NewPrinter(language.English)
|
||||
}
|
||||
|
||||
func ExitErrorf(rc int, format string, args ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, format+"\n", args...)
|
||||
os.Exit(rc)
|
||||
}
|
||||
|
||||
func ExitMessagef(rc int, format string, args ...interface{}) {
|
||||
fmt.Printf(format+"\n", args...)
|
||||
os.Exit(rc)
|
||||
}
|
||||
|
||||
func OnStringIndex(index uint, values ...string) (value string) {
|
||||
if index >= 0 && index < uint(len(values)) {
|
||||
value = values[index]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func OnEmpty(s string, altValues ...string) (value string) {
|
||||
if len(s) == 0 {
|
||||
for _, altValue := range altValues {
|
||||
if len(altValue) > 0 {
|
||||
value = altValue
|
||||
}
|
||||
}
|
||||
} else {
|
||||
value = s
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func OnCond(cond bool, trueValue, falseValue string) string {
|
||||
if cond {
|
||||
return trueValue
|
||||
} else {
|
||||
return falseValue
|
||||
}
|
||||
}
|
||||
|
||||
func OnCondIface(cond bool, trueValue, falseValue interface{}) interface{} {
|
||||
if cond {
|
||||
return trueValue
|
||||
} else {
|
||||
return falseValue
|
||||
}
|
||||
}
|
||||
|
||||
func OnCondInt(cond bool, trueValue, falseValue int) int {
|
||||
if cond {
|
||||
return trueValue
|
||||
} else {
|
||||
return falseValue
|
||||
}
|
||||
}
|
||||
|
||||
func OnCondInt64(cond bool, trueValue, falseValue int64) int64 {
|
||||
if cond {
|
||||
return trueValue
|
||||
} else {
|
||||
return falseValue
|
||||
}
|
||||
}
|
||||
|
||||
func OnSuccess(cond bool) string {
|
||||
return OnCond(cond, "SUCCEEDED", "FAILED")
|
||||
}
|
||||
|
||||
func OnOk(cond bool) string {
|
||||
return OnCond(cond, "OK", "KO")
|
||||
}
|
||||
|
||||
func RandBool() bool {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
return rand.Intn(2) > 0
|
||||
}
|
||||
|
||||
func S2Uint32(s string, defaultValue uint32) uint32 {
|
||||
var res uint32
|
||||
v, err := strconv.ParseUint(s, 10, 32)
|
||||
if err == nil {
|
||||
res = uint32(v)
|
||||
} else {
|
||||
res = defaultValue
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func S2Int32(s string, defaultValue int32) int32 {
|
||||
var res int32
|
||||
v, err := strconv.ParseUint(s, 10, 32)
|
||||
if err == nil {
|
||||
res = int32(v)
|
||||
} else {
|
||||
res = defaultValue
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func RemoveQuotes(s string) string {
|
||||
l := len(s)
|
||||
if l >= 2 {
|
||||
if (s[0] == '"' && s[l-1] == '"') || (s[0] == '\'' && s[l-1] == '\'') {
|
||||
s = s[1 : l-1]
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Credits: https://yourbasic.org/golang/formatting-byte-size-to-human-readable-format/
|
||||
func ByteCountSI(b int64) string {
|
||||
const unit = 1000
|
||||
if b < unit {
|
||||
return fmt.Sprintf("%d B", b)
|
||||
}
|
||||
div, exp := int64(unit), 0
|
||||
for n := b / unit; n >= unit; n /= unit {
|
||||
div *= unit
|
||||
exp++
|
||||
}
|
||||
return fmt.Sprintf("%.1f %cB",
|
||||
float64(b)/float64(div), "kMGTPE"[exp])
|
||||
}
|
||||
|
||||
// Credits: https://yourbasic.org/golang/formatting-byte-size-to-human-readable-format/
|
||||
func ByteCountIEC(b int64) string {
|
||||
const unit = 1024
|
||||
if b < unit {
|
||||
return fmt.Sprintf("%d B", b)
|
||||
}
|
||||
div, exp := int64(unit), 0
|
||||
for n := b / unit; n >= unit; n /= unit {
|
||||
div *= unit
|
||||
exp++
|
||||
}
|
||||
return fmt.Sprintf("%.1f %ciB",
|
||||
float64(b)/float64(div), "KMGTPE"[exp])
|
||||
}
|
169
os-util.go
Normal file
169
os-util.go
Normal file
@ -0,0 +1,169 @@
|
||||
// os-util.go
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ExpandPath(sourcePath string) (expandedPath string, err error) {
|
||||
if strings.HasPrefix(sourcePath, "~") {
|
||||
var home, userName, remainder string
|
||||
|
||||
slashPos := strings.IndexRune(sourcePath, '/')
|
||||
if slashPos > 0 {
|
||||
userName = sourcePath[1:slashPos]
|
||||
remainder = sourcePath[slashPos:]
|
||||
} else {
|
||||
userName = sourcePath[1:]
|
||||
}
|
||||
|
||||
if len(userName) == 0 {
|
||||
home, err = os.UserHomeDir()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
var userInfo *user.User
|
||||
userInfo, err = user.Lookup(userName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
home = userInfo.HomeDir
|
||||
}
|
||||
expandedPath = os.ExpandEnv(path.Join(home, remainder))
|
||||
} else {
|
||||
expandedPath = os.ExpandEnv(sourcePath)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ExpandPathV(sourcePath string, maps ...map[string]string) (expandedPath string, err error) {
|
||||
var normalizedPath string
|
||||
if strings.HasPrefix(sourcePath, "~") {
|
||||
var home, userName, remainder string
|
||||
|
||||
slashPos := strings.IndexRune(sourcePath, '/')
|
||||
if slashPos > 0 {
|
||||
userName = sourcePath[1:slashPos]
|
||||
remainder = sourcePath[slashPos:]
|
||||
} else {
|
||||
userName = sourcePath[1:]
|
||||
}
|
||||
|
||||
if len(userName) == 0 {
|
||||
home, err = os.UserHomeDir()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
var userInfo *user.User
|
||||
userInfo, err = user.Lookup(userName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
home = userInfo.HomeDir
|
||||
}
|
||||
normalizedPath = path.Join(home, remainder)
|
||||
} else {
|
||||
normalizedPath = sourcePath
|
||||
}
|
||||
|
||||
expandedPath = os.Expand(normalizedPath, func(name string) (value string) {
|
||||
var ok bool
|
||||
for _, vars := range maps {
|
||||
if value, ok = vars[name]; ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
value = os.Getenv(name)
|
||||
}
|
||||
return
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func ExpandRealPath(sourcePath string) (expandedRealPath string, err error) {
|
||||
var expandedPath string
|
||||
if expandedPath, err = ExpandPath(sourcePath); err == nil {
|
||||
if expandedPath, err = filepath.Abs(expandedPath); err == nil {
|
||||
expandedRealPath, err = filepath.EvalSymlinks(expandedPath)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetProgramDirPath() (dir string, err error) {
|
||||
var exe string
|
||||
exe, err = os.Executable()
|
||||
if err == nil {
|
||||
exe, err = filepath.EvalSymlinks(exe)
|
||||
if err == nil {
|
||||
exe, err = filepath.Abs(exe)
|
||||
if err == nil {
|
||||
dir = filepath.Dir(exe)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetProgramPath() (exec string, err error) {
|
||||
var tmpExec string
|
||||
tmpExec, err = os.Executable()
|
||||
if err == nil {
|
||||
tmpExec, err = filepath.EvalSymlinks(tmpExec)
|
||||
if err == nil {
|
||||
exec, err = filepath.Abs(tmpExec)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func MakeParentDir(filePath string) (dir string, err error) {
|
||||
dir = filepath.Dir(filePath)
|
||||
if !IsDirectory(dir) {
|
||||
if err1 := os.MkdirAll(dir, 0755); err1 != nil {
|
||||
err = fmt.Errorf("Can't make parent path of %#v: %v", filePath, err1)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ExpandFilePathAndMakeParent(specPath string) (expandedPath, expandedParent string, err error) {
|
||||
expandedPath, err = ExpandPath(specPath)
|
||||
if err == nil {
|
||||
expandedParent, err = MakeParentDir(expandedPath)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ExpandFilePathAndMakeParentV(specPath string, maps ...map[string]string) (expandedPath, expandedParent string, err error) {
|
||||
expandedPath, err = ExpandPathV(specPath, maps...)
|
||||
if err == nil {
|
||||
expandedParent, err = MakeParentDir(expandedPath)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func MakeDir(dirPath string) (err error) {
|
||||
if !IsDirectory(dirPath) {
|
||||
if err1 := os.MkdirAll(dirPath, 0755); err1 != nil {
|
||||
err = fmt.Errorf("Can't make directory path %#v: %v", dirPath, err1)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ExpandDirPathAndMakeParent(specPath string) (expandedPath string, err error) {
|
||||
expandedPath, err = ExpandPath(specPath)
|
||||
if err == nil {
|
||||
err = MakeDir(expandedPath)
|
||||
}
|
||||
return
|
||||
}
|
222
string.go
Normal file
222
string.go
Normal file
@ -0,0 +1,222 @@
|
||||
// string
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func PadStringRight(s string, pad string, size int, suffix string) (paddedString string) {
|
||||
if len(pad) == 0 {
|
||||
pad = " "
|
||||
}
|
||||
space := size - len(s)
|
||||
if space > 0 {
|
||||
paddedString = s + strings.Repeat(pad, space)
|
||||
} else {
|
||||
paddedString = s
|
||||
}
|
||||
paddedString += ": %v"
|
||||
return
|
||||
}
|
||||
|
||||
func UnescapeSpecialChars(source string, charSet, specialChars string) string {
|
||||
var buf strings.Builder
|
||||
var escape bool
|
||||
for _, c := range source {
|
||||
if c == '\\' {
|
||||
if escape {
|
||||
buf.WriteByte('\\')
|
||||
escape = false
|
||||
} else {
|
||||
escape = true
|
||||
}
|
||||
} else {
|
||||
if escape {
|
||||
if index := strings.IndexRune(charSet, c); index >= 0 {
|
||||
if index < len(specialChars) {
|
||||
buf.WriteByte(specialChars[index])
|
||||
}
|
||||
}
|
||||
escape = false
|
||||
} else {
|
||||
buf.WriteRune(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func UnescapeBlanks(source string) string {
|
||||
return UnescapeSpecialChars(source, "nrt", "\n\r\t")
|
||||
}
|
||||
|
||||
func EscapeSpecialChars(source string, charSet string) string {
|
||||
var buf strings.Builder
|
||||
for _, c := range source {
|
||||
if strings.IndexRune(charSet, c) >= 0 {
|
||||
buf.WriteByte(92) // 92 = backslash
|
||||
}
|
||||
buf.WriteRune(c)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func EscapeShellSpecialChars(source string) string {
|
||||
return EscapeSpecialChars(source, "!`$\\()#~&*;")
|
||||
}
|
||||
|
||||
func SplitPair(source, sep string) (left, right string) {
|
||||
parts := strings.SplitN(source, sep, 2)
|
||||
left = strings.TrimSpace(parts[0])
|
||||
if len(parts) > 1 {
|
||||
right = strings.TrimSpace(parts[1])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func StartsWith(s string, aliases ...string) bool {
|
||||
startsWith := false
|
||||
for _, alias := range aliases {
|
||||
if startsWith = strings.HasPrefix(alias, s); startsWith {
|
||||
break
|
||||
}
|
||||
}
|
||||
return startsWith
|
||||
}
|
||||
|
||||
func JoinStringMap(m map[string]string, kvSep, itemSep string) string {
|
||||
var sb strings.Builder
|
||||
for key, value := range m {
|
||||
if sb.Len() > 0 {
|
||||
sb.WriteString(itemSep)
|
||||
}
|
||||
sb.WriteString(key)
|
||||
sb.WriteString(kvSep)
|
||||
sb.WriteString(value)
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func JoinMap(m map[string]any, kvSep, itemSep string) string {
|
||||
var sb strings.Builder
|
||||
for key, value := range m {
|
||||
if sb.Len() > 0 {
|
||||
sb.WriteString(itemSep)
|
||||
}
|
||||
sb.WriteString(key)
|
||||
sb.WriteString(kvSep)
|
||||
if s, ok := value.(fmt.Stringer); ok {
|
||||
sb.WriteString(s.String())
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf("%v", value))
|
||||
}
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func SplitByRune(s string, sep rune, maxParts int) (parts []string) {
|
||||
return ScopedSplitByRune(s, sep, `'"`, `'"`, maxParts)
|
||||
// var capacity int = 0
|
||||
// var sb strings.Builder
|
||||
// if maxParts < 0 {
|
||||
// for _, b := range s {
|
||||
// if b == sep {
|
||||
// capacity++
|
||||
// }
|
||||
// }
|
||||
// capacity++
|
||||
// } else {
|
||||
// capacity = max(1, maxParts)
|
||||
// }
|
||||
// parts = make([]string, 0, capacity)
|
||||
|
||||
// count := 0
|
||||
// quote := rune(0)
|
||||
// for i, r := range s {
|
||||
// if r == sep && quote == rune(0) {
|
||||
// if len(parts) < capacity {
|
||||
// count++
|
||||
// if count == maxParts {
|
||||
// sb.WriteString(s[i:])
|
||||
// }
|
||||
// parts = append(parts, sb.String())
|
||||
// sb.Reset()
|
||||
// if count == maxParts {
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// } else if r == '\'' || r == '"' {
|
||||
// if quote == r {
|
||||
// quote = rune(0)
|
||||
// } else {
|
||||
// quote = r
|
||||
// }
|
||||
// sb.WriteRune(r)
|
||||
// } else {
|
||||
// sb.WriteRune(r)
|
||||
// }
|
||||
// }
|
||||
// if maxParts < 0 {
|
||||
// parts = append(parts, sb.String())
|
||||
// } else {
|
||||
// for ; count < maxParts; count++ {
|
||||
// parts = append(parts, sb.String())
|
||||
// sb.Reset()
|
||||
// }
|
||||
// }
|
||||
// return parts
|
||||
}
|
||||
|
||||
func ScopedSplitByRune(s string, sep rune, scopeOpeners, scopeClosers string, maxParts int) (parts []string) {
|
||||
var capacity int = 0
|
||||
var sb strings.Builder
|
||||
|
||||
if len(scopeClosers) != len(scopeOpeners) {
|
||||
panic("scope openers and closers must have the same length")
|
||||
}
|
||||
|
||||
if maxParts < 0 {
|
||||
capacity = strings.Count(s, string(sep)) + 1
|
||||
} else {
|
||||
capacity = max(1, maxParts)
|
||||
}
|
||||
|
||||
parts = make([]string, 0, capacity)
|
||||
|
||||
count := 0
|
||||
scopeIndex := -1
|
||||
for i, r := range s {
|
||||
if r == sep && scopeIndex < 0 {
|
||||
if len(parts) < capacity {
|
||||
count++
|
||||
if count == maxParts {
|
||||
sb.WriteString(s[i:])
|
||||
}
|
||||
parts = append(parts, sb.String())
|
||||
sb.Reset()
|
||||
if count == maxParts {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if scopeIndex < 0 {
|
||||
if quoteIndex := strings.IndexRune(scopeOpeners, r); quoteIndex >= 0 {
|
||||
scopeIndex = quoteIndex
|
||||
}
|
||||
} else if quoteIndex := strings.IndexRune(scopeClosers, r); quoteIndex >= 0 {
|
||||
scopeIndex = -1
|
||||
}
|
||||
sb.WriteRune(r)
|
||||
}
|
||||
}
|
||||
if maxParts < 0 {
|
||||
parts = append(parts, sb.String())
|
||||
} else {
|
||||
for ; count < maxParts; count++ {
|
||||
parts = append(parts, sb.String())
|
||||
sb.Reset()
|
||||
}
|
||||
}
|
||||
return parts
|
||||
}
|
283
text-util.go
Normal file
283
text-util.go
Normal file
@ -0,0 +1,283 @@
|
||||
// text-util.go
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/user"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TextTemplate struct {
|
||||
varMap map[string]string
|
||||
UserName string
|
||||
Uid string
|
||||
ProgramDir string
|
||||
HomeDir string
|
||||
env map[string]string
|
||||
}
|
||||
|
||||
func NewTextTemplate(args ...string) (instance *TextTemplate) {
|
||||
instance = &TextTemplate{}
|
||||
user, err := user.Current()
|
||||
if err == nil {
|
||||
instance.UserName = user.Username
|
||||
instance.HomeDir = user.HomeDir
|
||||
instance.Uid = user.Uid
|
||||
}
|
||||
|
||||
instance.ProgramDir, _ = GetProgramDirPath()
|
||||
instance.varMap = make(map[string]string, 5+len(args))
|
||||
instance.initVarMap()
|
||||
|
||||
for _, arg := range args {
|
||||
if len(arg) == 0 {
|
||||
break
|
||||
}
|
||||
parts := strings.SplitN(arg, ":", 2)
|
||||
if len(parts) == 2 {
|
||||
instance.varMap[parts[0]] = parts[1]
|
||||
} else {
|
||||
instance.varMap[parts[0]] = ""
|
||||
}
|
||||
}
|
||||
|
||||
instance.env = make(map[string]string)
|
||||
for _, v := range os.Environ() {
|
||||
parts := strings.SplitN(v, "=", 2)
|
||||
instance.env[parts[0]] = parts[1]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *TextTemplate) initVarMap() {
|
||||
self.varMap["dt"] = "Current timestamp as YYYY-MM-DD_HH-MM-SS"
|
||||
self.varMap["ymd"] = "Current date as YYYY-MM-DD"
|
||||
self.varMap["hm"] = "Current time as HH-MM"
|
||||
self.varMap["h"] = "Current time as HH"
|
||||
self.varMap["hms"] = "Current time as HH-MM-SS"
|
||||
self.varMap["month"] = "Month name"
|
||||
self.varMap["month-num"] = "Month as number (mm)"
|
||||
self.varMap["week-day"] = "Week day name"
|
||||
self.varMap["week-day-num"] = "Week day as number"
|
||||
self.varMap["yesterday"] = "Yesterday date"
|
||||
self.varMap["uid"] = self.Uid
|
||||
self.varMap["username"] = self.UserName
|
||||
self.varMap["home"] = self.HomeDir
|
||||
self.varMap["progdir"] = self.ProgramDir
|
||||
self.varMap["hostname"], _ = os.Hostname()
|
||||
}
|
||||
|
||||
func (self *TextTemplate) AddVar(name, value string) {
|
||||
self.varMap[name] = value
|
||||
}
|
||||
|
||||
// func MakeVarMap() map[string]string {
|
||||
// return map[string]string{
|
||||
// "ymd": "Current date as YYYY-MM-DD",
|
||||
// "hms": "Current time as HH-MM-SS",
|
||||
// // "host": "Hostname",
|
||||
// "uid": "UID",
|
||||
// "username": "Username",
|
||||
// "home": "Home directory",
|
||||
// }
|
||||
// }
|
||||
|
||||
func (self *TextTemplate) updateVarMap() {
|
||||
var now = time.Now()
|
||||
|
||||
self.varMap["ymd"] = fmt.Sprintf("%04d-%02d-%02d", now.Year(), now.Month(), now.Day())
|
||||
self.varMap["hms"] = fmt.Sprintf("%02d-%02d-%02d", now.Hour(), now.Minute(), now.Second())
|
||||
self.varMap["hm"] = fmt.Sprintf("%02d-%02d", now.Hour(), now.Minute())
|
||||
self.varMap["h"] = fmt.Sprintf("%02d", now.Hour())
|
||||
self.varMap["month"] = now.Format("January")
|
||||
self.varMap["month-num"] = fmt.Sprintf("%02d", now.Month())
|
||||
self.varMap["week-day-num"] = fmt.Sprintf("%d", 1+now.Weekday())
|
||||
self.varMap["week-day"] = now.Format("Monday")
|
||||
self.varMap["dt"] = self.varMap["ymd"] + "_" + self.varMap["hms"]
|
||||
|
||||
yday := now.AddDate(0, 0, -1)
|
||||
self.varMap["yesterday"] = fmt.Sprintf("%04d-%02d-%02d", yday.Year(), yday.Month(), yday.Day())
|
||||
// self.varMap["uid"] = strconv.Itoa(int(self.Uid))
|
||||
// self.varMap["uid"] = self.Uid
|
||||
// self.varMap["username"] = self.UserName
|
||||
// // self.varMap["host"] = self.Hostname
|
||||
// self.varMap["home"] = self.HomeDir
|
||||
}
|
||||
|
||||
func (self *TextTemplate) Expand(template string) string {
|
||||
|
||||
self.updateVarMap()
|
||||
|
||||
result := os.Expand(template, func(name string) string {
|
||||
value, ok := self.varMap[name]
|
||||
if ok {
|
||||
return value
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (self *TextTemplate) ExpandEnv(template string) (result string) {
|
||||
if len(template) > 0 {
|
||||
self.updateVarMap()
|
||||
result = os.Expand(template, func(name string) string {
|
||||
value, ok := self.varMap[name]
|
||||
if ok {
|
||||
return value
|
||||
} else {
|
||||
return "${" + name + "}"
|
||||
}
|
||||
})
|
||||
|
||||
// for k, v := range self.varMap {
|
||||
// self.env[k] = v
|
||||
// }
|
||||
|
||||
result = os.ExpandEnv(result)
|
||||
// result = os.Expand(result, func(name string) string {
|
||||
// value, ok := self.env[name]
|
||||
// if ok {
|
||||
// return value
|
||||
// } else {
|
||||
// return ""
|
||||
// }
|
||||
// })
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (self *TextTemplate) Value(key string) (value string, ok bool) {
|
||||
value, ok = self.varMap[key]
|
||||
return
|
||||
}
|
||||
|
||||
func (self *TextTemplate) String() string {
|
||||
var sb strings.Builder
|
||||
|
||||
keys := make([]string, 0, len(self.varMap))
|
||||
for k := range self.varMap {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, k := range keys {
|
||||
sb.WriteString(fmt.Sprintf("%s=%#v\n", k, self.varMap[k]))
|
||||
}
|
||||
// for k, v := range self.varMap {
|
||||
// sb.WriteString(fmt.Sprintf("%s=%#v\n", k, v))
|
||||
// }
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func ParseParamPairsFile(sourceFile string, separator string, hook func(line int, name, value string) (err error)) (err error) {
|
||||
var source io.Reader
|
||||
if source, err = os.Open(sourceFile); err == nil {
|
||||
err = ParseParamPairs(source, separator, hook)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ParseParamPairs(source io.Reader, separator string, hook func(line int, name, value string) (err error)) (err error) {
|
||||
var content, sep, name, value []byte
|
||||
var sb strings.Builder
|
||||
|
||||
if content, err = ioutil.ReadAll(source); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(separator) == 0 {
|
||||
sep = []byte{' '}
|
||||
} else {
|
||||
sep = []byte(separator)
|
||||
}
|
||||
for lineCount, rawLine := range bytes.Split(content, []byte{'\n'}) {
|
||||
rawLine = bytes.TrimSpace(rawLine)
|
||||
if len(rawLine) == 0 || rawLine[0] == '#' {
|
||||
continue
|
||||
}
|
||||
parts := bytes.SplitN(rawLine, sep, 2)
|
||||
name = bytes.TrimSpace(parts[0])
|
||||
if len(parts) > 1 {
|
||||
value = bytes.TrimSpace(parts[1])
|
||||
} else {
|
||||
value = []byte{}
|
||||
}
|
||||
quoteOn := false
|
||||
escMode := false
|
||||
sb.Reset()
|
||||
for _, c := range value {
|
||||
switch c {
|
||||
case '"':
|
||||
if escMode {
|
||||
sb.WriteByte(c)
|
||||
escMode = false
|
||||
} else {
|
||||
quoteOn = !quoteOn
|
||||
}
|
||||
case '\\':
|
||||
if escMode {
|
||||
sb.WriteByte(c)
|
||||
escMode = false
|
||||
} else {
|
||||
escMode = true
|
||||
}
|
||||
case 'n':
|
||||
if escMode {
|
||||
sb.WriteByte('\n')
|
||||
escMode = false
|
||||
} else {
|
||||
sb.WriteByte(c)
|
||||
}
|
||||
case 'r':
|
||||
if escMode {
|
||||
sb.WriteByte('\r')
|
||||
escMode = false
|
||||
} else {
|
||||
sb.WriteByte(c)
|
||||
}
|
||||
case 't':
|
||||
if escMode {
|
||||
sb.WriteByte('\t')
|
||||
escMode = false
|
||||
} else {
|
||||
sb.WriteByte(c)
|
||||
}
|
||||
case '#':
|
||||
if quoteOn {
|
||||
sb.WriteByte(c)
|
||||
} else if escMode {
|
||||
sb.WriteByte(c)
|
||||
escMode = false
|
||||
} else {
|
||||
goto endloop
|
||||
}
|
||||
default:
|
||||
if escMode {
|
||||
escMode = false
|
||||
}
|
||||
sb.WriteByte(c)
|
||||
}
|
||||
}
|
||||
endloop:
|
||||
if err == nil && quoteOn {
|
||||
err = errors.New("unbalanced quotes")
|
||||
break
|
||||
}
|
||||
if err == nil {
|
||||
if err = hook(lineCount, string(name), sb.String()); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
69
time.go
Normal file
69
time.go
Normal file
@ -0,0 +1,69 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func StringTimestamp2GoTime(s string) (ts time.Time) {
|
||||
secs, err := strconv.ParseInt(s, 10, 64)
|
||||
if err == nil {
|
||||
ts = time.Unix(secs, 0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func StringSeconds2GoDuration(s string) (secs time.Duration) {
|
||||
if len(s) > 0 {
|
||||
if s[len(s)-1] != 's' {
|
||||
s += "s"
|
||||
}
|
||||
secs, _ = time.ParseDuration(s)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Convert the spec string to a number of seconds.
|
||||
// spec is a number, optionally followed by a time unit among 's'(seconds),
|
||||
// 'm'(minutes), 'h'(hours), and 'd'(days)
|
||||
// If the time unit is omitted, defaultUnit is used.
|
||||
// Empty spec returns 0 seconds.
|
||||
func ParseTimeInterval(spec string, defaultUnit byte) (interval int64, err error) {
|
||||
var u byte
|
||||
var intVal int
|
||||
|
||||
spec = strings.TrimSpace(spec)
|
||||
if len(spec) == 0 {
|
||||
//err = errors.New(iface.ErrEmptyValue)
|
||||
return
|
||||
}
|
||||
spec = strings.ToLower(spec)
|
||||
u = spec[len(spec)-1]
|
||||
if u >= '0' && u <= '9' {
|
||||
u = 'm'
|
||||
} else {
|
||||
spec = spec[0 : len(spec)-1]
|
||||
}
|
||||
if intVal, err = strconv.Atoi(spec); err != nil {
|
||||
return
|
||||
// } else if intVal <= 0 {
|
||||
// err = errors.New(iface.ErrMustBePositive)
|
||||
// return
|
||||
}
|
||||
|
||||
switch u {
|
||||
case 's':
|
||||
interval = int64(intVal)
|
||||
case 'm':
|
||||
interval = int64(intVal) * 60
|
||||
case 'h':
|
||||
interval = int64(intVal) * 3600
|
||||
case 'd':
|
||||
interval = int64(intVal) * 86400
|
||||
default:
|
||||
err = fmt.Errorf("invalid time unit %c", u)
|
||||
}
|
||||
return
|
||||
}
|
204
tty-util.go
Normal file
204
tty-util.go
Normal file
@ -0,0 +1,204 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
const STDIN uint = 0
|
||||
const STDOUT uint = 1
|
||||
const STDERR uint = 2
|
||||
|
||||
const (
|
||||
NONE = iota
|
||||
BLACK
|
||||
RED
|
||||
GREEN
|
||||
BLUE
|
||||
BROWN
|
||||
// YELLOW
|
||||
CYAN
|
||||
MAGENTA
|
||||
WHITE
|
||||
)
|
||||
|
||||
type TTYContext struct {
|
||||
isTTY [3]bool
|
||||
}
|
||||
|
||||
func (self *TTYContext) Init() {
|
||||
for i, _ := range self.isTTY {
|
||||
self.isTTY[i] = StreamIsTerminal(getStream(uint(i)))
|
||||
}
|
||||
}
|
||||
|
||||
func (self *TTYContext) IsTTY(fd uint) bool {
|
||||
if fd == STDERR || fd == STDOUT {
|
||||
return self.isTTY[fd]
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *TTYContext) Clean() {
|
||||
for i, _ := range self.isTTY {
|
||||
self.isTTY[i] = false
|
||||
}
|
||||
}
|
||||
|
||||
func (self *TTYContext) Bold(fd uint) string {
|
||||
if self.isTTY[fd] {
|
||||
return "\x1b[1m"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *TTYContext) BoldOff(fd uint) string {
|
||||
if self.isTTY[fd] {
|
||||
return "\x1b[22m"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *TTYContext) Underline(fd uint) string {
|
||||
if self.isTTY[fd] {
|
||||
return "\x1b[4m"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *TTYContext) UnderlineOff(fd uint) string {
|
||||
if self.isTTY[fd] {
|
||||
return "\x1b[24m"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *TTYContext) Italic(fd uint) string {
|
||||
if self.isTTY[fd] {
|
||||
return "\x1b[3m"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *TTYContext) ItalicOff(fd uint) string {
|
||||
if self.isTTY[fd] {
|
||||
return "\x1b[23m"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *TTYContext) BlackFg(fd uint) string {
|
||||
if self.isTTY[fd] {
|
||||
return "\x1b[30m"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *TTYContext) RedFg(fd uint) string {
|
||||
if self.isTTY[fd] {
|
||||
return "\x1b[31m"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *TTYContext) GreenFg(fd uint) string {
|
||||
if self.isTTY[fd] {
|
||||
return "\x1b[32m"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *TTYContext) BrownFg(fd uint) string {
|
||||
if self.isTTY[fd] {
|
||||
return "\x1b[33m"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *TTYContext) BlueFg(fd uint) string {
|
||||
if self.isTTY[fd] {
|
||||
return "\x1b[34m"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *TTYContext) MagentaFg(fd uint) string {
|
||||
if self.isTTY[fd] {
|
||||
return "\x1b[35m"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *TTYContext) CyanFg(fd uint) string {
|
||||
if self.isTTY[fd] {
|
||||
return "\x1b[36m"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *TTYContext) WhiteFg(fd uint) string {
|
||||
if self.isTTY[fd] {
|
||||
return "\x1b[37m"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *TTYContext) FgColor(fd uint, n uint) string {
|
||||
var color string
|
||||
switch n {
|
||||
case BLACK:
|
||||
color = self.BlackFg(fd)
|
||||
case WHITE:
|
||||
color = self.WhiteFg(fd)
|
||||
case RED:
|
||||
color = self.RedFg(fd)
|
||||
case GREEN:
|
||||
color = self.GreenFg(fd)
|
||||
case BLUE:
|
||||
color = self.BlueFg(fd)
|
||||
case BROWN:
|
||||
color = self.BrownFg(fd)
|
||||
case CYAN:
|
||||
color = self.CyanFg(fd)
|
||||
case MAGENTA:
|
||||
color = self.MagentaFg(fd)
|
||||
}
|
||||
return color
|
||||
}
|
||||
|
||||
// func (self *TTYContext) BgColor(fd uint, n int) string {
|
||||
// var color string
|
||||
// switch n {
|
||||
// case BLACK:
|
||||
// color = self.BlackBg(fd)
|
||||
// case WHITE:
|
||||
// color = self.WhiteBg(fd)
|
||||
// case RED:
|
||||
// color = self.RedBg(fd)
|
||||
// case GREEN:
|
||||
// color = self.GreenBg(fd)
|
||||
// case CYAN:
|
||||
// color = self.CyanBg(fd)
|
||||
// case MAGENTA:
|
||||
// color = self.MagentaBg(fd)
|
||||
// }
|
||||
// return color
|
||||
// }
|
||||
|
||||
func (self *TTYContext) Reset(fd uint) string {
|
||||
if StreamIsTerminal(getStream(fd)) {
|
||||
return "\x1b[0m"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getStream(fd uint) *os.File {
|
||||
var s *os.File = nil
|
||||
|
||||
switch fd {
|
||||
case STDOUT:
|
||||
s = os.Stdout
|
||||
case STDERR:
|
||||
s = os.Stderr
|
||||
}
|
||||
return s
|
||||
}
|
Loading…
Reference in New Issue
Block a user