burrow/burrow

601 lines
15 KiB
Bash
Executable File

#!/bin/sh
version="v1.5.0"
configfiles="$HOME/.config/burrow/config $HOME/.config/burrow $HOME/.burrow"
# vars from config
config_dir_gopher="$HOME/gopher/"
config_dir_recipebox="recipebox"
config_dir_phlog="phlog"
config_dir_topics="topics"
config_gopher_server="sdf.org"
config_gopher_port="70"
config_gopher_root="/users/username/"
config_phlog_gophermap=true
config_phlog_usedate=true
config_recipebox_gophermap=false
config_recipebox_usedate=false
config_topics_gophermap=true
config_topics_usedate=false
config_git_commit=false
config_git_push=false
config_autoindent=true
config_file_rss="rss.xml"
config_gopher_name="My Gopher Hole"
config_gopher_desc="Description"
config_rss_num_entries="10"
config_phlog_autorss=false
# coreutils function wrappers
stat_func () {
return 0
}
# vars from flags
flag_debug=0
flag_version=0
flag_help=0
flag_noautoindent=0
# vars from args
arg_options="hvd"
arg_shortlist=0
arg_recipe=0
arg_phlog=0
arg_topic=0
arg_create_config=0
arg_edit_config=0
arg_update_git=0
arg_rss=0
# silence directory movements
push_d () {
cd "$@" || exit
}
pop_d () {
cd - || exit
}
show_help () {
cat > /dev/stdout << END
burrow [options] [commands]
COMMANDS:
phlog Create new phlog entry
recipe Add new recipe to box
topic Add or update a topic
rss Generate an rss feed of recent phlog entries
create-config Create a default configuration file
edit-config Edit your configuration file
update-git Silently pulls gopher git repo if it exists
OPTIONAL FLAGS:
-h Show this help
-v Show current version info
-d Debug mode
END
}
check_coreutils () {
if stat -c"%U" /dev/null >/dev/null 2>/dev/null ; then
stat_func () {
stat -c %Y "$1"
}
else
stat_func () {
stat -Lnqr "$1" | awk '{print $9}'
}
fi
}
parse_input () {
if ! parsed=$(getopt $arg_options "$@"); then
die "Invalid input" 2
fi
eval set -- "$parsed"
while true; do
case "$1" in
-h)
flag_help=1
shift
;;
-v)
flag_version=1
shift
;;
-d)
flag_debug=1
shift
;;
--)
shift
break
;;
*)
die "Internal error: $1" 3
;;
esac
done
for arg in "$@"; do
argc=$(printf "%s" "$arg" | tr '[:upper:]' '[:lower:]')
case $argc in
"shortlist") arg_shortlist=1 ;;
"phlog") arg_phlog=1 ;;
"recipe") arg_recipe=1 ;;
"topic") arg_topic=1 ;;
"create-config") arg_create_config=1 ;;
"edit-config") arg_edit_config=1 ;;
"update-git") arg_update_git=1 ;;
"rss") arg_rss=1 ;;
*) printf "Unknown command: %s\n" "$arg";;
esac
done
}
day_suffix () {
case $(date +%d) in
01|1|21|31) printf "st";;
02|2|22) printf "nd";;
03|3|23) printf "rd";;
*) printf "th";;
esac
}
update_gopher_root () {
newdate=$(date +"%B %d$(day_suffix), %Y")
newString=$(printf "i ==== Last Updated: %s ====\\t\\t%s\\t%s" \
"$newdate" \
"$config_gopher_server" \
"$config_gopher_port")
sed -i'' "s|.*Last\ Updated:.*|${newString}|" "${config_dir_gopher}gophermap"
}
check_directory () {
if [ ! -d "$1" ]; then
die "Missing directory: $1 aborting." 1
fi
}
die () {
msg="$1"
code="$2"
# exit code defaults to 1
if printf "%s" "$code" | grep -q '^[0-9]+$'; then
code=1
fi
# output message to stdout or stderr based on code
if [ ! -z "$msg" ]; then
if [ "$code" -eq 0 ]; then
printf "%s\n" "$msg"
else
printf "%s\n" "$msg" >&2
fi
fi
exit "$code"
}
finish () {
if [ -f "$temp_gophermap" ]; then
rm "$temp_gophermap"
fi
if [ -f "$temp_post" ]; then
rm "$temp_post"
fi
}
trap finish EXIT
make_post_git () {
if $config_git_commit; then
push_d "$config_dir_gopher"
git add "${post_dir}/gophermap" "${post_file}" "$type_gophermap"
if $update_root; then
git add "${config_dir_gopher}/gophermap"
fi
if $config_phlog_autorss; then
git add "${config_dir_gopher}${config_file_rss}"
fi
git commit -m "$post_type: $title"
if $config_git_push; then
git pull
git push
fi
pop_d
fi
}
make_post_process_formatting () {
# If using gophermap, prefix all post lines with "i" except links
if $use_gophermap; then
if [ $config_autoindent -a $flag_noautoindent -eq 0 ]; then
temp_post=$(mktemp -t "$(basename "$0").post.XXXXXXX") || \
die "Failed to create temporary file" 1
awk -v server="${config_gopher_server}" -v port="${config_gopher_port}" '/^[0-9h\+GIThsi].*\t/ {print $0; next} {print "i" $0 "\t\t" server "\t" port}' "$post_file" > "${temp_post}"
cp "${temp_post}" "${post_file}"
rm "${temp_post}"
fi
fi
}
make_post_gophermap () {
# Create temporary gophermap for new post
temp_gophermap=$(mktemp -t "$(basename "$0").gophermap.XXXXXXX") || \
die "Failed to create temporary file" 1
# Add appropriate link into temp gophermap
if $use_gophermap; then
if $use_date; then
# if using gophermap and date
printf "1%s - %s\t%s\t%s\t%s\n" \
"$(date +%Y-%m-%d)" \
"$title" \
"$post_file_path" \
"$config_gopher_server" \
"$config_gopher_port" > "$temp_gophermap"
else
# if using gophermap but not date
printf "1%s\t%s\t%s\t%s\n" \
"$title" \
"$post_file_path" \
"$config_gopher_server" \
"$config_gopher_port" > "$temp_gophermap"
fi
else
if $use_date; then
# if not using gophermap but using date
printf "0%s - %s\t%s\t%s\t%s\n" \
"$(date +%Y-%m-%d)" \
"$title" \
"$post_file_path" \
"$config_gopher_server" \
"$config_gopher_port" > "$temp_gophermap"
else
# if not using gophermap or date
printf "0%s\t%s\t%s\t%s\n" \
"$title" \
"$post_file_path" \
"$config_gopher_server" \
"$config_gopher_port" > "$temp_gophermap"
fi
fi
cat "$type_gophermap" >> "$temp_gophermap"
if ! $use_date; then
sort -fk 1.2 "$temp_gophermap" -o "$temp_gophermap"
fi
cat "$temp_gophermap" > "$type_gophermap"
rm "$temp_gophermap"
# Sort gophermap alphabetically if not using date
if [ "$use_gophermap" -a ! "$use_date" ]; then
sort -fo "$type_gophermap" "$type_gophermap"
fi
}
make_post_unprocess () {
if [ $config_autoindent -a $flag_noautoindent -eq 0 ]; then
temp_post=$(mktemp -t "$(basename "$0").post.XXXXXXX") || \
die "Failed to create temporary file" 1
# If using gophermaps, unformat it for editing
if $use_gophermap; then
awk -F"\t" '/^[0-9h\+GIThs].*\t/ {print $0; next} {sub(/^i/, "", $1);print $1}' "$post_file" > "${temp_post}"
else
# copy existing post to tempfile
cp "$post_file" "${temp_post}"
fi
fi
# Get timestamp of tempfile
temp_post_time=$(stat_func "$temp_post")
# Edit tempfile
$EDITOR "$temp_post"
# Verify that timestamp changed after editing, or abort
temp_post_time_check=$(stat_func "$temp_post")
if [ "$temp_post_time" -ne "$temp_post_time_check" ] ; then
cp "${temp_post}" "$post_file"
rm "${temp_post}"
else
rm "${temp_post}"
return 1
fi
}
make_post_temp () {
# Create a tempfile to do our work
temp_post=$(mktemp -t "$(basename "$0").post.XXXXXXX") || \
die "Failed to create temporary file" 1
# Populate tempfile with either template or default template
if [ -f "${post_dir}/.template" ]; then
cat "${post_dir}/.template" > "$temp_post"
else
{
printf "%s\n%s\n" "----------------------------------------" "$title"
if $use_date; then
date +"%B %d$(day_suffix), %Y"
fi
printf "%s\n\n\n" "----------------------------------------"
} > "$temp_post"
fi
# Check timestamp before editing file
temp_post_time=$(stat_func "$temp_post")
$EDITOR "$temp_post"
# Verify that timestamp changed after editing, or abort
temp_post_time_check=$(stat_func "$temp_post")
# If we saved a change, create the new file
if [ "$temp_post_time" -ne "$temp_post_time_check" ] ; then
mkdir -p "${post_dir}"
cp "${temp_post}" "$post_file"
rm "${temp_post}"
else
rm "${temp_post}"
return 1
fi
}
make_post_paths () {
type_gophermap="${config_dir_gopher}${post_type}/gophermap"
if $use_gophermap; then
title_slug=$(printf "%s" "$title" | \
sed -E -e 's/[^[:alnum:]]/-/g' -e 's/^-+|-+$//g' | tr -s '-' | tr '[:upper:]' '[:lower:]')
if $use_date; then
post_dir="${config_dir_gopher}${post_type}/$(date +%Y%m%d)-$title_slug"
post_path="${config_gopher_root}${post_type}/$(date +%Y%m%d)-$title_slug"
else
post_dir="${config_dir_gopher}${post_type}/$title_slug"
post_path="${config_gopher_root}${post_type}/$title_slug"
fi
post_file="${post_dir}/gophermap"
post_file_path="${post_path}"
else
post_dir="${config_dir_gopher}${post_type}"
post_path="${config_gopher_root}${post_type}"
title_slug=$(printf "%s" "$title" | \
sed -E -e 's/[^[:alnum:]]/-/g' -e 's/^-+|-+$//g' | tr -s '-' | tr '[:upper:]' '[:lower:]')
if $use_date; then
title_slug="$(date +%Y%m%d)-${title_slug}"
fi
post_file="${post_dir}/${title_slug}.txt"
post_file_path="${post_path}/${title_slug}.txt"
fi
}
make_post () {
query="$1"
post_type="$2"
use_gophermap="$3"
use_date="$4"
update_root="$5"
create_rss="$6"
check_directory "$config_dir_gopher"
check_directory "${config_dir_gopher}${post_type}"
printf "%s" "$query"
read -r title
if [ -z "$title" ]; then
die "Cancelled." 0
fi
make_post_paths
if [ -f "$post_file" ]; then
make_post_unprocess
if [ "$?" -eq 1 ]; then
die "Aborted edit" 0
fi
make_post_process_formatting
else
make_post_temp
if [ "$?" -eq 1 ]; then
die "Aborted post" 0
fi
make_post_process_formatting
make_post_gophermap
fi
if $update_root; then
update_gopher_root
fi
if $create_rss; then
make_rss
fi
make_post_git
}
make_rss () {
push_d "${config_dir_gopher}${config_dir_phlog}"
search_list=$(find . \
-mindepth 1 \
-type f \
-print | \
grep -v "^\./gophermap$" |
sort -r | \
head -n "${config_rss_num_entries}")
{
printf "<?xml version=\"1.0\"?><rss version=\"2.0\"><channel>\n"
printf "<title>%s</title>\n" "$config_gopher_name"
printf "<link>gopher://%s%s</link>\n" "$config_gopher_server" "$config_gopher_root"
printf "<description>%s</description>\n" "$config_gopher_desc"
} > "${config_dir_gopher}${config_file_rss}"
for f in $search_list; do
filename="$(printf "%s" "$f" | sed "s|${config_dir_gopher}${config_dir_phlog}/||")"
date="$(printf "%s" "$filename" | sed 's|^\./||' | awk 'BEGIN { FS="-" } { print $1; }')"
title="$(printf "%s" "$filename" | awk 'BEGIN { FS="-" } { $1=""; print $0; }' | sed "s|/gophermap||" | sed 's/^\ //' | sed 's/.*/\L&/; s/[a-z]*/\u&/g' )"
{
printf "<item>\n"
printf " <title>%s</title>\n" "$title"
printf " <link>gopher://%s/0%s%s/%s</link>\n" "$config_gopher_server" "$config_gopher_root" "$config_dir_phlog" "$filename"
printf " <pubdate>%s</pubdate>\n" "$(date -d "$date" +"%A, %d %b %Y %H:%M:%S %z" )"
printf " <description><![CDATA[<pre>\n"
if printf "%s" "$filename" | grep -q "gophermap$"
then
awk -F"\t" '/^[2-9\+GITs].*\t/ {print $0; next} /^h.*\t/ { l=substr($1, 2, length($1)); print l "\n " substr($2, 5, length($2)); next } /^[0-1].*\t/ { l=substr($1, 2, length($1)); t=substr($1,1,1); print l "\n gopher://" $3 "/" t $2; next } {sub(/^i/, "", $1);print $1}' "$f"
else
cat "$f"
fi
printf "</pre>]]></description>\n"
printf "</item>\n"
} >> "${config_dir_gopher}${config_file_rss}"
done
pop_d
printf "</channel>\n" >> "${config_dir_gopher}${config_file_rss}"
printf "</rss>\n" >> "${config_dir_gopher}${config_file_rss}"
}
edit_config () {
if [ -f "$HOME/.config/burrow/config" ]; then
$EDITOR "$HOME/.config/burrow/config"
elif [ -f "$HOME/.config/burrow" ]; then
$EDITOR "$HOME/.config/burrow"
elif [ -f "$HOME/.burrow" ]; then
$EDITOR "$HOME/.burrow"
else
die "No configuration found" 0
fi
}
create_config () {
if [ ! -f "$HOME/.config/burrow/config" ] && \
[ ! -f "$HOME/.config/burrow" ] && \
[ ! -f "$HOME/.burrow" ]; then
config="$HOME/.config/burrow/config"
mkdir -p "$(dirname "$config")"
{
printf "config_dir_gopher=\"%s/gopher/\"\n" "$HOME"
printf "\n"
printf "config_gopher_server=\"sdf.org\"\n"
printf "config_gopher_port=\"70\"\n"
printf "config_gopher_root=\"/users/username/\"\n"
printf "\n"
printf "config_dir_phlog=\"phlog\"\n"
printf "config_phlog_gophermap=true\n"
printf "config_phlog_usedate=true\n"
printf "config_phlog_autorss=false\n"
printf "\n"
printf "config_dir_recipebox=\"recipebox\"\n"
printf "config_recipebox_gophermap=false\n"
printf "config_recipebox_usedate=false\n"
printf "\n"
printf "config_dir_topics=\"topics\"\n"
printf "config_topics_gophermap=true\n"
printf "config_topics_usedate=false\n"
printf "\n"
printf "config_git_commit=false\n"
printf "config_git_push=false\n"
printf "\n"
printf "config_autoindent=true\n"
printf "\n"
printf "config_file_rss=\"rss.xml\"\n"
printf "config_gopher_name=\"My Gopher Hole\"\n"
printf "config_gopher_desc=\"Gopher Hole Description\"\n"
printf "config_rss_num_entries=\"10\"\n"
} >> "$config"
else
printf "Configuration already exists.\n"
fi
}
update_git () {
push_d "$config_dir_gopher"
if git rev-parse --is-inside-work-tree 2> /dev/null; then
git pull -q > /dev/null 2> /dev/null
fi
pop_d
}
main () {
check_coreutils "$@"
parse_input "$@"
if [ $arg_shortlist -gt 0 ]; then
out="phlog topic recipe rss edit-config create-config update-git -v -h -d"
die "${out}" 0
fi
if [ $flag_version -gt 0 ]; then
printf "%s\n" "$version"
fi
if [ $flag_help -gt 0 ]; then
show_help
fi
if [ $flag_debug -gt 0 ]; then
set -x
fi
if [ $arg_create_config -gt 0 ]; then
create_config
fi
if [ $arg_edit_config -gt 0 ]; then
edit_config
fi
for configfile in $configfiles; do
if [ -f "$configfile" ]; then
. "$configfile"
fi
done
if [ $arg_update_git -gt 0 ]; then
update_git
fi
if [ $arg_recipe -gt 0 ]; then
make_post "What is the name of your recipe? " \
"$config_dir_recipebox" \
"$config_recipebox_gophermap" \
"$config_recipebox_usedate" \
true
false
fi
if [ $arg_topic -gt 0 ]; then
make_post "What is the name of your topic? " \
"$config_dir_topics" \
"$config_topics_gophermap" \
"$config_topics_usedate" \
true
false
fi
if [ $arg_phlog -gt 0 ]; then
make_post "Enter a title for your post: " \
"$config_dir_phlog" \
"$config_phlog_gophermap" \
"$config_phlog_usedate" \
true
${config_phlog_autorss}
fi
if [ $arg_rss -gt 0 ]; then
make_rss
fi
}
main "$@"