| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727 | 
							- /*
 
-  * Public Domain Software
 
-  *
 
-  * I (Matthias Ladkau) am the author of the source code in this file.
 
-  * I have placed the source code in this file in the public domain.
 
-  *
 
-  * For further information see: http://creativecommons.org/publicdomain/zero/1.0/
 
-  */
 
- /*
 
- Package stringutil contains common function for string operations.
 
- */
 
- package stringutil
 
- import (
 
- 	"bytes"
 
- 	"crypto/md5"
 
- 	"encoding/json"
 
- 	"fmt"
 
- 	"regexp"
 
- 	"sort"
 
- 	"strconv"
 
- 	"strings"
 
- 	"unicode/utf8"
 
- )
 
- /*
 
- LongestCommonPrefix determines the longest common prefix of a given list of strings.
 
- */
 
- func LongestCommonPrefix(s []string) string {
 
- 	var res string
 
- 	commonPrefix := func(str1, str2 string) string {
 
- 		var buf bytes.Buffer
 
- 		rs2 := StringToRuneSlice(str2)
 
- 		rs2len := len(rs2)
 
- 		for i, c := range str1 {
 
- 			if i >= rs2len {
 
- 				break
 
- 			} else if c == rs2[i] {
 
- 				buf.WriteRune(c)
 
- 			}
 
- 		}
 
- 		return buf.String()
 
- 	}
 
- 	lens := len(s)
 
- 	if lens > 0 {
 
- 		res = s[0]
 
- 		for i := 1; i < lens; i++ {
 
- 			res = commonPrefix(res, s[i])
 
- 		}
 
- 	}
 
- 	return res
 
- }
 
- /*
 
- PrintStringTable prints a given list of strings as table with c columns.
 
- */
 
- func PrintStringTable(ss []string, c int) string {
 
- 	var ret bytes.Buffer
 
- 	if c < 1 {
 
- 		return ""
 
- 	}
 
- 	//  Determine max widths of columns
 
- 	maxWidths := make(map[int]int)
 
- 	for i, s := range ss {
 
- 		col := i % c
 
- 		if l := utf8.RuneCountInString(s); l > maxWidths[col] {
 
- 			maxWidths[col] = l
 
- 		}
 
- 	}
 
- 	for i, s := range ss {
 
- 		col := i % c
 
- 		if i < len(ss)-1 {
 
- 			var formatString string
 
- 			if col != c-1 {
 
- 				formatString = fmt.Sprintf("%%-%vv ", maxWidths[col])
 
- 			} else {
 
- 				formatString = "%v"
 
- 			}
 
- 			ret.WriteString(fmt.Sprintf(formatString, s))
 
- 		} else {
 
- 			ret.WriteString(fmt.Sprintln(s))
 
- 			break
 
- 		}
 
- 		if col == c-1 {
 
- 			ret.WriteString(fmt.Sprintln())
 
- 		}
 
- 	}
 
- 	return ret.String()
 
- }
 
- /*
 
- GraphicStringTableSymbols defines how to draw a graphic table.
 
- */
 
- type GraphicStringTableSymbols struct {
 
- 	BoxHorizontal string
 
- 	BoxVertical   string
 
- 	BoxMiddle     string
 
- 	BoxCornerTopLeft     string
 
- 	BoxCornerTopRight    string
 
- 	BoxCornerBottomLeft  string
 
- 	BoxCornerBottomRight string
 
- 	BoxTopMiddle    string
 
- 	BoxLeftMiddle   string
 
- 	BoxRightMiddle  string
 
- 	BoxBottomMiddle string
 
- }
 
- /*
 
- Standard graphic table drawing definitions.
 
- */
 
- var (
 
- 	SingleLineTable       = &GraphicStringTableSymbols{"─", "│", "┼", "┌", "┐", "└", "┘", "┬", "├", "┤", "┴"}
 
- 	DoubleLineTable       = &GraphicStringTableSymbols{"═", "║", "╬", "╔", "╗", "╚", "╝", "╦", "╠", "╣", "╩"}
 
- 	SingleDoubleLineTable = &GraphicStringTableSymbols{"═", "│", "╪", "╒", "╕", "╘", "╛", "╤", "╞", "╡", "╧"}
 
- 	DoubleSingleLineTable = &GraphicStringTableSymbols{"─", "║", "╫", "╓", "╖", "╙", "╜", "╥", "╟", "╢", "╨"}
 
- 	MonoTable             = &GraphicStringTableSymbols{"#", "#", "#", "#", "#", "#", "#", "#", "#", "#", "#"}
 
- )
 
