scripts/bread/bin/bread

319 lines
5.9 KiB
Bash
Executable File

#!/bin/bash
# bread: do stuff on breadpunk
# by breadw
: "${BREADNEWS:=/bread/news}"
: "${BREADDOCS:=/bread/docs}"
### Here's the bit that /was/ in lib.sh
#shellcheck disable=2034
DISCOUNT="markdown -f links,image,pants,html,autolink,fencedcode"
alias p=echo
alias l='p >&2'
N(){ f="${1:-$F}";n="${f#$UNKI/}";p "${n%.*}";}
NN(){ f="${1:-$F}";n="${f##*/}";p "${n%%.*}";}
N_(){ f="${1:-$F}";n="$(N "$f")";p "${n#_}";}
M(){ sed '/^$/q' "${2:-$F}"|grep "^$1"|cut -f2-;}
C(){ sed '1,/^$/d' "${1:-$F}";}
X(){
Xd=$((Xd+1));
eval "$(p "set -e";p "cat<<$UNKZ$Xd";cat "${1:--}"|sed 's/`/\\`/g';p;p "$UNKZ$Xd")";
}
modmsg()
{ # message as to when a page was modified
date -d "$1" \
+'this page was updated on <time datetime="%F">'"${2:-%e %B %Y}"'</time>'
}
series()
{
in="$(cat)"
s="$*"
if [ -n "$in" ]; then
s="$s $in"
fi
if [ -z "$s" ]; then
return
fi
tx "part of the $s series"
}
wrapstr()
{ # wrap a non-empty string
sed -e "s_^._<$*>&_" -e "s_.\$_&</$1>_"
}
addstr()
{ # add a string after, but not after an empty string
sed "s/^.\+\$/&$*/"
}
defstr()
{ # replace an empty string with a default
sed "s/^\$/$*/"
}
slugify()
{ # turn a string into a slug
if [ "$#" -gt 0 ]; then
echo "$*"
else
cat
fi |
tr '[:upper:]' '[:lower:]' |
sed \
-e 's/[[:punct:]]//g' \
-e 's/[^[:alnum:]_]/-/g' \
-e 's/-+/-/g' \
-e 's/^-//' -e 's/-$//'
}
recent_files()
{ # list files
# WARNING: find -printf is NOT posix :(
dir="$1"
shift
find "$dir" -maxdepth 1 \
-not -name '.*' "$@" \
-printf '%T@\t%p\n' \
| sort -nr \
| cut -f2
}
maybe_expand()
{ # expand a file based on its metadata (or lack thereof)
F="${1:-$F}"
expand="$(M expand "$F")"
if [ -z "$expand" ] || "$expand"; then
C "$F" | ./lht | X
else
C "$F"
fi
}
#shellcheck disable=2120
build()
{ # X on a file
F="${1:-$F}"
[ -f "$F" ] || return 1
l "$F"
dir="$UNKO/$(N)"
rm -rf "$dir"
mkdir -p "$dir"
X "$UNKL" > "$dir/index.html"
}
build_dir()
{ # build a directory
for F in "$UNKI/$1"/*; do
build || l "$F: not a file"
done
}
listing()
{ # print a listing
p "<ul class='listing'>"
for F in $(recent_files "$UNKI/$1" -type f); do
build && {
p "<li><a href='/$(N)/'>$(M title)</a>"
if [ -n "$(M series)" ]; then
p "<span class='series desc'>$(series "$(M series)")</span>"
fi
p "</li>"
}
done
p "</ul>"
}
###
groupck()
{
groups "$USER" | grep -q "$1" || {
echo "You're not in the $1 group!"
exit 3
}
}
setperms()
{
chown :bakers "$1"
chmod a+r,g+w "$1"
}
usage()
{
cat <<-ENDOFUSAGE
🍞 let's get this 🥖 B R E A D 🥐
a tool for breadpunk.club
🍞 🥖 🥐
usage: bread [-h] COMMAND [OPTIONS]
🍞 🥖 🥐
commands:
- news: read the news
- docs: read documents
for detailed usage of each command,
run bread COMMAND -h
🍞 🥖 🥐
ENDOFUSAGE
exit 1
}
bread_news()
{
num=4
artcmd="find \"$BREADNEWS\" -type f | sort -nr | head -n \"\$num\""
while getopts hn:s:w opt; do
case "$opt" in
h)
cat <<-ENDOFHELP
bread news : read the news
usage: bread news [-n NUM] [-s QUERY] [-w]
-n NUM read NUM newest articles. default: 4
-s QUERY search for QUERY in news database
-w write a new article (admin only)
ENDOFHELP
exit
;;
n) #shellcheck disable=2034
num="$OPTARG" ;;
s)
cmd="grep -R \"$OPTARG\" \"$BREADNEWS\" -l --exclude-dir .git"
;;
w)
groupck admin
article="$BREADNEWS/$(date +%F)@$(date +%H:%M).md"
cat <<-ENDOFARTICLE >"$article"
title
author $USER
date $(date +%F)
ENDOFARTICLE
$EDITOR "$article"
setperms admin
exit
;;
\?) return 2 ;;
*) return 2 ;;
esac
done
for art in $(eval "$artcmd"); do
echo " 🍞 🥖 🥐 "
printf '\033[34;1;4m%s\033[0m\n' "$(M title "$art")"
printf '\033[34;1m%s\033[0m\n' "$(M date "$art")"
C "$art" | pandoc -t plain
done
echo " 🍞 🥖 🥐 "
}
bread_docs()
{
while getopts hs:ne: opt; do
case "$opt" in
h)
cat <<-ENDOFHELP
bread docs : read helpful documents
usage: bread docs [-s QUERY] [-n] [-e QUERY]
-s QUERY search for QUERY in documents
-n write a new document
: if it has the same name as another,
: it'll fail
-e QUERY edit a document matching QUERY
ENDOFHELP
exit
;;
s)
grep -R "$OPTARG" "$BREADDOCS" -l --exclude-dir .git|while read -r doc; do
echo " 🍞 🥖 🥐 "
printf '\033[34;1;4m%s\033[0m\n' "$(M title "$doc")"
printf '\033[34;1m%s\033[0m\n' "$(M date "$doc")"
C "$doc" | pandoc -t plain
done
echo " 🍞 🥖 🥐 "
return
;;
n)
groupck bakers
doc="$(mktemp)"
cat <<-ENDOFDOC >"$doc"
title
author $USER
ENDOFDOC
$EDITOR "$doc"
fname="$BREADDOCS/$(M title "$doc"|slugify).md"
[[ "$fname" =~ */.md ]] && return
if [ -f "$fname" ]; then
echo "$fname already exists!"
echo "Your article is still at $doc."
exit 4
else
cp "$doc" "$fname"
fi
setperms bakers
return
;;
e)
groupck bakers
matches=( $(grep -R "$OPTARG" "$BREADDOCS" -l --exclude-dir .git) )
if [[ "${#matches[@]}" -eq 1 ]]; then
$EDITOR "${matches[0]}"
return
fi
select doc in "${matches[@]}"; do
[ "$REPLY" = q ] && return
$EDITOR "$doc"
return
done
setperms bakers
;;
\?) return 2 ;;
*) return 2 ;;
esac
done
select doc in "$BREADDOCS"/*; do
[ "$REPLY" = q ] && return
echo " 🍞 🥖 🥐 "
printf '\033[34;1;4m%s\033[0m\n' "$(M title "$doc")"
printf '\033[34;1m%s\033[0m\n' "$(M date "$doc")"
C "$doc" | pandoc -t plain
done
echo " 🍞 🥖 🥐 "
}
bread_publish()
{
groupck admin
git -C /bread/site pull
make -C /bread/site publish
# also publish gemini
/bread/update-gemini-index.sh
}
main()
{ # entry point
while getopts h opt; do
case "$opt" in
h) usage ;;
\?) exit 2 ;;
*) exit 2 ;;
esac
done
shift "$((OPTIND - 1))"
cmd="$1"; shift
[ -z "$cmd" ] && usage
bread_"$cmd" "$@" || exit $?
}
main "$@"