summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--helpers/general.go36
-rw-r--r--helpers/general_test.go97
-rw-r--r--helpers/path.go18
-rw-r--r--tpl/template.go22
4 files changed, 163 insertions, 10 deletions
diff --git a/helpers/general.go b/helpers/general.go
index 3e0a85fbc..baf957d75 100644
--- a/helpers/general.go
+++ b/helpers/general.go
@@ -112,6 +112,42 @@ func BytesToReader(in []byte) io.Reader {
return bytes.NewReader(in)
}
+// ReaderContains reports whether subslice is within r.
+func ReaderContains(r io.Reader, subslice []byte) bool {
+
+ if len(subslice) == 0 {
+ return false
+ }
+
+ bufflen := len(subslice) * 4
+ halflen := bufflen / 2
+ buff := make([]byte, bufflen)
+ var err error
+ var n, i int
+
+ for {
+ i++
+ if i == 1 {
+ n, err = io.ReadAtLeast(r, buff[:halflen], halflen)
+ } else {
+ if i != 2 {
+ // shift left to catch overlapping matches
+ copy(buff[:], buff[halflen:])
+ }
+ n, err = io.ReadAtLeast(r, buff[halflen:], halflen)
+ }
+
+ if n > 0 && bytes.Contains(buff, subslice) {
+ return true
+ }
+
+ if err != nil {
+ break
+ }
+ }
+ return false
+}
+
// ThemeSet checks whether a theme is in use or not.
func ThemeSet() bool {
return viper.GetString("theme") != ""
diff --git a/helpers/general_test.go b/helpers/general_test.go
index b5706a445..496439db1 100644
--- a/helpers/general_test.go
+++ b/helpers/general_test.go
@@ -1,7 +1,9 @@
package helpers
import (
+ "bytes"
"github.com/stretchr/testify/assert"
+ "io/ioutil"
"reflect"
"strings"
"testing"
@@ -44,6 +46,101 @@ func TestStringToReader(t *testing.T) {
assert.Equal(t, asString, ReaderToString(asReader))
}
+var containsTestText = (`На берегу пустынных волн
+Стоял он, дум великих полн,
+И вдаль глядел. Пред ним широко
+Река неслася; бедный чёлн
+По ней стремился одиноко.
+По мшистым, топким берегам
+Чернели избы здесь и там,
+Приют убогого чухонца;
+И лес, неведомый лучам
+В тумане спрятанного солнца,
+Кругом шумел.
+
+Τη γλώσσα μου έδωσαν ελληνική
+το σπίτι φτωχικό στις αμμουδιές του Ομήρου.
+Μονάχη έγνοια η γλώσσα μου στις αμμουδιές του Ομήρου.
+
+από το Άξιον Εστί
+του Οδυσσέα Ελύτη
+
+Sîne klâwen durh die wolken sint geslagen,
+er stîget ûf mit grôzer kraft,
+ich sih in grâwen tägelîch als er wil tagen,
+den tac, der im geselleschaft
+erwenden wil, dem werden man,
+den ich mit sorgen în verliez.
+ich bringe in hinnen, ob ich kan.
+sîn vil manegiu tugent michz leisten hiez.
+`)
+
+var containsBenchTestData = []struct {
+ v1 string
+ v2 []byte
+ expect bool
+}{
+ {"abc", []byte("a"), true},
+ {"abc", []byte("b"), true},
+ {"abcdefg", []byte("efg"), true},
+ {"abc", []byte("d"), false},
+ {containsTestText, []byte("стремился"), true},
+ {containsTestText, []byte(containsTestText[10:80]), true},
+ {containsTestText, []byte(containsTestText[100:110]), true},
+ {containsTestText, []byte(containsTestText[len(containsTestText)-100 : len(containsTestText)-10]), true},
+ {containsTestText, []byte(containsTestText[len(containsTestText)-20:]), true},
+ {containsTestText, []byte("notfound"), false},
+}
+
+// some corner cases
+var containsAdditionalTestData = []struct {
+ v1 string
+ v2 []byte
+ expect bool
+}{
+ {"", []byte("a"), false},
+ {"a", []byte(""), false},
+ {"", []byte(""), false},
+}
+
+func TestReaderContains(t *testing.T) {
+ for i, this := range append(containsBenchTestData, containsAdditionalTestData...) {
+ result := ReaderContains(StringToReader(this.v1), this.v2)
+ if result != this.expect {
+ t.Errorf("[%d] Got %t but expected %t", i, result, this.expect)
+ }
+ }
+}
+
+func BenchmarkReaderContains(b *testing.B) {
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ for i, this := range containsBenchTestData {
+ result := ReaderContains(StringToReader(this.v1), this.v2)
+ if result != this.expect {
+ b.Errorf("[%d] Got %t but expected %t", i, result, this.expect)
+ }
+ }
+ }
+}
+
+// kept to compare the above
+func _BenchmarkReaderContains(b *testing.B) {
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ for i, this := range containsBenchTestData {
+ bs, err := ioutil.ReadAll(StringToReader(this.v1))
+ if err != nil {
+ b.Fatalf("Failed %s", err)
+ }
+ result := bytes.Contains(bs, this.v2)
+ if result != this.expect {
+ b.Errorf("[%d] Got %t but expected %t", i, result, this.expect)
+ }
+ }
+ }
+}
+
func TestFindAvailablePort(t *testing.T) {
addr, err := FindAvailablePort()
assert.Nil(t, err)
diff --git a/helpers/path.go b/helpers/path.go
index 91419e0c9..546f23d36 100644
--- a/helpers/path.go
+++ b/helpers/path.go
@@ -16,16 +16,15 @@ package helpers
import (
"errors"
"fmt"
+ "github.com/spf13/afero"
+ jww "github.com/spf13/jwalterweatherman"
+ "github.com/spf13/viper"
"io"
"os"
"path/filepath"
"regexp"
"strings"
"unicode"
-
- "github.com/spf13/afero"
- jww "github.com/spf13/jwalterweatherman"
- "github.com/spf13/viper"
)
// FilepathPathBridge is a bridge for common functionality in filepath vs path
@@ -153,6 +152,17 @@ func IsEmpty(path string, fs afero.Fs) (bool, error) {
return fi.Size() == 0, nil
}
+// Check if a file contains a specified string.
+func FileContains(filename string, subslice []byte, fs afero.Fs) (bool, error) {
+ f, err := os.Open(filename)
+ if err != nil {
+ return false, err
+ }
+ defer f.Close()
+
+ return ReaderContains(f, subslice), nil
+}
+
// Check if a file or directory exists.
func Exists(path string, fs afero.Fs) (bool, error) {
_, err := fs.Stat(path)
diff --git a/tpl/template.go b/tpl/template.go
index 171d61825..2db7c4f79 100644
--- a/tpl/template.go
+++ b/tpl/template.go
@@ -1349,6 +1349,8 @@ func isBackupFile(path string) bool {
const baseAceFilename = "baseof.ace"
+var aceTemplateInnerMarker = []byte("= content")
+
func isBaseTemplate(path string) bool {
return strings.HasSuffix(path, baseAceFilename)
}
@@ -1391,14 +1393,22 @@ func (t *GoHTMLTemplate) loadTemplates(absPath string, prefix string) {
// ACE templates may have both a base and inner template.
if filepath.Ext(path) == ".ace" && !strings.HasSuffix(filepath.Dir(path), "partials") {
- // Look for the base first in the current path, then in _default.
- p := filepath.Join(filepath.Dir(path), baseAceFilename)
- if ok, err := helpers.Exists(p, hugofs.OsFs); err == nil && ok {
- baseTemplatePath = p
- } else {
- p := filepath.Join(absPath, "_default", baseAceFilename)
+ // This may be a view that shouldn't have base template
+ // Have to look inside it to make sure
+ needsBase, err := helpers.FileContains(path, aceTemplateInnerMarker, hugofs.OsFs)
+ if err != nil {
+ return err
+ }
+ if needsBase {
+ // Look for the base first in the current path, then in _default.
+ p := filepath.Join(filepath.Dir(path), baseAceFilename)
if ok, err := helpers.Exists(p, hugofs.OsFs); err == nil && ok {
baseTemplatePath = p
+ } else {
+ p := filepath.Join(absPath, "_default", baseAceFilename)
+ if ok, err := helpers.Exists(p, hugofs.OsFs); err == nil && ok {
+ baseTemplatePath = p
+ }
}
}
}