- /*
 
- PrintGraphicStringTable prints a given list of strings in a graphic table
 
- with c columns - creates a header after n rows using syms as drawing symbols.
 
- */
 
- func PrintGraphicStringTable(ss []string, c int, n int, syms *GraphicStringTableSymbols) string {
 
- 	var topline, bottomline, middleline, ret bytes.Buffer
 
- 	if c < 1 {
 
- 		return ""
 
- 	}
 
- 	if syms == nil {
 
- 		syms = MonoTable
 
- 	}
 
- 	//  Determine max widths of columns
 
- 	maxWidths := make(map[int]int)
 
- 	for i, s := range ss {
 
- 		col := i % c
 
- 		l := utf8.RuneCountInString(s)
 
- 		if l > maxWidths[col] {
 
- 			maxWidths[col] = l
 
- 		}
 
- 	}
 
- 	// Determine total width and create top, middle and bottom line
 
- 	totalWidth := 1
 
- 	topline.WriteString(syms.BoxCornerTopLeft)
 
- 	bottomline.WriteString(syms.BoxCornerBottomLeft)
 
- 	middleline.WriteString(syms.BoxLeftMiddle)
 
- 	for i := 0; i < len(maxWidths); i++ {
 
- 		totalWidth += maxWidths[i] + 2
 
- 		topline.WriteString(GenerateRollingString(syms.BoxHorizontal, maxWidths[i]+1))
 
- 		bottomline.WriteString(GenerateRollingString(syms.BoxHorizontal, maxWidths[i]+1))
 
- 		middleline.WriteString(GenerateRollingString(syms.BoxHorizontal, maxWidths[i]+1))
 
- 		if i < len(maxWidths)-1 {
 
- 			topline.WriteString(syms.BoxTopMiddle)
 
- 			bottomline.WriteString(syms.BoxBottomMiddle)
 
- 			middleline.WriteString(syms.BoxMiddle)
 
- 		}
 
- 	}
 
- 	topline.WriteString(syms.BoxCornerTopRight)
 
- 	bottomline.WriteString(syms.BoxCornerBottomRight)
 
- 	middleline.WriteString(syms.BoxRightMiddle)
 
- 	// Draw the table
 
- 	ret.WriteString(topline.String())
 
- 	ret.WriteString(fmt.Sprintln())
 
- 	row := 0
 
- 	for i, s := range ss {
 
- 		col := i % c
 
- 		ret.WriteString(syms.BoxVertical)
 
- 		if i < len(ss)-1 {
 
- 			formatString := fmt.Sprintf("%%-%vv ", maxWidths[col])
 
- 			ret.WriteString(fmt.Sprintf(formatString, s))
 
- 		} else {
 
- 			formatString := fmt.Sprintf("%%-%vv ", maxWidths[col])
 
- 			ret.WriteString(fmt.Sprintf(formatString, s))
 
- 			for col < c-1 && col < len(ss)-1 {
 
- 				col++
 
- 				ret.WriteString(syms.BoxVertical)
 
- 				ret.WriteString(GenerateRollingString(" ", maxWidths[col]))
 
- 				ret.WriteString(" ")
 
- 			}
 
- 			ret.WriteString(syms.BoxVertical)
 
- 			ret.WriteString(fmt.Sprintln())
 
- 			break
 
- 		}
 
- 		if col == c-1 {
 
- 			ret.WriteString(syms.BoxVertical)
 
- 			ret.WriteString(fmt.Sprintln())
 
- 			row++
 
- 			if row == n {
 
- 				ret.WriteString(middleline.String())
 
- 				ret.WriteString(fmt.Sprintln())
 
- 			}
 
- 		}
 
- 	}
 
- 	ret.WriteString(bottomline.String())
 
- 	ret.WriteString(fmt.Sprintln())
 
- 	return ret.String()
 
- }
 
- /*
 
- PrintCSVTable prints a given list of strings in a CSV table with c
 
- columns.
 
- */
 
- func PrintCSVTable(ss []string, c int) string {
 
- 	var ret bytes.Buffer
 
- 	var col int
 
- 	if c < 1 || len(ss) == 0 {
 
- 		return ""
 
- 	}
 
- 	// Write the table
 
- 	for i, s := range ss {
 
- 		col = i % c
 
- 		ret.WriteString(strings.TrimSpace(fmt.Sprint(s)))
 
- 		if col == c-1 {
 
- 			ret.WriteString(fmt.Sprintln())
 
- 		} else if i < len(ss)-1 {
 
- 			ret.WriteString(", ")
 
- 		}
 
- 	}
 
- 	if col != c-1 {
 
- 		ret.WriteString(fmt.Sprintln())
 
- 	}
 
- 	return ret.String()
 
- }
 
