about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorCase Duckworth2024-01-29 00:02:27 -0600
committerCase Duckworth2024-01-29 00:02:27 -0600
commit59e5a214a8be015b00004b1961a83f48dd6d65f4 (patch)
tree1cbe20804bc478a882039ef2b46293c781004fe2
downloadsubtext-59e5a214a8be015b00004b1961a83f48dd6d65f4.tar.gz
subtext-59e5a214a8be015b00004b1961a83f48dd6d65f4.zip
Initial commit
-rw-r--r--subtext.awk165
-rw-r--r--test.st33
2 files changed, 198 insertions, 0 deletions
diff --git a/subtext.awk b/subtext.awk new file mode 100644 index 0000000..c3e135b --- /dev/null +++ b/subtext.awk
@@ -0,0 +1,165 @@
1# subtext -- text substitutor -*- awk -*-
2# (C) C Duckworth <acdw@acdw.net>
3
4BEGIN {
5 ## Tuneables
6 postproc = get_value(postproc, "ST_POSTPROC", "shexpand")
7 bodyfunc = get_value(bodyfunc, "ST_BODYFUNC", "sub_text")
8 sopath = get_value(sopath, "ST_SOPATH", ".:"ENVIRON["HOME"]"/.subtext")
9 split(sopath, asopath, ":")
10 # postproc = postproc ? postproc : "shexpand"
11 # bodyfunc = bodyfunc ? bodyfunc : "sub_text"
12 ## Globals
13 # Booleans
14 true = 1
15 false = 0
16 ## Wrap the text in a function
17 pretext = "### begin text\n"bodyfunc"(){\n"
18 posttext = "}\n### end text"
19 ## Prelude function strings
20 # Ask sed to do these b/c awk has no capture groups ;_;
21 shellfix = "sed -E" \
22 " -e 's/`/\\\\`/g'" \
23 " -e 's/(^|[^\\$])\\$([^\\$]|$)/\\1\\\\$\\2/g'" \
24 " -e 's/(^|[^\\$])\\$(\\$+)([^\\$]|$)/\\1\\2\\3/g'"
25 shxwrap = " -e 's/^/:/'"
26 htmlfix = "sed -E" \
27 " -e 's#([^\\\\]|^)&#\\1\\&amp;#g'" \
28 " -e 's#([^\\\\]|^)<#\\1\\&lt;#g'" \
29 " -e 's#([^\\\\]|^)>#\\1\\&gt;#g'" \
30 " -e 's#\\\\([&<>])#\\1#g'";
31 ## Prelude
32 par = "#!/bin/sh\n### kernel\n" \
33 "preface()(sed \"s/^/$1/\")\n" \
34 "unpreface()(sed \"s/^$1//\")\n" \
35 "shexpand()(eval \"$( (echo 'cat<<.';preface +;echo .)" \
36 " | unpreface + )\")\n" \
37 "### library\n" \
38 "shellfix()(" shellfix ")\n" \
39 "htmlfix()(" htmlfix ")\n" \
40 "### variables\n" \
41 "ST_POSTPROC=" postproc "\n" \
42 "ST_BODYFUNC=" bodyfunc "\n" \
43 "ST_SOPATH=" sopath "\n" \
44 "### header\n"
45}
46
47### End a block
48
49end[endn] && $0 == end[endn] {
50 par = par "\n" end[endn--] "\n)"
51 printpar()
52 subdocp = false
53 next
54}
55
56### Line continuation
57
58/\\$/ {
59 getline nl
60 sub(/\\$/,"")
61 $0 = $0 " " nl
62}
63
64### Special commands
65## These call subtext-internal functions
66
67/^\.so/ { # Insert $2 verbatim (if in sopath), else error
68 source($2)
69 next
70}
71
72### Escape sequences
73
74/^\.\.\./ { # Delimit document (end is optional)
75 printpar()
76 docp = !docp
77 if (docp) print pretext "unpreface ':'<<'_'|eval \"$ST_POSTPROC\""
78 else end_text()
79 next
80}
81
82/^\.\./ && docp { # Begin a heredoc
83 # ..[<command>] [<options>] [<<delim]
84 ## ends with DELIM or '..'
85 subdocp = true
86 end[++endn] = (match($0,"<<") ? substr($0,RSTART+RLENGTH) : "..")
87 command = substr($0, 3, RSTART ? RSTART - 2 : length)
88 $0 = "$$(" (command ? command : "cat") "<<" end[endn]
89}
90
91/^\./ && docp { # One-line command
92 # .<command> [<parameters>]
93 ## wraps the line in $( ... ), basically (also quotes)
94 specialp = 2
95 gsub(/"/,"\\\\&")
96 ln = "$$(" substr($1, 2)
97 for (f=2;f<=NF;f++) ln = ln " \"" $f "\""
98 ln = ln ")"
99 $0 = ln
100}
101
102/^\\/ && docp { # \ at the beginning of a line escapes the next character
103 $0 = substr($0, 2)
104}
105
106### Book-keeping
107
108/^$/ {
109 if (!par) next
110 printpar()
111}
112
113{ par = par (par?"\n":"") $0 }
114{ if (--specialp < 0) specialp = 0 }
115
116END {
117 if (dead) exit dead
118 if (par) printpar()
119 while (endn > 0)
120 print "\n" end[endn--] "\n)"
121 if (docp) end_text()
122}
123
124function end_text() {
125 print "_\n" posttext
126}
127
128function printpar() {
129 specialp = specialp || (match(par, /^[ ]*</))
130 if (docp) {
131 if (!subdocp && !specialp) par = "<p>" par "</p>"
132 shx = shellfix shxwrap
133 print par | shx
134 close(shx)
135 } else print par
136 par = ""
137}
138
139function get_value(var, env_var, default) {
140 if (var) return var
141 else if (ENVIRON[env_var]) return ENVIRON[env_var]
142 else return default
143}
144
145function source(name) {
146 found = false
147 sp = ""
148 for (dir in asopath) {
149 fn = asopath[dir] "/" name
150 sp = asopath[dir] "\n\t" sp
151 while ((getline ln < fn) > 0) {
152 found = true
153 par = par (par?"\n":"") ln
154 }
155 if (found) break
156 }
157 if (!found)
158 die("Couldn't source " name "; looked in:\n\t" sp, 9)
159}
160
161function die(message, code) {
162 dead = code
163 print "!!" FILENAME ":" NR ": " message > "/dev/stderr"
164 exit
165}
diff --git a/test.st b/test.st new file mode 100644 index 0000000..b11bd87 --- /dev/null +++ b/test.st
@@ -0,0 +1,33 @@
1.so etlib.sh
2alias verse=cat
3title="test file"
4...
5here's a test file
6.a href="https://example.com" a link or something!!
7
8.h1 it has "headers" or whatever
9
10..blockquote title=foo
11it has paragraphs of quotes
12..
13
14$$(echo it has shell $scripty-stuff)
15
16..
17escaped par!
18omg wow
19..
20
21..p
22another one :O
23
24holy crap
25this is great
26..
27
28i should have more paragraphs
29
30they are wonderful
31i love them
32
33very much