Libraryize

This commit is contained in:
Case Duckworth 2020-03-15 18:23:38 -05:00
parent 1e7913665b
commit ce40a9da27
3 changed files with 80 additions and 257 deletions

80
hell Executable file
View File

@ -0,0 +1,80 @@
#!/bin/sh
# hell - HTML in shell
tx()
{
: "${RAW:=false}"
if "$RAW"; then
printf '%b\n' "$*"
return
fi
while
[ "$#" -gt 0 ] && printf %s "$1"
do
[ "$#" -gt 1 ] && printf ' '
shift
done
printf '\n'
}
el()
{ # el NAME [ATTR=VALUE...] [-] [TEXT...]
el="$1"; shift
READ_IN=true
text=
attr=
class=
for word; do
case "$word" in
\\*) # words starting with backslashes are text
text="$text${text:+ }${word#\\}"
;;
class=*) # otherwise, 'class=' is a special case
class="${class:-class=\"}${class:+ }${word#class=}"
;;
*=*) # otherwise, words containing an equals are attr=value pairs
anam="${word%=*}"
aval="${word#*=}"
aval="${aval%\"}"
aval="${aval#\"}"
attr="$attr${attr:+ }${anam}=\"${aval}\""
;;
-) # otherwise, '-' denotes stdin
"$READ_IN" &&
while IFS= read -r line; do
text="$text${text:+
}$line" # this is very ugly but it works
done
;;
/) # close-tag flag: don't look for text from stdin
READ_IN=false
;;
*) # otherwise, it's text
text="$text${text:+ }$word"
;;
esac
done
# close out the class string
if [ -n "$class" ]; then
class="$class\""
attr="$attr${attr:+ }$class"
fi
# if there's no text, try reading from stdin
if [ -z "$text" ] && "$READ_IN"; then
while IFS= read -r line; do
text="$text${text:+
}$line" # this is very ugly but it works
done
fi
# print the thing
printf '<%s%s>%s' "$el" "${attr:+ }$attr" "$text"
# if there's text, close the tag
[ -n "$text" ] && printf '</%s>' "$el"
printf '\n'
}

244
hell.sh
View File