- /*
 
- RuneSliceToString converts a slice of runes into a string.
 
- */
 
- func RuneSliceToString(buf []rune) string {
 
- 	var sbuf bytes.Buffer
 
- 	for _, r := range buf {
 
- 		fmt.Fprintf(&sbuf, "%c", r)
 
- 	}
 
- 	return sbuf.String()
 
- }
 
- /*
 
- StringToRuneSlice converts a string into a slice of runes.
 
- */
 
- func StringToRuneSlice(s string) []rune {
 
- 	var buf []rune
 
- 	for _, r := range s {
 
- 		buf = append(buf, r)
 
- 	}
 
- 	return buf
 
- }
 
- /*
 
- Plural returns the string 's' if the parameter is greater than one or
 
- if the parameter is 0.
 
- */
 
- func Plural(l int) string {
 
- 	if l > 1 || l == 0 {
 
- 		return "s"
 
- 	}
 
- 	return ""
 
- }
 
- /*
 
- GlobParseError describes a failure to parse a glob expression
 
- and gives the offending expression.
 
- */
 
- type GlobParseError struct {
 
- 	Msg  string
 
- 	Pos  int
 
- 	Glob string
 
- }
 
- /*
 
- Error Returns a string representation of the error.
 
- */
 
- func (e *GlobParseError) Error() string {
 
- 	return fmt.Sprintf("%s at %d of %s", e.Msg, e.Pos, e.Glob)
 
- }
 
- /*
 
- GlobToRegex converts a given glob expression into a regular expression.
 
- */
 
- func GlobToRegex(glob string) (string, error) {
 
- 	buf := new(bytes.Buffer)
 
- 	brackets, braces := 0, 0
 
- 	n := len(glob)
 
- 	for i := 0; i < n; i++ {
 
- 		char := glob[i]
 
- 		switch char {
 
- 		case '\\':
 
- 			// Escapes
 
- 			i++
 
- 			if i >= n {
 
- 				return "", &GlobParseError{"Missing escaped character", i, glob}
 
- 			}
 
- 			buf.WriteByte(char)
 
- 			buf.WriteByte(glob[i])
 
- 			continue
 
- 		case '*':
 
- 			// Wildcard match multiple characters
 
- 			buf.WriteByte('.')
 
- 		case '?':
 
- 			// Wildcard match any single character
 
- 			buf.WriteByte('.')
 
- 			continue
 
- 		case '{':
 
- 			// Group (always non-capturing)
 
- 			buf.WriteString("(?:")
 
- 			braces++
 
- 			continue
 
- 		case '}':
 
- 			// End of group
 
- 			if braces > 0 {
 
- 				braces--
 
- 				buf.WriteByte(')')
 
- 				continue
 
- 			}
 
- 		case '[':
 
- 			// Character class
 
- 			if brackets > 0 {
 
- 				return "", &GlobParseError{"Unclosed character class", i, glob}
 
- 			}
 
- 			brackets++
 
- 		case ']':
 
- 			// End of character class
 
- 			brackets = 0
 
- 		case ',':
 
- 			// OR in groups
 
- 			if braces > 0 {
 
- 				buf.WriteByte('|')
 
- 			} else {
 
- 				buf.WriteByte(char)
 
- 			}
 
- 			continue
 
- 		case '^':
 
- 			// Beginning of line in character classes otherwise normal
 
- 			// escaped character
 
- 			if brackets == 0 {
 
- 				buf.WriteByte('\\')
 
- 			}
 
- 		case '!':
 
- 			// [! is the equivalent of [^ in glob
 
- 			if brackets > 0 && glob[i-1] == '[' {
 
- 				buf.WriteByte('^')
 
- 			} else {
 
- 				buf.WriteByte('!')
 
- 			}
 
- 			continue
 
- 		case '.', '$', '(', ')', '|', '+':
 
- 			// Escape all regex characters which are not glob characters
 
- 			buf.WriteByte('\\')
 
- 		}
 
- 		buf.WriteByte(char)
 
- 	}
 
- 	if brackets > 0 {
 
- 		return "", &GlobParseError{"Unclosed character class", n, glob}
 
- 	} else if braces > 0 {
 
- 		return "", &GlobParseError{"Unclosed group", n, glob}
 
- 	}
 
- 	return buf.String(), nil
 
- }
 
- /*
 
- GlobStartingLiterals gets the first literals of a glob string.
 
- */
 
