# subtext -- text substitutor -*- awk -*- # (C) C Duckworth BEGIN { true = 1; false = 0 ## Tuneables runbody = bool(runbody ? runbody : true) postproc = postproc ? postproc : "shexpand" bodyfunc = bodyfunc ? bodyfunc : "body" sopath = get_value(sopath, ".:"ENVIRON["HOME"]"/.subtext") ## Globals ## Wrap the text in a function pretext = "### begin text\n"bodyfunc"(){\n" posttext = "}\n### end text" ## Prelude function strings # 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'" shxwrap = " -e 's/^/:/'" htmlfix = "sed -E" \ " -e 's#([^\\\\]|^)&#\\1\\&#g'" \ " -e 's#([^\\\\]|^)<#\\1\\<#g'" \ " -e 's#([^\\\\]|^)>#\\1\\>#g'" \ " -e 's#\\\\([&<>])#\\1#g'"; ## Prelude par = "#!/bin/sh\n### kernel\n" \ "preface()(sed \"s/^/$1/\")\n" \ "unpreface()(sed \"s/^$1//\")\n" \ "shexpand()(eval \"$( (echo 'cat<<.';preface +;echo .)" \ " | unpreface + )\")\n" \ "### library\n" \ "shellfix()(" shellfix ")\n" \ "htmlfix()(" htmlfix ")\n" \ "### variables\n" \ "ST_POSTPROC=" postproc "\n" \ "ST_BODYFUNC=" bodyfunc "\n" \ "ST_SOPATH=" sopath "\n" \ "### header\n" } ### End a block end[endn] && $0 == end[endn] { pushpar(end[endn--] "\n)", true) printpar() subdocp = false next } ### Line continuation /\\$/ { getline nl sub(/\\$/,"") $0 = $0 " " nl } ### Special commands ## These call subtext-internal functions /^#so/ { # Insert $2 verbatim (if in sopath), else error pushpar($0) source($2) next } /^###$/ { # Delimit document printpar() docp = !docp if (docp) { print pretext "unpreface ':'<<'_'|eval \"$ST_POSTPROC\"" } else { end_text() } next } ### Escape sequences /^\.\./ && docp { # Begin a heredoc # ..[] [] [< [] ## wraps the line in $( ... ), basically (also quotes) specialp = 2 gsub(/"/, "\\\\&", $0) ln = "$$(" substr($1, 2) for (f=2; f<=NF; f++) { ln = ln " \"" $f "\"" } ln = ln ")" $0 = ln } /^\\/ && docp { # \ at the beginning of a line escapes the next character $0 = substr($0, 2) } ### Book-keeping /^$/ { if (!par) next printpar() } { pushpar($0) if (--specialp < 0) specialp = 0 } END { if (dead) exit dead if (par) printpar() while (endn > 0) print "\n" end[endn--] "\n)" if (docp) end_text() if (runbody) print bodyfunc } function end_text() { print "_\n" posttext } function pushpar(text, force_newline) { par = par ((par || force_newline) ? "\n" : "") text } function printpar() { specialp = specialp || (match(par, /^[ ]*" par "

" shx = shellfix shxwrap print par | shx close(shx) } else print par par = "" } function get_value(var, env_var, default) { if (var) return var else return default } function bool(var, default) { if (var=="false" || var=="no" || var=="0") return false else if (var=="true" || var=="yes" || var=="1") return true else return var || default } function source(name) { found = false sp = "" split(sopath, asopath, ":") for (dir in asopath) { fn = asopath[dir] "/" name sp = asopath[dir] "\n\t" sp while ((getline ln < fn) > 0) { found = true pushpar(ln) } if (found) break } if (!found) die("Couldn't source " name "; looked in:\n\t" sp, 9) } function die(message, code) { dead = code print "!!" FILENAME ":" NR ": " message > "/dev/stderr" exit }