1 | package main
|
---|
2 |
|
---|
3 | import (
|
---|
4 | "bytes"
|
---|
5 | "io"
|
---|
6 | "io/ioutil"
|
---|
7 | "log"
|
---|
8 | "os"
|
---|
9 | "os/exec"
|
---|
10 | "path/filepath"
|
---|
11 | "strings"
|
---|
12 | )
|
---|
13 |
|
---|
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 |
|
---|
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)
|
---|
46 | if err := run(filepath.Join(ZSDIR, cmd), args, vars, out); err != nil {
|
---|
47 | return cmd + ":" + err.Error()
|
---|
48 | } else {
|
---|
49 | return string(out.Bytes())
|
---|
50 | }
|
---|
51 | }
|
---|
52 | }
|
---|
53 |
|
---|
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 |
|
---|
70 | func builtins() Funcs {
|
---|
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 | }
|
---|
78 | return ""
|
---|
79 | }
|
---|
80 | return Funcs{
|
---|
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...)
|
---|
91 | },
|
---|
92 | "sort": func(args ...string) []string {
|
---|
93 |
|
---|
94 | return Sort(args...)
|
---|
95 | },
|
---|
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 | }
|
---|