- func GlobStartingLiterals(glob string) string {
 
- 	buf := new(bytes.Buffer)
 
- 	n := len(glob)
 
- 	for i := 0; i < n; i++ {
 
- 		char := glob[i]
 
- 		if char == '\\' || char == '*' || char == '?' ||
 
- 			char == '{' || char == '[' {
 
- 			break
 
- 		}
 
- 		buf.WriteByte(char)
 
- 	}
 
- 	return buf.String()
 
- }
 
- /*
 
- LevenshteinDistance computes the Levenshtein distance between two strings.
 
- */
 
- func LevenshteinDistance(str1, str2 string) int {
 
- 	if str1 == str2 {
 
- 		return 0
 
- 	}
 
- 	rslice1 := StringToRuneSlice(str1)
 
- 	rslice2 := StringToRuneSlice(str2)
 
- 	n, m := len(rslice1), len(rslice2)
 
- 	if n == 0 {
 
- 		return m
 
- 	} else if m == 0 {
 
- 		return n
 
- 	}
 
- 	v0 := make([]int, m+1, m+1)
 
- 	v1 := make([]int, m+1, m+1)
 
- 	for i := 0; i <= m; i++ {
 
- 		v0[i] = i
 
- 	}
 
- 	var cost int
 
- 	for i := 0; i < n; i++ {
 
- 		v1[0] = i + 1
 
- 		for j := 0; j < m; j++ {
 
- 			if rslice1[i] == rslice2[j] {
 
- 				cost = 0
 
- 			} else {
 
- 				cost = 1
 
- 			}
 
- 			v1[j+1] = min3(v1[j]+1, v0[j+1]+1, v0[j]+cost)
 
- 		}
 
- 		v0, v1 = v1, v0
 
- 	}
 
- 	return v0[m]
 
- }
 
- /*
 
- 3 way min for computing the Levenshtein distance.
 
- */
 
- func min3(a, b, c int) int {
 
- 	ret := a
 
- 	if b < ret {
 
- 		ret = b
 
- 	}
 
- 	if c < ret {
 
- 		ret = c
 
- 	}
 
- 	return ret
 
- }
 
- /*
 
- VersionStringCompare compares two version strings. Returns: 0 if the strings are
 
- equal; -1 if the first string is smaller; 1 if the first string is greater.
 
- */
 
- func VersionStringCompare(str1, str2 string) int {
 
- 	val1 := strings.Split(str1, ".")
 
- 	val2 := strings.Split(str2, ".")
 
- 	idx := 0
 
- 	for idx < len(val1) && idx < len(val2) && val1[idx] == val2[idx] {
 
- 		idx++
 
- 	}
 
- 	switch {
 
- 	case idx < len(val1) && idx < len(val2):
 
- 		return versionStringPartCompare(val1[idx], val2[idx])
 
- 	case len(val1) > len(val2):
 
- 		return 1
 
- 	case len(val1) < len(val2):
 
- 		return -1
 
- 	}
 
- 	return 0
 
- }
 
- /*
 
- versionStringPartCompare compares two version string parts. Returns: 0 if the
 
- strings are equal; -1 if the first string is smaller; 1 if the first string is
 
- greater.
 
- */
 
- func versionStringPartCompare(str1, str2 string) int {
 
- 	pat := regexp.MustCompile("^([0-9]+)([\\D].*)?")
 
- 	res1 := pat.FindStringSubmatch(str1)
 
- 	res2 := pat.FindStringSubmatch(str2)
 
- 	switch {
 
- 	case res1 == nil && res2 == nil:
 
- 		return strings.Compare(str1, str2)
 
- 	case res1 == nil && res2 != nil:
 
- 		return -1
 
- 	case res1 != nil && res2 == nil:
 
- 		return 1
 
- 	}
 
- 	v1, _ := strconv.Atoi(res1[1])
 
- 	v2, _ := strconv.Atoi(res2[1])
 
- 	res := 0
 
- 	switch {
 
- 	case v1 > v2:
 
- 		res = 1
 
- 	case v1 < v2:
 
- 		res = -1
 
- 	}
 
- 	if res == 0 {
 
- 		switch {
 
- 		case res1[2] != "" && res2[2] == "":
 
- 			return 1
 
- 		case res1[2] == "" && res2[2] != "":
 
- 			return -1
 
- 		case res1[2] != "" && res2[2] != "":
 
- 			return strings.Compare(res1[2], res2[2])
 
- 		}
 
- 	}
 
- 	return res
 
- }
 
- /*
 
- IsAlphaNumeric checks if a string contains only alpha numerical characters or "_".
 
- */
 