@ -1,244 +0,0 @@
#!/bin/sh
# hell - HTML in shell
# (c) 2020 Case Duckworth <acdw at acdw dot net>
# License: MIT
# v.0.0.2 (codename WHY AM I DOING THIS?)
#
# This file is part of _hawkish_, an ssg made of shell, awk, and Makefiles
# (maybe). As POSIX as possible. For fun and non-profit.
# Maybe I could rename this Saitama: I'm a build system for fun!
# ANYWAY:
# This file is meant to be sourced by the eventual generated sh script.
# It will define functions that the generated script will use to generate
# HTML.
# The generated sh script will be generated by an intermediary awk script that
# transforms input text files into a shab(github.com/zimbatm/shab)-like file,
# which will be run to output the HTML.
[ -n "$DEBUG" ] && set -x
# implement die (TODO: put this in a util file?)
die()
{
case "$#" in
1) echo "$1"; exit 1 ;;
*) ec="$1"; shift; echo "$@"; exit "$ec" ;;
esac
}
# First things first - define how to cleanly exit.
cleanup()
{
rm "$BLOCKS" "$INLINES"
}
trap cleanup INT QUIT TERM EXIT
# BLOCKS holds all open block-level elements so that we can close them like good
# little children.
BLOCKS="$(mktemp hell-blocks.XXXXXX)"
# INLINES is the same, but for inlines.
INLINES="$(mktemp hell-inlines.XXXXXX)"
# You can push tags to the stack or pop them.
# Note: these only deal with tag NAMEs (e.g. 'p', 'h1', etc.). The actual TAGs
# ('<h1 class=whatever>', etc.) are left up to the caller.
# These only deal with one line at a time right now. I think that's best.
push()
{ # push FILE VALUE
# returns: VALUE
stack="$1"
#shift
printf '%s\n' "$2" >> "$stack"
}
# see unix.stackexchange.com/questions/474838
pop()
{ # pop FILE
# returns: popped VALUE
# shellcheck disable=2034
LC_TYPE=C
stack="$1"
#shift
l="$(tail -n1 "$stack"; echo t)"
[ "$l" = t ] && return 1
l="${l%t}"
# TODO: make sure truncate(1) is available most places
truncate -s "-${#l}" "$stack"
printf '%s' "$l"
}
# common function for block() and inline().
# consider: parsing 'tag.class#id.class[attr=attr]' type lines
# or just what it is now, i.e. 'attr=value attr=value text text'
parseterms()
{ # parseterms [attr=VALUE] [TEXT]
: "${attr:=}"
: "${text:=}"
class=""
for term; do
case "$term" in
\\*) # if it starts with a backslash, include it in text
text="$text ${term#\\}"
;;
class=*) # if it's a class, add it to the class string
# TODO: any other attrs to do this with?
if [ -z "$class" ]; then
class="class=\"${term#\class=}"
else
class="$class ${term#\class=}"
fi
;;
*=*) # if it has an equal, it's an attribute
name="${term%=*}"
value="${term#*=}"
attr="$attr $name=\"$value\""
;;
*) # otherwise, it's text
if [ -z "$text" ]; then
text="$term"
else
text="$text $term"
fi
;;
esac
done
if [ -n "$class" ]; then
class="$class\""
attr="$attr $class"
fi
}
# create a new tag.
# it won't auto-close unless the name ends with '/'.
tag()
{ # tag <-i|-b> TAG[/] [ATTR=VALUE...] [TEXT...]
autoclose=false
attr=""
text=""
case "$1" in
-i) # inline
tagstack="$INLINES"
shift
;;
-b) # block
tagstack="$BLOCKS"
shift
;;
*) # other -- default to block
tagstack="$BLOCKS"
;;
esac
case "$1" in
*/) # set this tag to auto-close. don't push it onto the stack.
autoclose=true
tag="${1%/}"
;;
*) # begin a tag and push it onto the stack.
tag="$1"
push "$tagstack" "$tag"
;;
esac
shift
parseterms "$@" # parse the rest of the arguments
if "$autoclose" && [ -z "$text" ]; then
# Okay, here's an ugly bit of HTML.
# APPARENTLY, with "void elements" or "foreign elements" we *can* use a slash to end an
# empty tag, but only with those. Otherwise, it's an error (apparently
# in HTML4 it's ALL an error, but W3C validates it fine. ufgghh...
case "$tag" in
area|base|br|col|embed|hr|img|input|link|meta|\
param|source|track|wbr|command|keygen|menuitem)
# void elements
printf '<%s%s />' "$tag" "$attr"
return
;;
# TODO: add mathml & svg ... maybe.
*) : ;; # do nothing - I'll open and close the tag below.
esac
fi
# open the tag and add whatever text we've provided
printf '<%s%s>%s' "$tag" "$attr" "${text:- }"
# close the tag if asked
if "$autoclose"; then
printf '</%s>' "$tag"
fi
printf '\n'
}
# end something
end()
{ # end [-b] [-t TAG] [-n NUMBER]
while getopts t:n:ba OPT; do
case "$OPT" in
t) # end upto tag -- close tags up to and including TAG
target="$OPTARG"
while : ; do
tag="$(pop "$INLINES")" || break # fall through to BLOCKS
printf '</%s>' "$tag"
[ "$tag" = "$target" ] && return 0 # found the thing!
done
while : ; do
tag="$(pop "$BLOCKS")" || return 1 # didn't find target
printf '</%s>\n' "$tag"
[ "$tag" = "$target" ] && return 0 # found the thing!
done
;;
n) # end NUMBER of tags
n="$OPTARG"
;;
b) # end up to the nearest block -- a special case of -n
i="$(wc -l < "$INLINES")"
n=$((i+1))
;;
a) # end all tags -- another special case of -n
i="$(wc -l < "$INLINES")"
b="$(wc -l < "$BLOCKS")"
n=$((i+b+1))
;;
\?) # bad OPTARG
return 2
;;
*) # bad OPT
return 2
;;
esac
done
# -n case
while [ "$n" -gt 0 ]; do
tag="$(pop "$INLINES")" || break # fall through to BLOCKS
printf '</%s>' "$tag"
n=$((n-1))
done
while [ "$n" -gt 0 ]; do
tag="$(pop "$BLOCKS")" || return 1 # exhausted tags before NUMBER
printf '</%s>\n' "$tag"
n=$((n-1))
done
}
# text is just a regular line of text.
# no error checking is done on where the text is going to be --
# it just prints it. (this is a less-error-prone `echo`.)
text()
{ # text [TEXT...]
if [ "$#" -gt 0 ]; then
printf '%s' "$1"
shift
if [ "$#" -gt 0 ]; then
printf ' %s' "$@"
fi
fi
printf '\n'
}

View File

@ -1,13 +0,0 @@
#!/bin/sh
. ./hell.sh
tag -b article
tag -b header
tag -b h1/ class=title "Here is a sample title"
tag -b p "Here's a paragraph. It's going to be pretty basic"
text "but hopefully it'll work out well. I need to quote"
text "the arguments so that quotes and shit don't have problems."
text "I wonder if exclamation points (!) will cause any issues."
end -a