2014-02-27 18:32:09 -05:00
|
|
|
// Copyright © 2014 Steve Francia <spf@spf13.com>.
|
|
|
|
//
|
|
|
|
// Licensed under the Simple Public License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
// http://opensource.org/licenses/Simple-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package helpers
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2014-10-16 20:20:09 -04:00
|
|
|
"crypto/md5"
|
|
|
|
"encoding/hex"
|
2014-12-26 15:18:26 -05:00
|
|
|
"errors"
|
2014-05-15 15:07:46 -04:00
|
|
|
"fmt"
|
2015-02-11 14:24:56 -05:00
|
|
|
bp "github.com/spf13/hugo/bufferpool"
|
2015-03-12 11:10:14 -04:00
|
|
|
jww "github.com/spf13/jwalterweatherman"
|
2015-02-11 14:24:56 -05:00
|
|
|
"github.com/spf13/viper"
|
2014-10-16 20:20:09 -04:00
|
|
|
"io"
|
2014-05-15 15:07:46 -04:00
|
|
|
"net"
|
2014-12-07 13:48:00 -05:00
|
|
|
"path/filepath"
|
2014-12-26 15:18:26 -05:00
|
|
|
"reflect"
|
2014-02-27 18:32:09 -05:00
|
|
|
"strings"
|
2015-03-12 11:10:14 -04:00
|
|
|
"sync"
|
2014-02-27 18:32:09 -05:00
|
|
|
)
|
|
|
|
|
2014-12-26 10:07:03 -05:00
|
|
|
// Filepath separator defined by os.Separator.
|
2014-12-07 13:48:00 -05:00
|
|
|
const FilePathSeparator = string(filepath.Separator)
|
|
|
|
|
2015-03-06 18:02:06 -05:00
|
|
|
// FindAvailablePort returns an available and valid TCP port.
|
2014-05-15 15:07:46 -04:00
|
|
|
func FindAvailablePort() (*net.TCPAddr, error) {
|
|
|
|
l, err := net.Listen("tcp", ":0")
|
|
|
|
if err == nil {
|
|
|
|
defer l.Close()
|
|
|
|
addr := l.Addr()
|
|
|
|
if a, ok := addr.(*net.TCPAddr); ok {
|
|
|
|
return a, nil
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("Unable to obtain a valid tcp port. %v", addr)
|
|
|
|
}
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-10-16 20:20:09 -04:00
|
|
|
|
2014-12-26 10:07:03 -05:00
|
|
|
// InStringArray checks if a string is an element of a slice of strings
|
|
|
|
// and returns a boolean value.
|
2014-10-20 20:15:33 -04:00
|
|
|
func InStringArray(arr []string, el string) bool {
|
|
|
|
for _, v := range arr {
|
|
|
|
if v == el {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2014-12-11 15:57:25 -05:00
|
|
|
// GuessType attempts to guess the type of file from a given string.
|
2014-10-16 20:20:09 -04:00
|
|
|
func GuessType(in string) string {
|
|
|
|
switch strings.ToLower(in) {
|
|
|
|
case "md", "markdown", "mdown":
|
|
|
|
return "markdown"
|
|
|
|
case "rst":
|
|
|
|
return "rst"
|
|
|
|
case "html", "htm":
|
|
|
|
return "html"
|
|
|
|
}
|
|
|
|
|
|
|
|
return "unknown"
|
|
|
|
}
|
|
|
|
|
2014-12-26 10:07:03 -05:00
|
|
|
// ReaderToBytes takes an io.Reader argument, reads from it
|
|
|
|
// and returns bytes.
|
2014-10-16 20:20:09 -04:00
|
|
|
func ReaderToBytes(lines io.Reader) []byte {
|
2015-03-12 15:50:44 -04:00
|
|
|
if lines == nil {
|
|
|
|
return []byte{}
|
|
|
|
}
|
2015-01-30 14:12:07 -05:00
|
|
|
b := bp.GetBuffer()
|
|
|
|
defer bp.PutBuffer(b)
|
|
|
|
|
2014-10-16 20:20:09 -04:00
|
|
|
b.ReadFrom(lines)
|
2015-01-30 14:12:07 -05:00
|
|
|
|
|
|
|
bc := make([]byte, b.Len(), b.Len())
|
|
|
|
copy(bc, b.Bytes())
|
|
|
|
return bc
|
2014-10-16 20:20:09 -04:00
|
|
|
}
|
|
|
|
|
2014-12-26 10:07:03 -05:00
|
|
|
// ReaderToString is the same as ReaderToBytes, but returns a string.
|
2014-11-04 00:26:56 -05:00
|
|
|
func ReaderToString(lines io.Reader) string {
|
2015-03-12 15:50:44 -04:00
|
|
|
if lines == nil {
|
|
|
|
return ""
|
|
|
|
}
|
2015-01-30 14:12:07 -05:00
|
|
|
b := bp.GetBuffer()
|
|
|
|
defer bp.PutBuffer(b)
|
2014-11-04 00:26:56 -05:00
|
|
|
b.ReadFrom(lines)
|
|
|
|
return b.String()
|
|
|
|
}
|
|
|
|
|
2014-12-26 10:07:03 -05:00
|
|
|
// StringToReader does the opposite of ReaderToString.
|
2014-11-04 00:26:56 -05:00
|
|
|
func StringToReader(in string) io.Reader {
|
|
|
|
return strings.NewReader(in)
|
|
|
|
}
|
|
|
|
|
2014-12-26 10:07:03 -05:00
|
|
|
// BytesToReader does the opposite of ReaderToBytes.
|
2014-11-04 00:26:56 -05:00
|
|
|
func BytesToReader(in []byte) io.Reader {
|
|
|
|
return bytes.NewReader(in)
|
|
|
|
}
|
|
|
|
|
2015-03-06 18:02:06 -05:00
|
|
|
// ThemeSet checks whether a theme is in use or not.
|
2015-02-11 14:24:56 -05:00
|
|
|
func ThemeSet() bool {
|
|
|
|
return viper.GetString("theme") != ""
|
|
|
|
}
|
|
|
|
|
2015-03-12 11:10:14 -04:00
|
|
|
// Avoid spamming the logs with errors
|
|
|
|
var deprecatedLogs = struct {
|
|
|
|
sync.RWMutex
|
|
|
|
m map[string]bool
|
|
|
|
}{m: make(map[string]bool)}
|
|
|
|
|
|
|
|
func Deprecated(object, item, alternative string) {
|
2015-03-12 13:51:31 -04:00
|
|
|
key := object + item + alternative
|
2015-03-12 11:10:14 -04:00
|
|
|
deprecatedLogs.RLock()
|
2015-03-12 13:51:31 -04:00
|
|
|
logged := deprecatedLogs.m[key]
|
2015-03-12 11:10:14 -04:00
|
|
|
deprecatedLogs.RUnlock()
|
|
|
|
if logged {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
deprecatedLogs.Lock()
|
2015-03-12 13:51:31 -04:00
|
|
|
if !deprecatedLogs.m[key] {
|
|
|
|
jww.ERROR.Printf("%s's %s is deprecated and will be removed in Hugo 0.15. Use %s instead.", object, item, alternative)
|
|
|
|
deprecatedLogs.m[key] = true
|
|
|
|
}
|
2015-03-12 11:10:14 -04:00
|
|
|
deprecatedLogs.Unlock()
|
|
|
|
}
|
|
|
|
|
2014-12-26 10:07:03 -05:00
|
|
|
// SliceToLower goes through the source slice and lowers all values.
|
2014-10-16 20:20:09 -04:00
|
|
|
func SliceToLower(s []string) []string {
|
|
|
|
if s == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
l := make([]string, len(s))
|
|
|
|
for i, v := range s {
|
|
|
|
l[i] = strings.ToLower(v)
|
|
|
|
}
|
|
|
|
|
|
|
|
return l
|
|
|
|
}
|
|
|
|
|
2014-12-26 10:07:03 -05:00
|
|
|
// Md5String takes a string and returns its MD5 hash.
|
2014-10-16 20:20:09 -04:00
|
|
|
func Md5String(f string) string {
|
|
|
|
h := md5.New()
|
|
|
|
h.Write([]byte(f))
|
|
|
|
return hex.EncodeToString(h.Sum([]byte{}))
|
|
|
|
}
|
2014-12-26 15:18:26 -05:00
|
|
|
|
|
|
|
// DoArithmetic performs arithmetic operations (+,-,*,/) using reflection to
|
|
|
|
// determine the type of the two terms.
|
|
|
|
func DoArithmetic(a, b interface{}, op rune) (interface{}, error) {
|
|
|
|
av := reflect.ValueOf(a)
|
|
|
|
bv := reflect.ValueOf(b)
|
|
|
|
var ai, bi int64
|
|
|
|
var af, bf float64
|
|
|
|
var au, bu uint64
|
|
|
|
switch av.Kind() {
|
|
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
|
|
ai = av.Int()
|
|
|
|
switch bv.Kind() {
|
|
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
|
|
bi = bv.Int()
|
|
|
|
case reflect.Float32, reflect.Float64:
|
|
|
|
af = float64(ai) // may overflow
|
|
|
|
ai = 0
|
|
|
|
bf = bv.Float()
|
|
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
|
|
bu = bv.Uint()
|
|
|
|
if ai >= 0 {
|
|
|
|
au = uint64(ai)
|
|
|
|
ai = 0
|
|
|
|
} else {
|
|
|
|
bi = int64(bu) // may overflow
|
|
|
|
bu = 0
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return nil, errors.New("Can't apply the operator to the values")
|
|
|
|
}
|
|
|
|
case reflect.Float32, reflect.Float64:
|
|
|
|
af = av.Float()
|
|
|
|
switch bv.Kind() {
|
|
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
|
|
bf = float64(bv.Int()) // may overflow
|
|
|
|
case reflect.Float32, reflect.Float64:
|
|
|
|
bf = bv.Float()
|
|
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
|
|
bf = float64(bv.Uint()) // may overflow
|
|
|
|
default:
|
|
|
|
return nil, errors.New("Can't apply the operator to the values")
|
|
|
|
}
|
|
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
|
|
au = av.Uint()
|
|
|
|
switch bv.Kind() {
|
|
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
|
|
bi = bv.Int()
|
|
|
|
if bi >= 0 {
|
|
|
|
bu = uint64(bi)
|
|
|
|
bi = 0
|
|
|
|
} else {
|
|
|
|
ai = int64(au) // may overflow
|
|
|
|
au = 0
|
|
|
|
}
|
|
|
|
case reflect.Float32, reflect.Float64:
|
|
|
|
af = float64(au) // may overflow
|
|
|
|
au = 0
|
|
|
|
bf = bv.Float()
|
|
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
|
|
bu = bv.Uint()
|
|
|
|
default:
|
|
|
|
return nil, errors.New("Can't apply the operator to the values")
|
|
|
|
}
|
|
|
|
case reflect.String:
|
|
|
|
as := av.String()
|
|
|
|
if bv.Kind() == reflect.String && op == '+' {
|
|
|
|
bs := bv.String()
|
|
|
|
return as + bs, nil
|
|
|
|
}
|
2015-03-06 18:02:06 -05:00
|
|
|
return nil, errors.New("Can't apply the operator to the values")
|
2014-12-26 15:18:26 -05:00
|
|
|
default:
|
|
|
|
return nil, errors.New("Can't apply the operator to the values")
|
|
|
|
}
|
|
|
|
|
|
|
|
switch op {
|
|
|
|
case '+':
|
|
|
|
if ai != 0 || bi != 0 {
|
|
|
|
return ai + bi, nil
|
|
|
|
} else if af != 0 || bf != 0 {
|
|
|
|
return af + bf, nil
|
|
|
|
} else if au != 0 || bu != 0 {
|
|
|
|
return au + bu, nil
|
|
|
|
} else {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
case '-':
|
|
|
|
if ai != 0 || bi != 0 {
|
|
|
|
return ai - bi, nil
|
|
|
|
} else if af != 0 || bf != 0 {
|
|
|
|
return af - bf, nil
|
|
|
|
} else if au != 0 || bu != 0 {
|
|
|
|
return au - bu, nil
|
|
|
|
} else {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
case '*':
|
|
|
|
if ai != 0 || bi != 0 {
|
|
|
|
return ai * bi, nil
|
|
|
|
} else if af != 0 || bf != 0 {
|
|
|
|
return af * bf, nil
|
|
|
|
} else if au != 0 || bu != 0 {
|
|
|
|
return au * bu, nil
|
|
|
|
} else {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
case '/':
|
|
|
|
if bi != 0 {
|
|
|
|
return ai / bi, nil
|
|
|
|
} else if bf != 0 {
|
|
|
|
return af / bf, nil
|
|
|
|
} else if bu != 0 {
|
|
|
|
return au / bu, nil
|
|
|
|
} else {
|
|
|
|
return nil, errors.New("Can't divide the value by 0")
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return nil, errors.New("There is no such an operation")
|
|
|
|
}
|
|
|
|
}
|