# subtext -*- awk -*- # (C) C Duckworth ## Subtext is an awk program that converts roff-like input to a shell script ## that can output markup. Lines prefixed with . are wrapped in $(...) ## constructs within the `body' function, but lines prefixed with # are ## transported to the top of the file, where they're executed within the shell ## environment. BEGIN { ## Tuneables dryrun = dryrun ? dryrun : 0 sopath = sopath ? sopath : ".:" ENVIRON["HOME"] "/.subtext" shxend = shxend ? shxend : "%%end" ## Shellfix: escape ` and $ in input # Ask sed to do these b/c awk has no capture groups ;_; shellfix = "sed -E" \ " -e 's/`/\\\\`/g'" \ " -e 's/(^|[^\\$])\\$([^\\$]|$)/\\1\\\\$\\2/g'" \ " -e 's/(^|[^\\$])\\$(\\$+)([^\\$]|$)/\\1\\2\\3/g'" } /\\$/ { # line continuation getline nl sub(/\\$/, "") $0 = $0 " " nl } /^#so/ { # source a file pushpar() source($2) next } /^#/ { # head lines sub(/^#+[ ]*/,"") head = head (head?"\n":"") $0 next } /^\.\./ { # block body = body (body?"\n":"") \ "$$(unquote + << .." \ (length>2 ? " | " substr($1,3) " " shquote(2) : "") \ slurp("..") \ "..\n)" next } /^\./ { # line body = body (body?"\n":"") \ "$$(" substr($1, 2) " " shquote(2) ")" next } /^$/ { # line break if (!pushpar()) next } { # regular text par = par (par?"\n":"") $0 } END { if (dead) exit dead print "#!/bin/sh" print "### generated with subtext" print "quote()(sed \"s/^/$1/\")" print "unquote()(sed \"s/^$1//\")" printf "shexpand()(eval \"$(echo 'cat<<%s';cat;echo '%s')\")\n", \ shxend, shxend printf "shellfix()(%s)\n", shellfix printf "ST_SOPATH=%s\n", sopath print "### head" print head print "### body" print "body(){ unquote : << \\_ | shexpand" pushpar() print body | (shellfix " -e 's/^/:/'") close(shellfix " -e 's/^/:/'") print "_" print "}" if (!dryrun) print "body" } function shquote(begin, end, out) { if (!begin) begin = 1 if (!end) end = NF for (i=begin; i<=NF; i++) { gsub(/"/, "\\\"", $i) out = out (out?" ":"") "\"" $i "\"" } return out } function slurp(to, out, nl) { while (nl != to) { getline nl out = out "\n+" nl } sub("\\+"to"$", "", out) return out } function pushpar() { if (!par) return 0 if (!match(par, /^[ ]*" par "

" body = body (body?"\n":"") par par = "" return 1 } function source(name, found, sp) { split(sopath, asopath, ":") for (dir in asopath) { fn = asopath[dir] "/" name sp = asopath[dir] "\n\t" sp while ((getline ln < fn) > 0) { found = 1 head = head (head?"\n":"") ln } if (found) break } if (!found) { printf "Couldn't source %s; looked in:\n\t%s", name, sp dead = 127 exit } }