- func IsAlphaNumeric(str string) bool {
 
- 	ret, _ := regexp.MatchString("^[a-zA-Z0-9_]*$", str)
 
- 	return ret
 
- }
 
- /*
 
- IsTrueValue checks if a given string is a true value.
 
- */
 
- func IsTrueValue(str string) bool {
 
- 	str = strings.ToLower(str)
 
- 	return str == "true" || str == "yes" || str == "on" || str == "ok" ||
 
- 		str == "1" || str == "active" || str == "enabled"
 
- }
 
- /*
 
- IndexOf returns the index of str in slice or -1 if it does not exist.
 
- */
 
- func IndexOf(str string, slice []string) int {
 
- 	for i, s := range slice {
 
- 		if str == s {
 
- 			return i
 
- 		}
 
- 	}
 
- 	return -1
 
- }
 
- /*
 
- MapKeys returns the keys of a map as a sorted list.
 
- */
 
- func MapKeys(m map[string]interface{}) []string {
 
- 	ret := make([]string, 0, len(m))
 
- 	for k := range m {
 
- 		ret = append(ret, k)
 
- 	}
 
- 	sort.Strings(ret)
 
- 	return ret
 
- }
 
- /*
 
- GenerateRollingString creates a string by repeating a given string pattern.
 
- */
 
- func GenerateRollingString(seq string, size int) string {
 
- 	var buf bytes.Buffer
 
- 	rs := StringToRuneSlice(seq)
 
- 	l := len(rs)
 
- 	if l == 0 {
 
- 		return ""
 
- 	}
 
- 	for i := 0; i < size; i++ {
 
- 		buf.WriteRune(rs[i%l])
 
- 	}
 
- 	return buf.String()
 
- }
 
- /*
 
- ConvertToString tries to convert a given object into a stable string. This
 
- function can be used to display nested maps.
 
- */
 
- func ConvertToString(v interface{}) string {
 
- 	if vStringer, ok := v.(fmt.Stringer); ok {
 
- 		return vStringer.String()
 
- 	}
 
- 	if _, err := json.Marshal(v); err != nil {
 
- 		v = containerStringConvert(v)
 
- 	}
 
- 	if vString, ok := v.(string); ok {
 
- 		return vString
 
- 	} else if res, err := json.Marshal(v); err == nil {
 
- 		return string(res)
 
- 	}
 
- 	return fmt.Sprint(v)
 
- }
 
- /*
 
- ConvertToPrettyString tries to convert a given object into a stable human-readable
 
- string.
 
- */
 
- func ConvertToPrettyString(v interface{}) string {
 
- 	var res []byte
 
- 	var err error
 
- 	if res, err = json.MarshalIndent(v, "", "  "); err != nil {
 
- 		if res, err = json.MarshalIndent(containerStringConvert(v), "", "  "); err != nil {
 
- 			res = []byte(fmt.Sprint(v))
 
- 		}
 
- 	}
 
- 	return string(res)
 
- }
 
- /*
 
- containerStringConvert converts container contents into strings.
 
- */
 
- func containerStringConvert(v interface{}) interface{} {
 
- 	res := v
 
- 	if mapContainer, ok := v.(map[interface{}]interface{}); ok {
 
- 		newRes := make(map[string]interface{})
 
- 		for mk, mv := range mapContainer {
 
- 			newRes[ConvertToString(mk)] = containerStringConvert(mv)
 
- 		}
 
- 		res = newRes
 
- 	} else if mapList, ok := v.([]interface{}); ok {
 
- 		newRes := make([]interface{}, len(mapList))
 
- 		for i, lv := range mapList {
 
- 			newRes[i] = containerStringConvert(lv)
 
- 		}
 
- 		res = newRes
 
- 	}
 
- 	return res
 
- }
 
- /*
 
- MD5HexString calculates the MD5 sum of a string and returns it as hex string.
 
- */
 
- func MD5HexString(str string) string {
 
- 	return fmt.Sprintf("%x", md5.Sum([]byte(str)))
 
- }
 
- /*
 
- LengthConstantEquals compares two strings in length-constant time. This
 
- function is deliberately inefficient in that it does not stop at the earliest
 
- possible time. This is to prevent timing attacks when comparing password
 
- hashes.
 
- */
 
- func LengthConstantEquals(str1 []byte, str2 []byte) bool {
 
- 	diff := len(str1) ^ len(str2)
 
- 	for i := 0; i < len(str1) && i < len(str2); i++ {
 
- 		diff |= int(str1[i] ^ str2[i])
 
- 	}
 
- 	return diff == 0
 
- }
 
 
  |