Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/gohugoio/hugo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/markup
diff options
context:
space:
mode:
Diffstat (limited to 'markup')
-rw-r--r--markup/asciidocext/convert.go36
-rw-r--r--markup/asciidocext/convert_test.go50
-rw-r--r--markup/converter/converter.go2
-rw-r--r--markup/internal/external.go54
-rw-r--r--markup/pandoc/convert.go36
-rw-r--r--markup/pandoc/convert_test.go6
-rw-r--r--markup/rst/convert.go58
-rw-r--r--markup/rst/convert_test.go11
8 files changed, 151 insertions, 102 deletions
diff --git a/markup/asciidocext/convert.go b/markup/asciidocext/convert.go
index ff843cb6e..4c83e0e95 100644
--- a/markup/asciidocext/convert.go
+++ b/markup/asciidocext/convert.go
@@ -21,10 +21,9 @@ import (
"path/filepath"
"strings"
+ "github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/htesting"
- "github.com/cli/safeexec"
-
"github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/markup/asciidocext/asciidocext_config"
"github.com/gohugoio/hugo/markup/converter"
@@ -67,7 +66,11 @@ type asciidocConverter struct {
}
func (a *asciidocConverter) Convert(ctx converter.RenderContext) (converter.Result, error) {
- content, toc, err := a.extractTOC(a.getAsciidocContent(ctx.Src, a.ctx))
+ b, err := a.getAsciidocContent(ctx.Src, a.ctx)
+ if err != nil {
+ return nil, err
+ }
+ content, toc, err := a.extractTOC(b)
if err != nil {
return nil, err
}
@@ -83,20 +86,19 @@ func (a *asciidocConverter) Supports(_ identity.Identity) bool {
// getAsciidocContent calls asciidoctor as an external helper
// to convert AsciiDoc content to HTML.
-func (a *asciidocConverter) getAsciidocContent(src []byte, ctx converter.DocumentContext) []byte {
- path := getAsciidoctorExecPath()
- if path == "" {
+func (a *asciidocConverter) getAsciidocContent(src []byte, ctx converter.DocumentContext) ([]byte, error) {
+ if !hasAsciiDoc() {
a.cfg.Logger.Errorln("asciidoctor not found in $PATH: Please install.\n",
" Leaving AsciiDoc content unrendered.")
- return src
+ return src, nil
}
args := a.parseArgs(ctx)
args = append(args, "-")
- a.cfg.Logger.Infoln("Rendering", ctx.DocumentName, "with", path, "using asciidoctor args", args, "...")
+ a.cfg.Logger.Infoln("Rendering", ctx.DocumentName, " using asciidoctor args", args, "...")
- return internal.ExternallyRenderContent(a.cfg, ctx, src, path, args)
+ return internal.ExternallyRenderContent(a.cfg, ctx, src, asciiDocBinaryName, args)
}
func (a *asciidocConverter) parseArgs(ctx converter.DocumentContext) []string {
@@ -195,12 +197,10 @@ func (a *asciidocConverter) appendArg(args []string, option, value, defaultValue
return args
}
-func getAsciidoctorExecPath() string {
- path, err := safeexec.LookPath("asciidoctor")
- if err != nil {
- return ""
- }
- return path
+const asciiDocBinaryName = "asciidoctor"
+
+func hasAsciiDoc() bool {
+ return hexec.InPath(asciiDocBinaryName)
}
// extractTOC extracts the toc from the given src html.
@@ -311,8 +311,12 @@ func nodeContent(node *html.Node) string {
// Supports returns whether Asciidoctor is installed on this computer.
func Supports() bool {
+ hasBin := hasAsciiDoc()
if htesting.SupportsAll() {
+ if !hasBin {
+ panic("asciidoctor not installed")
+ }
return true
}
- return getAsciidoctorExecPath() != ""
+ return hasBin
}
diff --git a/markup/asciidocext/convert_test.go b/markup/asciidocext/convert_test.go
index acc525c3b..3a350c5ce 100644
--- a/markup/asciidocext/convert_test.go
+++ b/markup/asciidocext/convert_test.go
@@ -21,8 +21,10 @@ import (
"path/filepath"
"testing"
+ "github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/config"
+ "github.com/gohugoio/hugo/config/security"
"github.com/gohugoio/hugo/markup/converter"
"github.com/gohugoio/hugo/markup/markup_config"
"github.com/gohugoio/hugo/markup/tableofcontents"
@@ -280,20 +282,28 @@ func TestAsciidoctorAttributes(t *testing.T) {
c.Assert(args[4], qt.Equals, "--no-header-footer")
}
-func TestConvert(t *testing.T) {
- if !Supports() {
- t.Skip("asciidoctor not installed")
- }
- c := qt.New(t)
+func getProvider(c *qt.C, mconf markup_config.Config) converter.Provider {
+ sc := security.DefaultConfig
+ sc.Exec.Allow = security.NewWhitelist("asciidoctor")
- mconf := markup_config.Default
p, err := Provider.New(
converter.ProviderConfig{
MarkupConfig: mconf,
Logger: loggers.NewErrorLogger(),
+ Exec: hexec.New(sc),
},
)
c.Assert(err, qt.IsNil)
+ return p
+}
+
+func TestConvert(t *testing.T) {
+ if !Supports() {
+ t.Skip("asciidoctor not installed")
+ }
+ c := qt.New(t)
+
+ p := getProvider(c, markup_config.Default)
conv, err := p.New(converter.DocumentContext{})
c.Assert(err, qt.IsNil)
@@ -308,14 +318,8 @@ func TestTableOfContents(t *testing.T) {
t.Skip("asciidoctor not installed")
}
c := qt.New(t)
- mconf := markup_config.Default
- p, err := Provider.New(
- converter.ProviderConfig{
- MarkupConfig: mconf,
- Logger: loggers.NewErrorLogger(),
- },
- )
- c.Assert(err, qt.IsNil)
+ p := getProvider(c, markup_config.Default)
+
conv, err := p.New(converter.DocumentContext{})
c.Assert(err, qt.IsNil)
r, err := conv.Convert(converter.RenderContext{Src: []byte(`:toc: macro
@@ -390,14 +394,7 @@ func TestTableOfContentsWithCode(t *testing.T) {
t.Skip("asciidoctor not installed")
}
c := qt.New(t)
- mconf := markup_config.Default
- p, err := Provider.New(
- converter.ProviderConfig{
- MarkupConfig: mconf,
- Logger: loggers.NewErrorLogger(),
- },
- )
- c.Assert(err, qt.IsNil)
+ p := getProvider(c, markup_config.Default)
conv, err := p.New(converter.DocumentContext{})
c.Assert(err, qt.IsNil)
r, err := conv.Convert(converter.RenderContext{Src: []byte(`:toc: auto
@@ -433,13 +430,8 @@ func TestTableOfContentsPreserveTOC(t *testing.T) {
c := qt.New(t)
mconf := markup_config.Default
mconf.AsciidocExt.PreserveTOC = true
- p, err := Provider.New(
- converter.ProviderConfig{
- MarkupConfig: mconf,
- Logger: loggers.NewErrorLogger(),
- },
- )
- c.Assert(err, qt.IsNil)
+ p := getProvider(c, mconf)
+
conv, err := p.New(converter.DocumentContext{})
c.Assert(err, qt.IsNil)
r, err := conv.Convert(converter.RenderContext{Src: []byte(`:toc:
diff --git a/markup/converter/converter.go b/markup/converter/converter.go
index 3fa3bea39..180208a7b 100644
--- a/markup/converter/converter.go
+++ b/markup/converter/converter.go
@@ -16,6 +16,7 @@ package converter
import (
"bytes"
+ "github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/identity"
@@ -32,6 +33,7 @@ type ProviderConfig struct {
Cfg config.Provider // Site config
ContentFs afero.Fs
Logger loggers.Logger
+ Exec *hexec.Exec
Highlight func(code, lang, optsStr string) (string, error)
}
diff --git a/markup/internal/external.go b/markup/internal/external.go
index 0937afa34..97cf5cc7d 100644
--- a/markup/internal/external.go
+++ b/markup/internal/external.go
@@ -2,42 +2,56 @@ package internal
import (
"bytes"
+ "fmt"
"strings"
- "github.com/cli/safeexec"
+ "github.com/gohugoio/hugo/common/collections"
"github.com/gohugoio/hugo/common/hexec"
-
"github.com/gohugoio/hugo/markup/converter"
)
func ExternallyRenderContent(
cfg converter.ProviderConfig,
ctx converter.DocumentContext,
- content []byte, path string, args []string) []byte {
+ content []byte, binaryName string, args []string) ([]byte, error) {
logger := cfg.Logger
- cmd, err := hexec.SafeCommand(path, args...)
- if err != nil {
- logger.Errorf("%s rendering %s: %v", path, ctx.DocumentName, err)
- return nil
+
+ if strings.Contains(binaryName, "/") {
+ panic(fmt.Sprintf("should be no slash in %q", binaryName))
}
- cmd.Stdin = bytes.NewReader(content)
+
+ argsv := collections.StringSliceToInterfaceSlice(args)
+
var out, cmderr bytes.Buffer
- cmd.Stdout = &out
- cmd.Stderr = &cmderr
+ argsv = append(argsv, hexec.WithStdout(&out))
+ argsv = append(argsv, hexec.WithStderr(&cmderr))
+ argsv = append(argsv, hexec.WithStdin(bytes.NewReader(content)))
+
+ cmd, err := cfg.Exec.New(binaryName, argsv...)
+ if err != nil {
+ return nil, err
+ }
+
err = cmd.Run()
+
// Most external helpers exit w/ non-zero exit code only if severe, i.e.
// halting errors occurred. -> log stderr output regardless of state of err
for _, item := range strings.Split(cmderr.String(), "\n") {
item := strings.TrimSpace(item)
if item != "" {
- logger.Errorf("%s: %s", ctx.DocumentName, item)
+ if err == nil {
+ logger.Warnf("%s: %s", ctx.DocumentName, item)
+ } else {
+ logger.Errorf("%s: %s", ctx.DocumentName, item)
+ }
}
}
+
if err != nil {
- logger.Errorf("%s rendering %s: %v", path, ctx.DocumentName, err)
+ logger.Errorf("%s rendering %s: %v", binaryName, ctx.DocumentName, err)
}
- return normalizeExternalHelperLineFeeds(out.Bytes())
+ return normalizeExternalHelperLineFeeds(out.Bytes()), nil
}
// Strips carriage returns from third-party / external processes (useful for Windows)
@@ -45,13 +59,13 @@ func normalizeExternalHelperLineFeeds(content []byte) []byte {
return bytes.Replace(content, []byte("\r"), []byte(""), -1)
}
-func GetPythonExecPath() string {
- path, err := safeexec.LookPath("python")
- if err != nil {
- path, err = safeexec.LookPath("python.exe")
- if err != nil {
- return ""
+var pythonBinaryCandidates = []string{"python", "python.exe"}
+
+func GetPythonBinaryAndExecPath() (string, string) {
+ for _, p := range pythonBinaryCandidates {
+ if pth := hexec.LookPath(p); pth != "" {
+ return p, pth
}
}
- return path
+ return "", ""
}
diff --git a/markup/pandoc/convert.go b/markup/pandoc/convert.go
index 1c25e41d2..ae90cf417 100644
--- a/markup/pandoc/convert.go
+++ b/markup/pandoc/convert.go
@@ -15,7 +15,7 @@
package pandoc
import (
- "github.com/cli/safeexec"
+ "github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/htesting"
"github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/markup/internal"
@@ -44,7 +44,11 @@ type pandocConverter struct {
}
func (c *pandocConverter) Convert(ctx converter.RenderContext) (converter.Result, error) {
- return converter.Bytes(c.getPandocContent(ctx.Src, c.ctx)), nil
+ b, err := c.getPandocContent(ctx.Src, c.ctx)
+ if err != nil {
+ return nil, err
+ }
+ return converter.Bytes(b), nil
}
func (c *pandocConverter) Supports(feature identity.Identity) bool {
@@ -52,31 +56,35 @@ func (c *pandocConverter) Supports(feature identity.Identity) bool {
}
// getPandocContent calls pandoc as an external helper to convert pandoc markdown to HTML.
-func (c *pandocConverter) getPandocContent(src []byte, ctx converter.DocumentContext) []byte {
+func (c *pandocConverter) getPandocContent(src []byte, ctx converter.DocumentContext) ([]byte, error) {
logger := c.cfg.Logger
- path := getPandocExecPath()
- if path == "" {
+ binaryName := getPandocBinaryName()
+ if binaryName == "" {
logger.Println("pandoc not found in $PATH: Please install.\n",
" Leaving pandoc content unrendered.")
- return src
+ return src, nil
}
args := []string{"--mathjax"}
- return internal.ExternallyRenderContent(c.cfg, ctx, src, path, args)
+ return internal.ExternallyRenderContent(c.cfg, ctx, src, binaryName, args)
}
-func getPandocExecPath() string {
- path, err := safeexec.LookPath("pandoc")
- if err != nil {
- return ""
- }
+const pandocBinary = "pandoc"
- return path
+func getPandocBinaryName() string {
+ if hexec.InPath(pandocBinary) {
+ return pandocBinary
+ }
+ return ""
}
// Supports returns whether Pandoc is installed on this computer.
func Supports() bool {
+ hasBin := getPandocBinaryName() != ""
if htesting.SupportsAll() {
+ if !hasBin {
+ panic("pandoc not installed")
+ }
return true
}
- return getPandocExecPath() != ""
+ return hasBin
}
diff --git a/markup/pandoc/convert_test.go b/markup/pandoc/convert_test.go
index bd6ca19e6..f549d5f4f 100644
--- a/markup/pandoc/convert_test.go
+++ b/markup/pandoc/convert_test.go
@@ -16,7 +16,9 @@ package pandoc
import (
"testing"
+ "github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/common/loggers"
+ "github.com/gohugoio/hugo/config/security"
"github.com/gohugoio/hugo/markup/converter"
@@ -28,7 +30,9 @@ func TestConvert(t *testing.T) {
t.Skip("pandoc not installed")
}
c := qt.New(t)
- p, err := Provider.New(converter.ProviderConfig{Logger: loggers.NewErrorLogger()})
+ sc := security.DefaultConfig
+ sc.Exec.Allow = security.NewWhitelist("pandoc")
+ p, err := Provider.New(converter.ProviderConfig{Exec: hexec.New(sc), Logger: loggers.NewErrorLogger()})
c.Assert(err, qt.IsNil)
conv, err := p.New(converter.DocumentContext{})
c.Assert(err, qt.IsNil)
diff --git a/markup/rst/convert.go b/markup/rst/convert.go
index 4c11c4be8..b86b35f1b 100644
--- a/markup/rst/convert.go
+++ b/markup/rst/convert.go
@@ -18,7 +18,7 @@ import (
"bytes"
"runtime"
- "github.com/cli/safeexec"
+ "github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/htesting"
"github.com/gohugoio/hugo/identity"
@@ -48,7 +48,11 @@ type rstConverter struct {
}
func (c *rstConverter) Convert(ctx converter.RenderContext) (converter.Result, error) {
- return converter.Bytes(c.getRstContent(ctx.Src, c.ctx)), nil
+ b, err := c.getRstContent(ctx.Src, c.ctx)
+ if err != nil {
+ return nil, err
+ }
+ return converter.Bytes(b), nil
}
func (c *rstConverter) Supports(feature identity.Identity) bool {
@@ -57,31 +61,38 @@ func (c *rstConverter) Supports(feature identity.Identity) bool {
// getRstContent calls the Python script rst2html as an external helper
// to convert reStructuredText content to HTML.
-func (c *rstConverter) getRstContent(src []byte, ctx converter.DocumentContext) []byte {
+func (c *rstConverter) getRstContent(src []byte, ctx converter.DocumentContext) ([]byte, error) {
logger := c.cfg.Logger
- path := getRstExecPath()
+ binaryName, binaryPath := getRstBinaryNameAndPath()
- if path == "" {
+ if binaryName == "" {
logger.Println("rst2html / rst2html.py not found in $PATH: Please install.\n",
" Leaving reStructuredText content unrendered.")
- return src
+ return src, nil
}
- logger.Infoln("Rendering", ctx.DocumentName, "with", path, "...")
+ logger.Infoln("Rendering", ctx.DocumentName, "with", binaryName, "...")
var result []byte
+ var err error
+
// certain *nix based OSs wrap executables in scripted launchers
// invoking binaries on these OSs via python interpreter causes SyntaxError
// invoke directly so that shebangs work as expected
// handle Windows manually because it doesn't do shebangs
if runtime.GOOS == "windows" {
- python := internal.GetPythonExecPath()
- args := []string{path, "--leave-comments", "--initial-header-level=2"}
- result = internal.ExternallyRenderContent(c.cfg, ctx, src, python, args)
+ pythonBinary, _ := internal.GetPythonBinaryAndExecPath()
+ args := []string{binaryPath, "--leave-comments", "--initial-header-level=2"}
+ result, err = internal.ExternallyRenderContent(c.cfg, ctx, src, pythonBinary, args)
} else {
args := []string{"--leave-comments", "--initial-header-level=2"}
- result = internal.ExternallyRenderContent(c.cfg, ctx, src, path, args)
+ result, err = internal.ExternallyRenderContent(c.cfg, ctx, src, binaryName, args)
+ }
+
+ if err != nil {
+ return nil, err
}
+
// TODO(bep) check if rst2html has a body only option.
bodyStart := bytes.Index(result, []byte("<body>\n"))
if bodyStart < 0 {
@@ -96,24 +107,29 @@ func (c *rstConverter) getRstContent(src []byte, ctx converter.DocumentContext)
}
}
- return result[bodyStart+7 : bodyEnd]
+ return result[bodyStart+7 : bodyEnd], err
}
-func getRstExecPath() string {
- path, err := safeexec.LookPath("rst2html")
- if err != nil {
- path, err = safeexec.LookPath("rst2html.py")
- if err != nil {
- return ""
+var rst2Binaries = []string{"rst2html", "rst2html.py"}
+
+func getRstBinaryNameAndPath() (string, string) {
+ for _, candidate := range rst2Binaries {
+ if pth := hexec.LookPath(candidate); pth != "" {
+ return candidate, pth
}
}
- return path
+ return "", ""
}
-// Supports returns whether rst is installed on this computer.
+// Supports returns whether rst is (or should be) installed on this computer.
func Supports() bool {
+ name, _ := getRstBinaryNameAndPath()
+ hasBin := name != ""
if htesting.SupportsAll() {
+ if !hasBin {
+ panic("rst not installed")
+ }
return true
}
- return getRstExecPath() != ""
+ return hasBin
}
diff --git a/markup/rst/convert_test.go b/markup/rst/convert_test.go
index 269d92caa..5d2882de1 100644
--- a/markup/rst/convert_test.go
+++ b/markup/rst/convert_test.go
@@ -16,7 +16,9 @@ package rst
import (
"testing"
+ "github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/common/loggers"
+ "github.com/gohugoio/hugo/config/security"
"github.com/gohugoio/hugo/markup/converter"
@@ -28,7 +30,14 @@ func TestConvert(t *testing.T) {
t.Skip("rst not installed")
}
c := qt.New(t)
- p, err := Provider.New(converter.ProviderConfig{Logger: loggers.NewErrorLogger()})
+ sc := security.DefaultConfig
+ sc.Exec.Allow = security.NewWhitelist("rst", "python")
+
+ p, err := Provider.New(
+ converter.ProviderConfig{
+ Logger: loggers.NewErrorLogger(),
+ Exec: hexec.New(sc),
+ })
c.Assert(err, qt.IsNil)
conv, err := p.New(converter.DocumentContext{})
c.Assert(err, qt.IsNil)