[24] | 1 | package main
|
---|
| 2 |
|
---|
| 3 | import (
|
---|
| 4 | "bytes"
|
---|
| 5 | "io"
|
---|
[32] | 6 | "io/ioutil"
|
---|
[24] | 7 | "log"
|
---|
| 8 | "os"
|
---|
| 9 | "os/exec"
|
---|
| 10 | "path/filepath"
|
---|
| 11 | "strings"
|
---|
| 12 | )
|
---|
| 13 |
|
---|
[32] | 14 | func makeFuncs(funcs Funcs, vars Vars) Funcs {
|
---|
| 15 | f := Funcs{}
|
---|
| 16 | for k, v := range funcs {
|
---|
| 17 | f[k] = v
|
---|
| 18 | }
|
---|
| 19 | for k, v := range vars {
|
---|
| 20 | f[k] = varFunc(v)
|
---|
| 21 | }
|
---|
| 22 | // Plugin functions
|
---|
| 23 | files, _ := ioutil.ReadDir(ZSDIR)
|
---|
| 24 | for _, file := range files {
|
---|
| 25 | if !file.IsDir() {
|
---|
| 26 | name := file.Name()
|
---|
| 27 | if !strings.HasSuffix(name, ".html") && !strings.HasSuffix(name, ".amber") {
|
---|
| 28 | f[renameExt(name, "", "")] = pluginFunc(name, vars)
|
---|
| 29 | } else {
|
---|
| 30 | f[renameExt(name, "", "")] = partialFunc(name, f, vars)
|
---|
| 31 | }
|
---|
| 32 | }
|
---|
| 33 | }
|
---|
| 34 | return f
|
---|
| 35 | }
|
---|
| 36 |
|
---|
[24] | 37 | func varFunc(s string) func() string {
|
---|
| 38 | return func() string {
|
---|
| 39 | return s
|
---|
| 40 | }
|
---|
| 41 | }
|
---|
| 42 |
|
---|
| 43 | func pluginFunc(cmd string, vars Vars) func(args ...string) string {
|
---|
| 44 | return func(args ...string) string {
|
---|
| 45 | out := bytes.NewBuffer(nil)
|
---|
[25] | 46 | if err := run(filepath.Join(ZSDIR, cmd), args, vars, out); err != nil {
|
---|
[24] | 47 | return cmd + ":" + err.Error()
|
---|
| 48 | } else {
|
---|
| 49 | return string(out.Bytes())
|
---|
| 50 | }
|
---|
| 51 | }
|
---|
| 52 | }
|
---|
| 53 |
|
---|
[32] | 54 | func partialFunc(name string, funcs Funcs, vars Vars) func() string {
|
---|
| 55 | return func() string {
|
---|
| 56 | var err error
|
---|
| 57 | w := bytes.NewBuffer(nil)
|
---|
| 58 | if strings.HasSuffix(name, ".amber") {
|
---|
| 59 | err = buildAmber(filepath.Join(ZSDIR, name), w, funcs, vars)
|
---|
| 60 | } else {
|
---|
| 61 | err = buildHTML(filepath.Join(ZSDIR, name), w, funcs, vars)
|
---|
| 62 | }
|
---|
| 63 | if err != nil {
|
---|
| 64 | return name + ":" + err.Error()
|
---|
| 65 | }
|
---|
| 66 | return string(w.Bytes())
|
---|
| 67 | }
|
---|
| 68 | }
|
---|
| 69 |
|
---|
[24] | 70 | func builtins() Funcs {
|
---|
[25] | 71 | exec := func(cmd string, args ...string) string {
|
---|
| 72 | out := bytes.NewBuffer(nil)
|
---|
| 73 | if err := run(cmd, args, Vars{}, out); err != nil {
|
---|
| 74 | return cmd + ":" + err.Error()
|
---|
| 75 | } else {
|
---|
| 76 | return string(out.Bytes())
|
---|
| 77 | }
|
---|
[24] | 78 | return ""
|
---|
| 79 | }
|
---|
| 80 | return Funcs{
|
---|
[32] | 81 | "exec": exec,
|
---|
| 82 | "var": Var,
|
---|
| 83 | "lorem": Lorem,
|
---|
| 84 | "dateparse": DateParse,
|
---|
| 85 | "datefmt": DateFmt,
|
---|
| 86 | "wc": WordCount,
|
---|
| 87 | "ttr": TimeToRead,
|
---|
| 88 | "ls": List,
|
---|
| 89 | "...": func(args ...string) []string {
|
---|
| 90 | return append([]string{"..."}, args...)
|
---|
[24] | 91 | },
|
---|
[32] | 92 | "sort": func(args ...string) []string {
|
---|
| 93 |
|
---|
| 94 | return Sort(args...)
|
---|
| 95 | },
|
---|
[24] | 96 | }
|
---|
| 97 | }
|
---|
| 98 |
|
---|
| 99 | func renameExt(path, from, to string) string {
|
---|
| 100 | if from == "" {
|
---|
| 101 | from = filepath.Ext(path)
|
---|
| 102 | }
|
---|
| 103 | if strings.HasSuffix(path, from) {
|
---|
| 104 | return strings.TrimSuffix(path, from) + to
|
---|
| 105 | } else {
|
---|
| 106 | return path
|
---|
| 107 | }
|
---|
| 108 | }
|
---|
| 109 |
|
---|
| 110 | func globals() Vars {
|
---|
| 111 | vars := Vars{}
|
---|
| 112 | for _, e := range os.Environ() {
|
---|
| 113 | pair := strings.Split(e, "=")
|
---|
| 114 | if strings.HasPrefix(pair[0], "ZS_") {
|
---|
| 115 | vars[strings.ToLower(pair[0][3:])] = pair[1]
|
---|
| 116 | }
|
---|
| 117 | }
|
---|
| 118 | return vars
|
---|
| 119 | }
|
---|
| 120 |
|
---|
| 121 | // Converts zs markdown variables into environment variables
|
---|
| 122 | func env(vars Vars) []string {
|
---|
| 123 | env := []string{"ZS=" + os.Args[0], "ZS_OUTDIR=" + PUBDIR}
|
---|
| 124 | env = append(env, os.Environ()...)
|
---|
| 125 | if vars != nil {
|
---|
| 126 | for k, v := range vars {
|
---|
| 127 | env = append(env, "ZS_"+strings.ToUpper(k)+"="+v)
|
---|
| 128 | }
|
---|
| 129 | }
|
---|
| 130 | return env
|
---|
| 131 | }
|
---|
| 132 |
|
---|
| 133 | // Runs command with given arguments and variables, intercepts stderr and
|
---|
| 134 | // redirects stdout into the given writer
|
---|
| 135 | func run(cmd string, args []string, vars Vars, output io.Writer) error {
|
---|
| 136 | var errbuf bytes.Buffer
|
---|
| 137 | c := exec.Command(cmd, args...)
|
---|
| 138 | c.Env = env(vars)
|
---|
| 139 | c.Stdout = output
|
---|
| 140 | c.Stderr = &errbuf
|
---|
| 141 |
|
---|
| 142 | err := c.Run()
|
---|
| 143 |
|
---|
| 144 | if errbuf.Len() > 0 {
|
---|
| 145 | log.Println("ERROR:", errbuf.String())
|
---|
| 146 | }
|
---|
| 147 |
|
---|
| 148 | if err != nil {
|
---|
| 149 | return err
|
---|
| 150 | }
|
---|
| 151 | return nil
|
---|
| 152 | }
|
---|
| 153 |
|
---|
| 154 | // Splits a string in exactly two parts by delimiter
|
---|
| 155 | // If no delimiter is found - the second string is be empty
|
---|
| 156 | func split2(s, delim string) (string, string) {
|
---|
| 157 | parts := strings.SplitN(s, delim, 2)
|
---|
| 158 | if len(parts) == 2 {
|
---|
| 159 | return parts[0], parts[1]
|
---|
| 160 | } else {
|
---|
| 161 | return parts[0], ""
|
---|
| 162 | }
|
---|
| 163 | }
|
---|