# subtext -- text substitutor -*- awk -*- # (C) C Duckworth BEGIN { ## Tuneables postproc = get_value(postproc, "ST_POSTPROC", "shexpand") bodyfunc = get_value(bodyfunc, "ST_BODYFUNC", "sub_text") sopath = get_value(sopath, "ST_SOPATH", ".:"ENVIRON["HOME"]"/.subtext") split(sopath, asopath, ":") # postproc = postproc ? postproc : "shexpand" # bodyfunc = bodyfunc ? bodyfunc : "sub_text" ## Globals # Booleans true = 1 false = 0 ## 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] { par = par "\n" end[endn--] "\n)" 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 source($2) next } ### Escape sequences /^\.\.\./ { # Delimit document (end is optional) printpar() docp = !docp if (docp) print pretext "unpreface ':'<<'_'|eval \"$ST_POSTPROC\"" else end_text() next } /^\.\./ && docp { # Begin a heredoc # ..[] [] [< [] ## wraps the line in $( ... ), basically (also quotes) specialp = 2 gsub(/"/,"\\\\&") 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() } { par = par (par?"\n":"") $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() } function end_text() { print "_\n" posttext } 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 if (ENVIRON[env_var]) return ENVIRON[env_var] else return default } function source(name) { found = false sp = "" for (dir in asopath) { fn = asopath[dir] "/" name sp = asopath[dir] "\n\t" sp while ((getline ln < fn) > 0) { found = true par = par (par?"\n":"") 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 }